# Connecting in `app.jadpo`

A connection is how your app reaches a database, a mail server or a
provider's API. You declare each one once, in `connections:` in
`app.jadpo`. Each entry is either a built-in driver (`driver: Driver.x`)
or a package (`package: X.vN`), with the typed settings that driver or
package declares. A connection to a package also names the domains whose
actions may call it, the credential from `config.jadpo`, and which of the
package's hosts to use.

Here is one connection to a provider's API, with everything it needs. Acme
Couriers publishes no package, so the app describes its API itself in
`shared/providers/`. `package:` names that package, by its name and version.
`couriers` is your name for the connection, and `shipping` is one of your
domains.

`app.jadpo`:

```jadpo
application {
    connections: {
        couriers: {
            package: AcmeCouriers.v1
            domains: [shipping]
            credential: config.couriers_api_key
            environment: config.couriers_environment
        }
    }
}
```

`config.jadpo`:

```jadpo
config {
    couriers_api_key: Text {
        secret: true
    }
    couriers_environment: ProviderEnvironment {
        default: ProviderEnvironment.sandbox
    }
}
```

`shared/providers/acme_couriers/v1/package.jadpo`:

```jadpo
/// Acme Couriers' booking API, written from its documentation.
package AcmeCouriers.v1 {
    licence: Licence.proprietary
    jadpo: "1.0"
}
```

`shared/providers/acme_couriers/v1/provider.jadpo`:

```jadpo
/// Acme Couriers' booking API.
provider {
    protocol: Protocol.json_http
    hosts: {
        live: "https://api.acme-couriers.example"
        sandbox: "https://sandbox.acme-couriers.example"
    }
    credential: CredentialScheme.bearer
}
```

`shared/providers/acme_couriers/v1/resources/bookings.jadpo`:

```jadpo
public resource bookings {
    create: {
        method: HttpMethod.post
        path: "/v1/bookings"
        input: CreateBooking
        output: Booking
        changes: true
        idempotent: true
        source: PackageSource.written
    }
}
```

`shared/providers/acme_couriers/v1/types/create_booking.jadpo`:

```jadpo
/// What booking a collection takes.
public type CreateBooking = Object {
    postcode: Text
}
```

`shared/providers/acme_couriers/v1/types/booking.jadpo`:

```jadpo
/// A booked collection.
public type Booking = Object {
    id: Text
}
```

An action in `shipping` calls it. `use app` brings in the connection, and
`use shared/providers/acme_couriers` brings in the package's names.

`domains/shipping/actions/book.jadpo`:

```jadpo
use app { couriers }
use shared/providers/acme_couriers

/// Books a collection with the courier, through its connection.
action book(request: AcmeCouriers.CreateBooking) -> AcmeCouriers.Booking {
    return couriers.bookings.create(request)
}
```

A fuller `connections:` block uses more of the keys, which the list after it
explains. `Stripe` and `ExchangeRates` stand for packages an app has installed:

```jadpo
// app.jadpo
application {
    connections: {
        main: {
            driver: Driver.postgres
            url: config.database_url
        }
        stripe: {
            package: Stripe.v2024_06_20
            domains: [payments]
            credential: config.stripe_secret_key
            environment: config.stripe_environment
            webhooks: {
                secret: config.stripe_webhook_secret
            }
            erasure: {
                key: User.stripe_customer_id
            }
            development: {
                sandbox: [payment_intents.retrieve, customers]
            }
        }
        couriers: {
            package: AcmeCouriers.v1
            domains: [shipping]
            credential: config.couriers_api_key
            environment: config.couriers_environment
            timeout: 5s
        }
        rates: {
            package: ExchangeRates.v1
            domains: [finance]
            credential: config.rates_key
            polls: {
                latest: 6h
            }
        }
    }
}
```

- **`domains:`** lists the domains whose actions may call the connection.
  No other domain can.
- **`environment:`** is a `ProviderEnvironment` setting, `.sandbox` or
  `.live`, which picks between the package's hosts. Calls are HTTPS only,
  and redirects and proxies are always refused.
- **The credential** is a `secret: true` setting
  ([Secrets](/docs/secrets.md)). The package
  says how it is sent, and code can never read it. For a provider that
  acts for each user, the credential is an `OAuthGrant` instead.
- **`host:`** gives the address of a package with no fixed host: another
  Jadpo app, a self-hosted product, or a service already running
  elsewhere.
- **`timeout:` and `retry:`** override the default time limit and retries.
  `max_concurrent:` may lower the package's limit on calls at once.
- **`webhooks:`** gives the webhook secret and, for webhooks that need a
  reply, `replies:` (see "Receiving from outside", further down this page).
- **`polls:`** changes a poll's interval, **`erasure:`** names the field
  holding the person's key at this provider, and **`development:`** lists
  what reaches the sandbox in `jadpo dev`. Each has its own section below.
- **`outbound:`**, which isn't built yet, grants a
  [service run by Jadpo](/docs/running-a-service-in-a-container.md) the hosts its
  package asks for.
- **A package's own `settings:`** are given by name, such as
  `message_stream: "outbound"`.

Two versions of one package are two connections, used side by side while
callers move across. A library package needs no connection: any domain
brings it in with a `use` line.

Two connections: one to the app's own integration with fixed live and
sandbox hosts, and one to an integration with no fixed host, reached at
the address a setting gives and taking a setting of its own. The
`package:` line names the package by its name and version, as the lock
does, with no `use` line.

```jadpo
application {
    connections: {
        couriers: {
            package: AcmeCouriers.v1
            domains: [shipping]
            credential: config.couriers_api_key
            environment: config.couriers_environment
            timeout: 5s
        }
        ledger: {
            package: Ledger.v1
            domains: [billing]
            credential: config.ledger_token
            host: config.ledger_address
            account: "shop"
        }
    }
}
```

```jadpo
config {
    couriers_api_key: Text {
        secret: true
    }
    couriers_environment: ProviderEnvironment
    ledger_address: Url
    ledger_token: Text {
        secret: true
    }
}
```

```jadpo
/// The ledger's API, at the address each deployment gives.
provider {
    protocol: Protocol.json_http
    credential: CredentialScheme.bearer
    settings: {
        account: Text
    }
}
```

A package declares its own settings in `provider.jadpo`'s `settings:`, each
a plain value (`Text`, `Int`, `Decimal`, `Bool` or `Duration`), and every
connection to it gives each one by name, written out or read from a
setting of that type (`account: config.ledger_account`).

A connection's name shares one namespace with the app's domains and
libraries, so `billing.<name>` always means one thing. A connection named
like a domain or a library is an error, reported at the connection,
because that is the name you can change in `app.jadpo` without moving any
code:

```jadpo
application {
    connections: {
        billing: {
            driver: Driver.postgres
            url: config.database_url
        }
    }
}
```

```text
error: Connection `billing` is named like domain `billing`
 --> app.jadpo:3:9
  |
3 |         billing: {
  |         ^^^^^^^ already a domain
  reason: Connections share one namespace with domains and libraries, so `billing.<name>` means one thing; it is already the domain `billing`.
  next: Rename the connection, such as `billing_api`, and the calls through it (agent)
  rule: semantic.duplicate_declaration
  help: https://jadpo.dev/docs/errors/semantic.duplicate_declaration

check failed: 1 error, 4 files
```

## The network report

The network report lists every host your app can reach, so a reviewer
sees a new one before it ships. `jadpo check` writes it to
`build/report/network.md`, with a `.json` twin, once the connections
read cleanly. Each row gives a host, the connection that reaches it, when
(a package's environment setting picks its live or sandbox host), what
for (a package, or a driver's capability) and the domains that use it.

A new host needs a person: `jadpo check` keeps the same hosts, by
connection, in `network.jadpo_record` at the root, and a host a
connection reaches that it didn't on the default branch, a new
connection's included, needs an acceptance,
`couriers: NetworkChange.host_added`
([Acceptances](/docs/acceptances.md)).

The report is built for small diffs, as
[the permissions report](/docs/the-permissions-report.md) is. Rows are
in order of connection and host, each host is shown as written, and there
are no times or totals. So a new connection or a changed host is a
one-line change in review. For the app above:

| Connection | Host | When | For | Domains |
|---|---|---|---|---|
| `couriers` | `https://api.acme-couriers.example` | `config.couriers_environment` is `live` | `AcmeCouriers.v1` | `shipping` |
| `couriers` | `https://sandbox.acme-couriers.example` | `config.couriers_environment` is `sandbox` | `AcmeCouriers.v1` | `shipping` |
| `ledger` | the address in `config.ledger_address` | always | `Ledger.v1` | `billing` |

A database or cache connection is listed at the address in its `url:`
setting, for the domains whose entities it stores. A database's `For`
says `without TLS` when its connection says `tls: false`, and names the
setting its server's certificate is checked against when it names an
`authority:` ([PostgreSQL over TLS](/docs/stores.md#postgresql-over-tls)). A
store of files on the disk driver reaches no host and isn't listed. A
package Jadpo runs (`runs:`) is listed as the service it runs from its
image.

Not built yet: the hosts a service run by Jadpo asks for aren't in the
report until services are built.
