Aller au contenu

Documentation d'API

OpenAPI, AsyncAPI et génération automatique — publier un contrat API clair et toujours à jour pour vos consommateurs.


Pourquoi documenter une API ?

Une API non documentee est une boite noire. Ses consommateurs doivent lire le code source, deviner les champs requis, tester en aveugle. La documentation d'API remplacé les allers-retours en Slack par une référence consultable à tout moment.

API-first vs code-first

  • API-first : on écrit la spec OpenAPI avant le code. L'implémentation suit le contrat.
  • Code-first : on écrit le code et on généré la spec depuis les annotations.

Les deux approches sont valides. L'API-first favorise le design délibéré ; le code-first colle mieux au code existant.


OpenAPI / Swagger

OpenAPI (anciennement Swagger) est le standard pour documenter les API REST. Un fichier YAML ou JSON décrit chaque endpoint, ses parametres, ses corps de requête et ses réponses.

Structure d'un fichier OpenAPI

openapi: "3.1.0"
info:
  title: "API Commandes"
  version: "2.0.0"
  description: |
    API REST pour la gestion des commandes e-commerce.
    Toutes les reponses sont en JSON. Les erreurs suivent RFC 7807.
  contact:
    name: "Equipe Commandes"
    email: "commandes@example.com"

servers:
  - url: "https://api.example.com/v2"
    description: "Production"
  - url: "https://api.staging.example.com/v2"
    description: "Staging"

paths:
  /orders:
    get:
      summary: "Lister les commandes"
      operationId: "listOrders"
      tags: ["orders"]
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, confirmed, shipped, delivered, cancelled]
          description: "Filtrer par statut"
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        "200":
          description: "Liste paginee des commandes"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderPage"
        "401":
          $ref: "#/components/responses/Unauthorized"

    post:
      summary: "Creer une commande"
      operationId: "createOrder"
      tags: ["orders"]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrderRequest"
            example:
              customer_id: "usr_123"
              lines:
                - product_sku: "PROD-001"
                  quantity: 2
      responses:
        "201":
          description: "Commande creee"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "422":
          $ref: "#/components/responses/ValidationError"

components:
  schemas:
    Order:
      type: object
      required: [id, status, total, created_at]
      properties:
        id:
          type: string
          example: "ord_abc123"
        status:
          type: string
          enum: [pending, confirmed, shipped, delivered, cancelled]
        total:
          type: number
          format: float
          example: 59.99
        created_at:
          type: string
          format: date-time

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - BearerAuth: []

Annotations depuis le code

Python avec FastAPI

FastAPI généré le schéma OpenAPI automatiquement depuis les type hints et les modèles Pydantic.

from fastapi import FastAPI, HTTPException, Query
from pydantic import BaseModel, Field
from typing import Optional
from enum import Enum

app = FastAPI(
    title="API Commandes",
    version="2.0.0",
    description="API REST pour la gestion des commandes e-commerce.",
)

class OrderStatus(str, Enum):
    pending = "pending"
    confirmed = "confirmed"
    shipped = "shipped"

class CreateOrderRequest(BaseModel):
    customer_id: str = Field(..., description="ID du client", example="usr_123")
    notes: Optional[str] = Field(None, max_length=500, description="Notes internes")

    model_config = {
        "json_schema_extra": {
            "example": {"customer_id": "usr_123", "notes": "Livraison urgente"}
        }
    }

@app.post(
    "/orders",
    response_model=OrderResponse,
    status_code=201,
    summary="Creer une commande",
    tags=["orders"],
    responses={
        422: {"description": "Donnees invalides"},
        401: {"description": "Non authentifie"},
    },
)
async def create_order(request: CreateOrderRequest):
    """
    Cree une nouvelle commande pour le client specifie.

    La commande est initialement en statut **pending**.
    Elle passe en **confirmed** apres validation du paiement.
    """
    # Implementation
    ...

Java avec Spring Boot + SpringDoc

@RestController
@RequestMapping("/api/v2/orders")
@Tag(name = "orders", description = "Gestion des commandes")
public class OrderController {

    @Operation(
        summary = "Creer une commande",
        description = "Cree une commande en statut pending. Le paiement est traite de facon asynchrone.",
        responses = {
            @ApiResponse(responseCode = "201", description = "Commande creee"),
            @ApiResponse(responseCode = "422", description = "Donnees invalides",
                content = @Content(schema = @Schema(implementation = ValidationError.class)))
        }
    )
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public OrderResponse createOrder(
            @Valid @RequestBody
            @io.swagger.v3.oas.annotations.parameters.RequestBody(
                description = "Details de la commande",
                required = true
            )
            CreateOrderRequest request) {
        return orderService.create(request);
    }
}

AsyncAPI pour les API evenementielles

Pour les architectures event-driven (Kafka, RabbitMQ, WebSocket), AsyncAPI est l'équivalent d'OpenAPI.

asyncapi: "3.0.0"
info:
  title: "Events Commandes"
  version: "1.0.0"
  description: "Evenements emis par le service de commandes."

channels:
  order/created:
    description: "Emis quand une nouvelle commande est creee."
    messages:
      orderCreated:
        $ref: "#/components/messages/OrderCreated"

  order/status-changed:
    description: "Emis a chaque changement de statut d'une commande."
    messages:
      statusChanged:
        $ref: "#/components/messages/OrderStatusChanged"

components:
  messages:
    OrderCreated:
      name: "OrderCreated"
      contentType: "application/json"
      payload:
        type: object
        required: [order_id, customer_id, total, timestamp]
        properties:
          order_id:
            type: string
            example: "ord_abc123"
          customer_id:
            type: string
          total:
            type: number
          timestamp:
            type: string
            format: date-time

Outils de rendu et de publication

Outil Type Description
Swagger UI Web Interface interactive incluse dans FastAPI/Spring
Redoc Web Rendu clair, trois colonnes, sans essais
Stoplight SaaS Editor + publication + mock server
Scalar Web / NPM Moderne, rapide, open-source
Spectral CLI Linter OpenAPI, valide les conventions
# Valider un fichier OpenAPI avec Spectral
npx @stoplight/spectral-cli lint openapi.yaml

# Generer un site Redoc statique
npx @redocly/cli build-docs openapi.yaml --output docs/api/index.html

Versioning et changelog de l'API

Changelog structuré

# Changelog API Commandes

## v2.1.0 — 2024-09-01

### Ajouts
- `GET /orders/{id}/timeline` : historique des changements de statut.
- Champ `notes` (optionnel) sur `CreateOrderRequest`.

### Modifications (non-breaking)
- `GET /orders` : nouveau parametre `?sort=created_at|total`.

## v2.0.0 — 2024-06-01

### Modifications incompatibles
- `customer` renomme en `customer_id` dans les reponses (breaking).
- Suppression du champ `legacy_ref` (deprecie depuis v1.5).

### Migration depuis v1.x
Voir [guide de migration](./migration-v1-v2.md).

Versionnez vos API

Une API sans version est une promesse impossible a tenir. Des le premier consommateur externe, ajoutez /v1/ dans votre URL ou un header API-Version. Cela vous permettra de faire évoluer sans casser.


Prochaine étape

Vous savez maintenant documenter le code, les décisions et les API. Voyons comment maintenir cette documentation vivante dans le temps.

Bonnes pratiques →