# Calling

Your app reaches an outside service, such as a courier's API, through a
**connection**: a named entry under `connections:` in `app.jadpo` that
says which package to use, which of your domains may use it and which
credential it sends
([Connecting in `app.jadpo`](connecting-in-app-jadpo.md)). A domain is one
business area of your app, in its own folder under `domains/`. The
example below declares a connection named `couriers`.

There are two kinds of package
([Kinds of package](kinds-of-package.md)):

- A **provider package** describes one outside API at one version: its
  operations, grouped into resources such as `bookings`, and their types
  and failures. You call it through a connection.
- A **library package** is plain shared code with no service behind it,
  such as a postcode parser. You call it through the package's name.

## Calling a provider

You call a provider's operation as `connection.resource.operation(input)`.
`couriers.bookings.create(request)` calls the `create` operation of the
`bookings` resource, through the connection `couriers`. A file brings the
connection in with `use app { couriers }`. A provider package's public
action is called the same way, as `connection.action(input)`.

Only actions may call either. An action is code that may change things,
such as writing records or calling outside, unlike a function, which only
computes a value ([Functions and actions](functions-and-actions.md)).

A call can fail, so you write it with `attempt` or a `match`. `attempt`
passes a failure on to whoever called your action
([Passing a failure on: `attempt`](passing-a-failure-on-attempt.md)).
After the call you list how each of the package's failures becomes one of
your app's own, written `condition: Failure`, as in
[Turning one failure into another](turning-one-failure-into-another.md).

This sketch books a collection for a `Shipment`, a stored record of your
app that isn't shown, and saves the booking's id on it. The complete
example under [A call, step by step](calling.md#a-call-step-by-step) compiles.

```jadpo
// domains/shipping/actions/book_collection.jadpo
action book_collection(shipment: Shipment) fails CollectionSlotGone -> Shipment {
    var request: AcmeCouriers.CreateBooking = AcmeCouriers.CreateBooking {
        postcode: shipment.postcode
        slot_id: shipment.slot
    }
    var booking: AcmeCouriers.Booking = attempt couriers.bookings.create(request) {
        slot_taken: CollectionSlotGone
    }
    return attempt Shipment.update(shipment) {
        set: {
            booking_id: booking.id
        }
    }
}
```

`slot_taken` is the code of one of the failures the package declares for
the operation, under its `failures:`.

## Calling a library package

A library package's functions are called through the package's name, with
no connection, from anywhere a library may be called:

```jadpo
// domains/accounts/actions/save_address.jadpo
use packages/postcodes

var postcode: Postcodes.UkPostcode = attempt Postcodes.uk.parse(input.postcode) {
    invalid_postcode: AddressInvalid
}
```

## Types

The types come from the package, and your app writes none. The checker
compares what you send with what the operation takes. At runtime a
response is checked only for the fields your code reads, so a provider
adding or changing other fields breaks nothing. A package input may take
any of your app's object types, sent as JSON, such as Postmark's template
variables or Stripe's metadata.

## Failures

Map a package's failure at the call to give it your app's meaning. A
package failure you don't map becomes a fault: a problem outside your
code that the runtime raises, which never appears in your `fails` list
([Faults](faults.md)). To change a package failure's code, not
just its message, map it to your app's own failure at the call.

The runtime also raises failures of its own on any outside call, whatever
the package declares:

- `misconfigured` when the provider rejects the credential;
- `unavailable` when the provider surely did nothing;
- `outcome_unknown` when the provider may have acted.

A call that gets no answer in time is `timed_out`. You may map
`timed_out` to one of your own `unavailable` failures only on an
operation that is safe to send again
([Retrying a call, and timing out](calling.md#retrying-a-call-and-timing-out)).

## Idempotency keys

An idempotency key lets a provider recognise a request sent twice and act
on it once. The package says where the key goes, and the runtime supplies
it. You don't choose the key yourself.

Each call's key is built from what identifies this call. The action's key
is the client's `Idempotency-Key` header or the request's id in a route
([Idempotent writes](idempotent-writes.md)), and the delivery key in
subscribers and schedules, which names the delivery being handled
([Delivery: at least once, safe to repeat](delivery-at-least-once-safe-to-repeat.md)).

The runtime never sends that key as it is. It hashes it with the
application, the environment, the connection, the caller's scope (who is
calling, so one person's key never matches another's), the call's step
number and a key generation. The result is `jd1_` followed by 32 base32
characters of a SHA-256, 36 characters in all. Where the provider takes
fewer, it is cut to the package's `max_length:`.

So when two callers send the same `Idempotency-Key`, a provider that keys
requests by account never takes one for a repeat of the other.

## Time limits and retries

By default a call may take 3 seconds (`timeout:`) and is tried up to 3
times within 30 seconds (`retry: { attempts: within: }`). The wait between
tries doubles each time, with some randomness (exponential backoff with
full jitter): at most 200 milliseconds before the second try and 400
before the third.

A route has 10 seconds by default, its deadline, which the route's
`deadline:` changes ([Time limits](time-limits.md)). Three
3-second tries and their waits take less than 10 seconds, so a route
making one call needs no `deadline:` of its own. Retrying never runs past
the route's deadline: a wait that would end after it stops the retrying,
and the last answer stands. In background work, retrying stops at
`within:` or when the work's own time budget runs out, whichever comes
first.

Only safe repeats are retried: the request never left, the provider said
it did nothing (honouring `Retry-After`), or the call is idempotent.
[Retrying a call, and timing out](calling.md#retrying-a-call-and-timing-out) has
the details.

The package's rate limits space calls out before the provider refuses
them. `max_concurrent:` caps how many calls run at once per app
instance. Further calls wait, and a subscriber delivery that is waiting
is retried later
([How polls and `max_concurrent:` run](how-polls-and-max-concurrent-run.md)).
Heavy work such as PDF rendering runs in subscribers, never in the
route.

## Pages of results

A list operation returns a `Page<T>`
([Pages](order-and-pages.md#pages)). The package says how the
provider pages, and `page.next` asks for the next page. Work over many
pages uses `batches.each(from:, each:, finished:, key:)`, which pages
through in the background and resumes where it stopped
([Batches: work over many items](batches-work-over-many-items.md)).

## Calls and writes

In an action, calls that change something come before any write. The
writes commit after the last call returns. If the commit fails, the
action runs again, and so do its calls, under the same keys. No
transaction is open while a call is out. Reads before the call use a
snapshot closed before the call, and a write to a record loaded before it
checks the row is unchanged
([Outside calls hold no transaction](all-or-nothing-actions-and-concurrent-changes.md#outside-calls-hold-no-transaction)).

A changing call with no idempotency key must be the only changing call
in its action.
Several keyed calls follow
[Several changing calls in one action](several-changing-provider-calls.md).
Work that must follow a write emits an event
([Emitting an event](emitting-an-event.md)).

## A call, step by step

Here an action calls an operation through the connection `couriers`,
declared in `app.jadpo`. The action is in `shipping`, a domain the
connection lists. It builds the package's own input type through the
package's name. It maps the package's `slot_taken` failure to the app's
`CollectionSlotGone`, a failure of the same kind (`conflict`). The
runtime's driver for the package's protocol, native code that speaks
JSON over HTTP, sends the call; how it is sent and read is under
[Decoding](decoding.md).

```jadpo
use app { couriers }
use shipping/failures { CollectionSlotGone }
use shared/providers/acme_couriers

/// Books a collection, mapping a slot the courier has given away to the
/// shop's own failure.
action book_collection(
    postcode: AcmeCouriers.Postcode,
    slot: AcmeCouriers.SlotId
) fails CollectionSlotGone -> AcmeCouriers.Booking {
    var request: AcmeCouriers.CreateBooking = AcmeCouriers.CreateBooking {
        postcode: postcode
        slot_id: slot
    }
    return attempt couriers.bookings.create(request) {
        slot_taken: CollectionSlotGone
    }
}
```

- **Mapped at the call.** Each arm of the call's `attempt` is keyed by the
  code of one of the operation's `failures:`, and names the app's failure
  of the same kind. The action fails with the app's failure, and the
  provider's failure is kept as its internal cause, for the logs. A
  failure no arm maps is a fault when it comes, and is never passed on, so
  a package failure is never in the app's `fails` list. A `match` on the
  call handles the package's failures by name, as for a library function.
- **The runtime's own failures** come as they would for any outside call.
  `misconfigured` means the provider rejected the credential, or the
  address is one the runtime won't send to.
- **`unavailable`** means the call can safely be made again. The provider
  surely did nothing: it answered `408` or `429`, or said the key is
  still in use, or the call never left. A call with an idempotency key
  that may have acted is `unavailable` too, since sending it again under
  the same key is safe.
- **`outcome_unknown`** means a call with no key may have acted: the
  provider answered with a server error, or didn't answer in time.
- **Sent once no transaction is open.** The call goes out through the
  capability seam, the one place in the runtime that makes outside calls,
  where each call is logged and keyed. What the action has only read
  before the call is a snapshot, closed before the call goes out. Its
  writes run in a transaction opened after the call, which holds each
  record loaded before the call to the row version it was loaded at
  ([Outside calls hold no transaction](all-or-nothing-actions-and-concurrent-changes.md#outside-calls-hold-no-transaction)).
- **A call after a write.** A call made after the action's first write is
  never sent: it is the fault `call_in_transaction`. `jadpo check`
  refuses a write placed before a later call (`data.write_before_call`),
  so checked code never meets the fault. If a record changes while the
  call is out, the action runs again, and the call is repeated under its
  key. An item of a batch whose `each:` action calls a provider is a
  batch of its own ([How a run is kept](how-a-run-is-kept.md)).

## Retrying a call, and timing out

Here the connection sets `timeout: 5s`, so each try may take five
seconds, and `retry:`, which tries the courier up to four times within 20
seconds. Looking up a parcel changes nothing, so it is safe to send
again, and the action maps `timed_out:` at the call. A lookup the courier
never answers in time, on any try, is the shop's own
`CourierUnavailable`.

```jadpo
application {
    connections: {
        couriers: {
            package: AcmeCouriers.v1
            domains: [shipping]
            credential: config.couriers_api_key
            environment: config.couriers_environment
            timeout: 5s
            retry: {
                attempts: 4
                within: 20s
            }
        }
    }
}
```

```jadpo
use app { couriers }
use shipping/failures { CourierUnavailable }
use shared/providers/acme_couriers

/// Where a parcel is, or the shop's own failure when the courier doesn't
/// answer in time.
action parcel_status(
    parcel: AcmeCouriers.ParcelRef
) fails CourierUnavailable -> AcmeCouriers.Status {
    var tracked_parcel: AcmeCouriers.Parcel = attempt couriers.parcels.get(
        AcmeCouriers.ParcelLookup {
            id: parcel
        }
    ) {
        timed_out: CourierUnavailable
    }
    return tracked_parcel.status
}
```

- **What is sent again.** A call that never left, or that the provider
  said did nothing (`408`, `429`, its key still in use), whatever it
  changes; and one that may have acted (a server's error, no answer in
  time) only when it changes nothing or takes its package's key, sent
  again unchanged under the same key. A change with no key that may have
  acted is never sent again: it is `outcome_unknown`.
- **How long it waits between.** 200 milliseconds before the second
  attempt, doubled before each after it, each wait drawn evenly from zero
  to that (full jitter); a provider's `Retry-After` in seconds instead,
  where it gives one. A wait that would end after `within:`, or after
  what is left of the route's deadline or of background work's budget,
  ends the retrying, and the last answer stands. Each attempt is logged
  as a call of its own.
- **`timed_out:`** turns the runtime's `timed_out` into one of the app's
  own failures of kind `unavailable`, once every attempt has gone
  unanswered in time. The runtime's `timed_out` is kept as its cause. You
  can map it only on an operation that changes nothing or is
  `idempotent: true`. Mapping it on an operation that changes something
  with no key is the error `failure.unknown_mapping`.
- **A timeout you don't map** is `unavailable` where the call was safe to
  send again, and `outcome_unknown` where it wasn't.
