# Exemples de configuration

> Chaque cas construit est un fichier de examples/, validé en CI comme toute configuration du dépôt, et ce que son commentaire annonce est épinglé par un test…

Exemple - Depuis la version 0.1.0 - Flexway v1.6.3 - https://flexway.io/docs/operations-examples-rest/

> 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/).

Chaque cas **construit** est un fichier de `examples/`, **validé en CI** comme
toute configuration du dépôt, et ce que son commentaire annonce est **épinglé par un test** : un
exemple ne peut pas dériver du code. Le `flexway.yaml` ci-dessous est le fichier lui-même ; le
contrat est montré par extrait. À vérifier chez soi : `flexway validate`.

| Cas | Ce qu'il montre | Depuis |
| --- | --- | --- |
| REST, le minimum (`examples/rest-minimal`) | Un contrat, un amont : chaque opération est servie, rien d'autre | `0.1.0` |
| Identité et CORS (`examples/identity-and-cors`) | Deux fournisseurs OIDC, tenant tiré d'une claim, une application navigateur ; les exigences restent au contrat | `0.3.0` |
| Débit par appelant (`examples/rate-limits`) | Seaux par appelant, classes par méthode, une classe propre à une opération | `0.1.0` |
| Résilience (`examples/resilience`) | Budgets en couches, pool, cloisonnement, coupe-circuit, limite de corps | `0.2.0 / 0.4.0` |
| Flux SSE et WebSocket (`examples/streaming`) | Relais sans tampon ; seul le silence arrête un flux | `0.2.0 / 0.5.0` |
| Transformations (`examples/transformations`) | En-têtes, chemin, paramètres ; condition CEL ; chaîne à quatre niveaux ; retrait `!nom` | `0.1.0` |
| Plusieurs API, une passerelle (`examples/multi-host`) | Un contrat par hôte, rechargé sans redémarrage | `0.1.0` |
| Outils pour agents (MCP) (`examples/mcp-tools`) | Opérations marquées offertes en outils ; jeton émis pour la surface | `0.1.0` |
| Serveur MCP existant, liste déclarée (`examples/mcp-federation`) | Un serveur MCP qui tourne déjà, derrière la passerelle : liste fermée, portées par outil, jeton échangé, découverte avec le jeton de la passerelle | `0.1.0` |
| Serveur MCP existant, tout ce qu'il annonce (`examples/mcp-federation-all`) | `"*"` choisi explicitement, un outil renommé, aucun jeton vers un serveur qui fait confiance au réseau | `0.1.0` |
| Outils projetés et fédérés ensemble (`examples/mcp-mixed`) | Une opération du contrat et un serveur MCP existant sur une même surface, une seule liste | `0.1.0` |
| Jeton échangé vers l'amont (`examples/token-exchange`) | Un amont qui vérifie les jetons reçoit le sien, au nom de l'appelant (RFC 8693) | `0.1.0` |
| Chaîne d'agents bornée (`examples/agent-chain`) | Profondeur et âge bornés, resserrés par une opération sensible, révocation vue par introspection | `0.1.0` |
| Clés d'API (`examples/api-keys`) | Une intégration connue : clé liée à un tenant et à des opérations, empreinte seule au magasin, jamais relayée | `0.1.0` |
| Certificats clients attestés par l'edge (`examples/mtls-edge`) | Certificats déclarés par URI ou sujet, `x-forwarded-client-cert` cru de l'edge seul | `0.1.0` |
| Inférence seule, appelants externes (`examples/ai-surface`) | Surface OpenAI-compatible sans contrat : jeton avec portée, routage par politique, épinglage, budgets | `0.1.0` |
| Inférence derrière un maillage (`examples/ai-mesh`) | En-têtes d'un ingress de confiance, à côté d'un contrat ; débit propre à `/v1/**` | `0.1.0` |
| Coût, plafond souple et repli (`examples/ai-cost`) | Prix par backend, budget en euros, plafond souple qui bascule, repli avant tout octet | `0.1.0` |
| mTLS vers un amont | Identité de la passerelle hors maillage | `0.3.0` |
| Écouteur TLS de la passerelle | Certificats clients vérifiés sans edge | `0.1.0` |
| Remplacer une passerelle en place | Clé de débit d'un BFF de confiance, `type` des erreurs, corps laissé au service, deux audiences | `1.0.0` |
| Débit partagé, forfaits, délestage | Compteurs partagés entre répliques, quota par tenant, priorités de délestage | `1.0.0` |
| Amont gRPC en gRPC, gRPC-Web et REST (`examples/grpc-upstream`) | Un `.proto` annoté, servi aux trois clients ; une opération par méthode | `1.1.0` |
| Cache de réponses (`examples/response-cache`) | Cache public au contrat, politique sur des routes, cache par sujet, borne mémoire | `1.2.0` |
| Corps et statut de réponse (`examples/body-transforms`) | Projection fermée, masque, statut sémantique, borne et échantillon | `1.2.0` |
| Appel externe (`examples/external-call`) | Une décision du SI client avant l'amont, un enrichissement après ; panne déclarée, budget consommé | `1.2.0` |
| [Pas encore construit](https://flexway.io/docs/operations-examples-gateway/#pas-encore-construit) | Appareils MQTT, Sparkplug B, OPC UA ; plans, budgets par utilisateur et par cas d'usage, webhooks entrants signés, listes d'adresses, compression, coalescence ; cartes A2A signées, délégation et registre ; licence signée | Sa version |
| Agent A2A derrière la passerelle (`examples/a2a-agent`) | Une carte déclarée comme contrat, deux liaisons, jeton échangé obligatoire, lecture gratuite, flux bornés, cibles push en liste blanche | `1.2.0` |
| [Configurations cibles des prochaines versions](https://flexway.io/docs/operations-planned-configurations/) | **Pas construit** : un exemple complet par cas d'usage - appareils MQTT et industrie, plans, budgets, webhooks entrants, listes d'adresses, compression, coalescence | `1.7.0`, `1.8.0` |
| Kafka : publier, diffuser, livrer (`examples/async-kafka`) | Porte de publication et outil MCP, SSE en éventail et WebSocket accusé, webhook signé et rebut, canal lié au tenant, SASL SCRAM sur TLS | `1.5.0` |
| RabbitMQ en AMQP 0-9-1 : canal par personne, flux public (`examples/async-amqp`) | Canal lié au sujet vérifié, flux public anonyme et sa reprise, CloudEvents structuré, utilisateur et hôte virtuel | `1.5.0` |
| MQTT 5 : claim, attribut tenant, webhook (`examples/async-mqtt`) | Canal lié à une claim, sujet commun filtré par l'attribut `tenant`, webhook par abonnement partagé | `1.5.0` |
| Charges Avro sous schéma, registre de schémas (`examples/async-binary`) | Corps Avro validé à la publication (au choix de l'opération), JSON vers SSE et webhooks, trames WebSocket binaires, format filaire de Confluent lu dans un registre, sujet du tenant exigé, enregistrements d'un producteur Kafka ordinaire | `1.6.0` |
| [NATS et AMQP 1.0](https://flexway.io/docs/operations-examples-brokers/#nats-jetstream--publier-depuis-http-ou-un-outil-mcp) (`examples/async-nats-*`, `examples/async-amqp1-*`) | Flux JetStream alimenté depuis HTTP ou un outil MCP, SSE et WebSocket liés au tenant, webhook depuis un consommateur durable ; AMQP 1.0 vers Azure Service Bus et RabbitMQ 4 | `1.6.0` |
| Clients Kafka natifs : clusters virtuels par tenant (`examples/kafka-proxy`) | Producteurs, consommateurs et outils Kafka de toute bibliothèque derrière la passerelle : cluster virtuel au contrat, sujets, groupes et transactions écrits dans l'espace du tenant, OAUTHBEARER, clé d'API en PLAIN, certificat client, SNI sur TLS ou un port par courtier dans un maillage | `1.6.0` |

## REST, le minimum

<!-- example: examples/rest-minimal/flexway.yaml -->

```yaml
# The smallest useful configuration: one contract, one upstream. Every operation of the contract is
# served and relayed there; nothing else is.
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://orders.internal:8080
```

## Identité et CORS

<!-- example: examples/identity-and-cors/flexway.yaml -->

```yaml
# Two identity providers at once, the tenant from a signed claim, a browser application on its own
# origin. What each operation requires is in the contract, never here.
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://orders.internal:8080
auth:
  tenant_header: X-Tenant                       # written toward the upstream; a caller's value is overwritten
  issuers:
    - name: staff
      discovery: https://id.example.com/realms/staff/.well-known/openid-configuration
      audience: api
      roles_claim: realm_access.roles
    - name: customers
      discovery: https://login.example.net/.well-known/openid-configuration
      audience: api
      tenant_claim: org
policies:
  definitions:
    browser:
      type: cors
      config:
        allow_origins: ["https://app.example.com"]
        allow_credentials: true
        max_age: 10m
  chains:
    global: [browser]
```

```yaml
# extrait de examples/identity-and-cors/openapi.yaml
paths:
  /catalogue:
    get:
      operationId: browseCatalogue
      security: []                              # anonymous, said explicitly
  /me/orders:
    get:
      operationId: myOrders
      security: [ { customers: [orders:read] } ] # a scope, read from the token
  /admin/report:
    get:
      operationId: adminReport
      security: [ { staff: [admin] } ]           # a role (http bearer schemes list roles)
```

## Débit par appelant

<!-- example: examples/rate-limits/flexway.yaml -->

```yaml
# A token bucket per caller - the token's subject, else the client's address, read from
# X-Forwarded-For only behind a declared edge.
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://search.internal:8080
server:
  trusted_proxies: [10.0.0.0/8]                  # the edge; nobody else chooses their address
policies:
  definitions:
    per-caller:
      type: rate-limit
      config:
        key: caller
        classes:
          read:      { rps: 50, burst: 100 }
          write:     { rps: 10, burst: 20 }
          expensive: { rps: 1,  burst: 5 }
  chains:
    global: [per-caller]
```

```yaml
# extrait de examples/rate-limits/openapi.yaml
paths:
  /search:
    get:
      operationId: search
      x-flexway-rate-class: expensive            # its own class, whatever its method
  /items:
    get:
      operationId: listItems                     # GET: class "read"
    post:
      operationId: createItem                    # POST: class "write"
```

## Résilience

<!-- example: examples/resilience/flexway.yaml -->

```yaml
# Layered budgets, connection pools, a bulkhead and a circuit breaker per upstream. Retries are
# built in: once, for an idempotent method or an Idempotency-Key, within a retry budget.
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://billing.internal:8080
    timeout: 3s                                  # total budget of an exchange with this upstream
    pool: { max_idle: 100, max_conns: 200, idle_timeout: 90s }
    max_in_flight: 500                           # bulkhead: past it, 503 at once
    circuit_breaker: { failure_ratio: 0.5, min_requests: 20, window: 10s, cooldown: 5s }
  exports:
    url: http://exports.internal:8080
    max_in_flight: 20                            # a slow upstream uses up its own slots, never everyone's
server:
  timeouts:
    connect: 2s
    read: 60s                                    # silence between two bytes - what governs a stream
    total: 30s
  limits:
    max_body: 2MiB                               # a body read for validation; past it, 413
  shutdown:
    drain: 20s
```

```yaml
# extrait de examples/resilience/openapi.yaml
paths:
  /invoices:
    get:
      operationId: listInvoices
  /invoices/export:
    post:
      operationId: exportInvoices
      x-flexway-timeout: 20s                     # this operation's own budget - the most precise wins
      x-flexway-upstream: exports                # another upstream than the default
```

## Flux SSE et WebSocket

<!-- example: examples/streaming/flexway.yaml -->

```yaml
# SSE and WebSocket relayed as they come: no buffer, no deadline on a stream - only silence ends
# it (server.timeouts.read). Send a heartbeat more often than that.
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://live.internal:8080
server:
  timeouts:
    read: 90s
```

```yaml
# extrait de examples/streaming/openapi.yaml
paths:
  /events:
    get:
      operationId: streamEvents
          content: { text/event-stream: { schema: { type: string } } }
  /ws:
    get:
      operationId: liveSocket                    # a WebSocket upgrade, relayed like any request
      x-flexway-timeout: 2s                      # the total budget stops at 101: an open socket is not cut
```

## Transformations

<!-- example: examples/transformations/flexway.yaml -->

```yaml
# Enriching a flow without touching the upstream: named policies, a CEL condition, composed
# global -> host -> routes -> operation, the most precise wins, `!name` removes.
version: 1
contracts:
  openapi: ./openapi.yaml
upstreams:
  default:
    url: http://orders.internal:8080
auth:
  issuers:
    - name: customers
      discovery: https://login.example.net/.well-known/openid-configuration
      audience: api
policies:
  definitions:
    plan-header:
      type: header-inject
      when: "has(auth.subject)"                   # CEL: authenticated callers only
      config:
        set: { x-plan: "{{ auth.claims.plan }}", x-operation: "{{ route.operation_id }}" }
        on_missing: skip                          # skip | empty | fail - required for an expression
    strip-server:
      type: header-remove
      phase: response                             # on what the upstream answers
      config: { names: [server, x-powered-by] }
    v2-path:
      type: rewrite-path                          # the upstream's path, never the served one
      config: { to: "/v2/orders/{id}" }
    channel:
      type: query-inject
      config: { set: { channel: gateway } }
  chains:
    global: [strip-server]
    routes:
      "/orders/*": [v2-path, channel]             # * one segment, ** any number
```

```yaml
# extrait de examples/transformations/openapi.yaml
paths:
  /orders/{id}:
    get:
      operationId: getOrder
      x-flexway-policies: [plan-header]          # this operation alone gets the caller's plan
  /orders/{id}/legacy:
    get:
      operationId: getLegacyOrder
      x-flexway-policies: ["!strip-server"]      # removes a policy a broader level brought
```

## Plusieurs API, une passerelle

<!-- example: examples/multi-host/flexway.yaml -->

```yaml
# Two APIs on one gateway, each on its own host, neither visible from the other - and reloaded
# without a restart when a file changes.
version: 1
contracts:
  openapi: ./public.yaml                         # every host without contracts of its own
upstreams:
  catalogue: { url: "http://catalogue.internal:8080" }
  backoffice: { url: "http://backoffice.internal:8080" }
hosts:
  api.example.com:
    default_upstream: catalogue
  admin.example.com:
    default_upstream: backoffice
    contracts: [./backoffice.yaml]               # exactly this, nothing of public.yaml
server:
  reload:
    interval: 5s                                 # "0s": SIGHUP only
```
