flexway
FRENParler à un expert

Servir sa première API

TutorielExploitationDepuis la version 0.1.0

Markdown

Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la capacité côté produit : guide produit ; l'appeler : guide développeur.

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

Commande

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 :

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 :

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 :

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 :

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.

Étapes suivantes