# Servir sa première API

> Objectif : router une opération déclarée, sans identité ni quota, pour vérifier la chaîne.

Tutoriel - Depuis la version 0.1.0 - Flexway v1.6.3 - https://flexway.io/docs/operations-first-api/

> Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la capacité côté produit : [guide produit](https://flexway.io/docs/product-overview/) ; l'appeler : [guide développeur](https://flexway.io/docs/developer-overview/).

**Objectif** : router une opération déclarée, sans identité ni quota, pour vérifier la chaîne.

**Commande**

```bash
cat > flexway.yaml <<'EOF'
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://host.docker.internal:8081   # votre API ; dans un conteneur, localhost est la passerelle elle-même
    protocol: http
    timeout: 3s
EOF

flexway validate                     # hors ligne, sans cluster
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>
```

Un tag d'image porte le `v` de la release (`ghcr.io/flexwiz/flexway:v1.0.0`). La passerelle sert
le trafic sur `8080` et son administration sur `9090` ; l'`openapi.yaml` est à
côté de `flexway.yaml`, et chacune de ses opérations porte un `operationId` - une opération sans
lui est refusée au chargement, car quotas, clés et audit nomment les opérations par lui.

**Vérif** : une opération **déclarée** dans l'OpenAPI répond. Une route non déclarée répond `404`
en `application/problem+json` - pas parce qu'elle est interdite, parce qu'elle **n'existe pas**.

> Toute opération sans `x-flexway-upstream` utilise l'amont `default`. C'est ce qui permet de
> démarrer avec quatre lignes.

**Gabarits de chemin.** Un segment peut mêler paramètres et texte, comme OpenAPI le permet :
`/reports/{year}.pdf`, `/api/v{n}/status`, `/tiles/{z}-{x}-{y}.png`, `/tasks/{id}:cancel`. Chaque
paramètre prend au moins un caractère et s'arrête au premier texte qui le suit (`/files/{name}.{ext}`
sur `archive.tar.gz` : `name` = `archive`, `ext` = `tar.gz`). Précédence : un segment littéral
(`/reports/latest.pdf`), puis le segment mixte qui a le plus de texte, puis le paramètre seul
(`/reports/{id}`). Sont **refusés au chargement**, opération nommée : deux paramètres collés
(`{x}{y}`), une accolade isolée, et deux segments mixtes de même rang qui peuvent lire le même texte
(`a{x}` et `{x}a`).

**Avant de démarrer**, la même vérification se joue hors ligne, sans grappe ni réseau :

```bash
flexway validate
# ou, sans dépôt cloné - le validateur est dans chaque image publiée (signée, même 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
```

Code de sortie `0` quand le profil est accepté, `1` avec le champ fautif quand il est refusé, `2`
sur une ligne de commande mal formée. `--contract` valide un autre document OpenAPI contre la même
configuration, à la place de `contracts.openapi`.

Elle rejoue **exactement** ce que fait le service au démarrage - même chargeur, mêmes
refus. Un validateur qui réimplémenterait les contrôles finirait par diverger de ce
qu'il valide. Une exception, voulue : une référence de secret (`"${VAR}"`, `file:/...`) est
vérifiée dans sa forme, pas résolue - une chaîne d'intégration ne détient pas les secrets de
production. Une variable absente se découvre au démarrage, qui refuse de servir et la nomme.

**Régler les délais.** Trois budgets, avec des défauts qui conviennent à une API ordinaire - ne les
écrire que pour s'en écarter :

```yaml
server:
  timeouts:
    connect: 5s     # établir la connexion amont
    read: 60s       # silence toléré entre deux octets - c'est lui, et lui seul, qui borne un flux
    total: 30s      # l'échange entier, reprises comprises
  shutdown:
    drain: 20s      # au SIGTERM, temps laissé à ce qui est en cours
```

Le plus précis gagne : `server.timeouts.total`, puis `timeout` sur l'amont, puis
`x-flexway-timeout` sur l'opération dans le contrat. Une recherche à `2s` et un export à `120s`
cohabitent ainsi sans relever le budget de tout le monde.

**Vérif** : une opération qui dépasse son budget répond `504` `upstream_timeout` à l'échéance,
pas à celle du client. Un flux SSE qui émet au moins une fois par `read` n'est **jamais** coupé,
quelle que soit sa durée.

**Dire qui répond d'une opération, et quand elle sera retirée.** Deux extensions du contrat, lues pour
le catalogue de la console - jamais sur le chemin de la requête :

```yaml
info:
  title: Orders
  version: "1.0.0"
  x-flexway-owner: { team: orders-team, contact: "#orders" }   # chaque opération en hérite
paths:
  /v1/orders/{id}:
    get:
      operationId: getOrderV1
      deprecated: true
      x-flexway-sunset: "2027-06-30"      # date de retrait : exige deprecated: true
      x-flexway-owner: { team: legacy-team }   # celle de l'opération gagne
```

`team` est obligatoire, `contact` facultatif ; 128 caractères chacun. Le contact est un **canal
d'équipe** (une liste, un salon, une page), jamais une personne : tout lecteur de la console le voit.
La date s'écrit `AAAA-MM-JJ`. En gRPC, dans l'option de la méthode :

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

**Vérif** : `flexway validate` refuse une date sans dépréciation (`is set on an operation that is not deprecated`),
une date mal écrite, un `owner` sans `team` ou avec une clé inconnue ; le `/config`
d'administration publie `owner` et `sunset` dans `catalog`. Une opération sans propriétaire se charge :
c'est la conformité du catalogue qui la signale. Les en-têtes `Sunset` et `Deprecation` vers les
appelants viendront avec un change suivant.

*Disponible depuis `1.2.0`.*
