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.