REST API design - 18 - OpenAPI : le contrat entre ton API et le monde

OpenAPI 3.x, YAML, code-first vs spec-first, openapi-typescript, $ref et examples pour une spec qui sert vraiment.

REST API design 19 / 25
  1. 01 REST API design - 00 - Pourquoi le design de ton API change tout
  2. 02 REST API design - 01 - Les principes REST que personne ne lit
  3. 03 REST API design - 02 - Des URLs qui ont du sens
  4. 04 REST API design - 03 - Les méthodes HTTP, pour de vrai
  5. 05 REST API design - 04 - Les codes de statut HTTP qu'il faut connaître
  6. 06 REST API design - 05 - Body, headers et le diable dans les détails
  7. 07 REST API design - 06 - La pagination, ou comment ne pas tuer ta base
  8. 08 REST API design - 07 - Filtrage et tri sans prise de tête
  9. 09 REST API design - 08 - La validation avec Zod, gardien de ton API
  10. 10 REST API design - 09 - Erreurs : un format que tes clients vont adorer
  11. 11 REST API design - 10 - Authentification : JWT, API keys et OAuth2
  12. 12 REST API design - 11 - Versioning : quand et comment faire évoluer ton API
  13. 13 REST API design - 12 - CORS : comprendre et debugger les erreurs cross-origin
  14. 14 REST API design - 13 - Rate limiting : protéger ton API sans frustrer tes clients
  15. 15 REST API design - 14 - Caching : les bonnes réponses sont celles qu'on n'envoie pas
  16. 16 REST API design - 15 - Upload de fichiers : multipart, signed URLs et streaming
  17. 17 REST API design - 16 - Relations entre ressources : embarquer, lier ou les deux
  18. 18 REST API design - 17 - HATEOAS : des liens dans tes réponses
  19. 19 REST API design - 18 - OpenAPI : le contrat entre ton API et le monde
  20. 20 REST API design - 19 - Documentation : une API non documentee est une API inutile
  21. 21 REST API design - 20 - Testing : tester ton API sans devenir fou
  22. 22 REST API design - 21 - Webhooks : quand c'est ton API qui appelle
  23. 23 REST API design - 22 - Performance : quand chaque milliseconde compte
  24. 24 REST API design - 23 - Sécurité : les attaques que tu vas subir (et comment t'en protéger)
  25. 25 REST API design - 24 - Glossaire : tous les termes REST API en un seul endroit

18 - OpenAPI : le contrat entre ton API et le monde

Ce que tu vas apprendre

  • Ce qu'est OpenAPI 3.x et la structure d'un fichier de spec
  • La différence entre code-first et spec-first
  • Comment utiliser $ref pour organiser une grosse spec
  • La génération de types TypeScript avec openapi-typescript
  • Les bonnes pratiques pour les examples et les descriptions

Prerequisites

Avoir lu l'article sur HATEOAS. Savoir lire du YAML et avoir une API REST a documenter.


Pendant des annees, j'ai documente mes APIs dans un fichier Notion. Trois colonnes : endpoint, méthode, description. A chaque modification de l'API, je mettais à jour le doc. Ou plutot, j'oubliais. En six mois, le doc et l'API n'avaient plus rien a voir. Le jour ou j'ai découvert OpenAPI, j'ai compris que la documentation d'une API ne devait pas etre un document séparé mais un contrat lie au code.

OpenAPI 3.x en bref

OpenAPI (anciennement Swagger) est une spécification qui decrit une API REST dans un format machine-readable (YAML ou JSON). Un fichier OpenAPI contient :

  • Les endpoints disponibles (paths)
  • Les méthodes HTTP supportees
  • Les paramètres, headers, request bodies
  • Les schemas de réponse
  • L'authentification requise
yamlopenapi: 3.1.0
info:
  title: Mon API
  version: 1.0.0
  description: API de gestion de produits

paths:
  /products:
    get:
      summary: Liste des produits
      operationId: listProducts
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        "200":
          description: Liste paginee des produits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductList"

Code-first vs spec-first

Spec-first : tu ecris la spec OpenAPI avant le code. L'équipe valide le contrat, puis les devs implementent. Le front peut commencer a coder avec un mock server généré depuis la spec.

Code-first : tu ecris le code et la spec est générée automatiquement depuis les decorateurs ou les annotations. Pratique, mais la spec devient un sous-produit du code au lieu d'etre un contrat explicite.

Mon avis : spec-first pour les API publiques et les équipes front/back separees. Code-first pour les projets solo ou les API internes ou la vitesse prime. Sur paltemps.fr, je fais du code-first avec Elysia qui généré la spec automatiquement.

En code-first avec Elysia :

typescriptimport { Elysia, t } from "elysia";
import { swagger } from "@elysiajs/swagger";

new Elysia()
  .use(swagger())
  .get("/products", () => db.select().from(products), {
    query: t.Object({
      page: t.Optional(t.Number({ default: 1 })),
      limit: t.Optional(t.Number({ default: 20 }))
    }),
    response: t.Object({
      data: t.Array(t.Object({
        id: t.Number(),
        name: t.String(),
        price: t.Number()
      }))
    })
  })
  .listen(3000);

Elysia généré la spec OpenAPI a /swagger/json. Les types du schema sont verifies au runtime et utilises pour la documentation. Un seul endroit, zero desynchronisation.

Organiser avec $ref

Une spec monolithique de 3000 lignes est inmaintenable. $ref permet de découper en fichiers :

yaml# openapi.yaml
paths:
  /products:
    $ref: "./paths/products.yaml"
  /orders:
    $ref: "./paths/orders.yaml"

components:
  schemas:
    Product:
      $ref: "./schemas/product.yaml"
    Order:
      $ref: "./schemas/order.yaml"
yaml# schemas/product.yaml
type: object
required:
  - id
  - name
  - price
properties:
  id:
    type: integer
    example: 42
  name:
    type: string
    example: "Burger classique"
  price:
    type: integer
    description: Prix en centimes
    example: 1200

Chaque schema dans son fichier. Chaque path dans son fichier. La spec principale ne fait qu'assembler les morceaux. redocly bundle openapi.yaml -o dist/openapi.yaml les fusionne en un seul fichier pour la distribution.

Generer des types TypeScript

openapi-typescript transforme ta spec en types TypeScript. Fini les interfaces manuelles qui derivent :

bashnpx openapi-typescript ./openapi.yaml -o ./src/api-types.ts

Ca généré :

typescriptexport interface components {
  schemas: {
    Product: {
      id: number;
      name: string;
      price: number;
    };
    ProductList: {
      data: components["schemas"]["Product"][];
      pagination: components["schemas"]["Pagination"];
    };
  };
}

Avec openapi-fetch, tu as un client type-safe généré depuis la spec :

typescriptimport createClient from "openapi-fetch";
import type { paths } from "./api-types";

const client = createClient<paths>({ baseUrl: "https://api.example.com" });

const { data } = await client.GET("/products", {
  params: { query: { page: 1, limit: 20 } }
});
// data est type: { data: Product[], pagination: Pagination }

Zero any, zero cast. Si la spec change, le compilateur te dit exactement quoi corriger.

Les examples, c'est pas du luxe

Une spec sans examples est une spec que personne ne lit. Ajoute des examples partout :

yamlcomponents:
  schemas:
    Product:
      type: object
      properties:
        name:
          type: string
          example: "Burger classique"
        price:
          type: integer
          description: Prix en centimes
          example: 1200
        status:
          type: string
          enum: [draft, published, archived]
          example: published

  examples:
    ProductExample:
      summary: Un burger
      value:
        id: 42
        name: "Burger classique"
        price: 1200
        status: published

Les examples apparaissent dans Swagger UI et Redoc. Ils permettent au lecteur de comprendre les donnees en un coup d'oeil, sans lire les descriptions de chaque champ.

Résumé

  • OpenAPI 3.x est le standard pour decrire une API REST en YAML/JSON
  • Spec-first pour les API publiques, code-first pour les projets internes
  • Utilise $ref pour découper une grosse spec en fichiers gignables
  • openapi-typescript + openapi-fetch donnent un client type-safe sans effort
  • Ajoute des examples a chaque schema et chaque réponse

Article précédent : HATEOAS Article suivant : Documentation

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.