# Serving your first API

> Goal: route a declared operation, with no identity and no quota, to check the chain end to end.

Tutorial - Since version 0.1.0 - Flexway v1.6.3 - https://flexway.io/en/docs/operations-first-api/

> Operations guide: configure, deploy and observe the gateway. What the capability does, product side: [product guide](https://flexway.io/en/docs/product-overview/); calling it: [developer guide](https://flexway.io/en/docs/developer-overview/).

**Goal**: route a declared operation, with no identity and no quota, to check the chain end to end.

**Command**

```bash
cat > flexway.yaml <<'YAML'
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://host.docker.internal:8081   # your API; in a container, localhost is the gateway itself
    protocol: http
    timeout: 3s
YAML

flexway validate                     # offline, no cluster needed
docker run --rm --name flexway -p 8080:8080 -p 9090:9090 \
  --add-host=host.docker.internal:host-gateway \
  -v "$PWD:/etc/flexway:ro" \
  ghcr.io/flexwiz/flexway:<version>
```

An image tag carries the release's `v` (`ghcr.io/flexwiz/flexway:v1.0.0`). The gateway serves
traffic on `8080` and its admin surface on `9090`; the `openapi.yaml` sits next
to `flexway.yaml`, and every operation in it carries an `operationId` - an operation without one is
refused at load, since quotas, keys and audit name operations by it.

**Check**: an operation **declared** in the OpenAPI answers. An undeclared route answers `404` in
`application/problem+json` - not because it is forbidden, because it **does not exist**.

> Any operation without `x-flexway-upstream` uses the `default` upstream. That is what lets you
> start with four lines.

**Path templates.** A segment can mix parameters and text, as OpenAPI allows: `/reports/{year}.pdf`,
`/api/v{n}/status`, `/tiles/{z}-{x}-{y}.png`, `/tasks/{id}:cancel`. Each parameter takes at least one
character and stops at the first text that follows it (`/files/{name}.{ext}` on `archive.tar.gz`:
`name` = `archive`, `ext` = `tar.gz`). Precedence: a literal segment (`/reports/latest.pdf`), then
the mixed segment with the most text, then the bare parameter (`/reports/{id}`). **Refused at load**,
naming the operation: two parameters side by side (`{x}{y}`), a lone brace, and two mixed segments of
the same rank that can read the same text (`a{x}` and `{x}a`).

**Before starting**, the same check runs offline - no cluster, no network:

```bash
flexway validate
# or, without a checkout - the validator ships in every published image (signed, same digest):
docker run --rm -v "$PWD:/etc/flexway:ro" ghcr.io/flexwiz/flexway:<version> validate -v
docker run --rm -v "$PWD:/w:ro" ghcr.io/flexwiz/flexway:<version> validate --config /w/flexway.yaml --contract /w/next.yaml
```

Exit code `0` when the profile is accepted, `1` with the field at fault when it is refused, `2` on
a misused command line. `--contract` validates another OpenAPI document against the same
configuration, in place of `contracts.openapi`.

It replays **exactly** what the service does at start - same loader, same refusals. A
validator that reimplemented the checks would eventually disagree with the thing it
validates. One deliberate exception: a secret reference (`"${VAR}"`, `file:/...`) is checked for
its form, not resolved - a pipeline does not hold the production secrets. An unset variable is
found at start, which refuses to serve and names it.

**Tuning timeouts.** Three budgets, with defaults that suit an ordinary API - write them only to
depart from them:

```yaml
server:
  timeouts:
    connect: 5s     # establishing the upstream connection
    read: 60s       # silence tolerated between two bytes - this, and only this, bounds a stream
    total: 30s      # the whole exchange, retries included
  shutdown:
    drain: 20s      # on SIGTERM, time left to what is in flight
```

The most specific wins: `server.timeouts.total`, then `timeout` on the upstream, then
`x-flexway-timeout` on the operation in the contract. A search at `2s` and an export at `120s` live
side by side without raising everyone's budget.

**Check**: an operation that runs past its budget answers `504` `upstream_timeout` at that budget,
not at the client's. An SSE stream that emits at least once per `read` is **never** cut, however
long it runs.

**Say who answers for an operation, and when it is withdrawn.** Two contract extensions, read for the
console's catalogue - never on the request path:

```yaml
info:
  title: Orders
  version: "1.0.0"
  x-flexway-owner: { team: orders-team, contact: "#orders" }   # every operation inherits it
paths:
  /v1/orders/{id}:
    get:
      operationId: getOrderV1
      deprecated: true
      x-flexway-sunset: "2027-06-30"      # withdrawal date: requires deprecated: true
      x-flexway-owner: { team: legacy-team }   # the operation's own wins
```

`team` is required, `contact` optional; 128 characters each. The contact is a **team's channel** (a
list, a room, a page), never a person: every reader of the console sees it. The date is written
`YYYY-MM-DD`. In gRPC, in the method's option:

```protobuf
rpc ListPayouts(Req) returns (Resp) {
  option deprecated = true;                 // Protobuf's native option, read as deprecated
  option (flexway.v1.operation) = { owner: { team: "payments-team" } sunset: "2027-03-01" };
}
```

**Check**: `flexway validate` refuses a date without deprecation ("is set on an operation that is not
deprecated"), a malformed date, an `owner` without `team` or with an unknown key; the admin `/config`
publishes `owner` and `sunset` in `catalog`. An operation without an owner loads: the catalogue's
compliance reports it. The `Sunset` and `Deprecation` headers towards callers come with a later change.

*Available since `1.2.0`.*
