Pourquoi une documentation centrée usage
Une documentation d’API REST utile n’est pas une liste d’URLs : c’est un guide d’intégration. Elle doit permettre à un lecteur (développeur front, partenaire, QA, SRE) de comprendre rapidement quoi appeler, avec quelles données, ce qu’il reçoit, et comment réagir aux erreurs. L’objectif est de formaliser un contrat : un ensemble de règles stables, testables et partageables entre producteurs et consommateurs.
Ce que doit contenir chaque ressource
- Description fonctionnelle : à quoi sert la ressource et dans quels cas l’utiliser.
- Endpoints : opérations disponibles, avec prérequis (authentification, rôles, scopes).
- Paramètres : chemin, query, headers, corps, avec types, contraintes, valeurs par défaut.
- Exemples réalistes : requêtes et réponses complètes (headers inclus si utiles).
- Codes d’erreur : cas d’erreur fréquents, format de réponse, et actions recommandées côté client.
- Règles de collection : pagination, tri, filtres autorisés, limites et comportements par défaut.
Gabarit pratique : documenter un endpoint de bout en bout
Utilisez un gabarit répétable pour éviter les oublis. Exemple sur une ressource projects (projets) et ses tâches tasks.
1) Décrire l’intention et les prérequis
But : créer une tâche dans un projet. Pré-requis : l’utilisateur doit avoir le droit project:write sur le projet.
2) Définir l’endpoint et ses paramètres
Méthode + chemin : POST /projects/{projectId}/tasks
projectId(path, string) : identifiant du projet.Idempotency-Key(header, optionnel) : clé d’idempotence pour éviter les doublons lors de retries.
3) Décrire le corps de requête (champs, contraintes, règles)
Body (JSON) :
- Écoutez le fichier audio avec l'écran éteint.
- Obtenez un certificat à la fin du programme.
- Plus de 5000 cours à découvrir !
Téléchargez l'application
title(string, requis) : 3–120 caractères.dueDate(string date, optionnel) : date d’échéance au format ISO 8601 (ex.2026-02-01).priority(string, optionnel) :low|medium|high, défautmedium.
4) Fournir un exemple de requête complet
POST /projects/prj_123/tasks HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: 2f6b0c2a-2f1d-4b6a-9e2a-1f3a7c9b2d11
{
"title": "Préparer la démo client",
"dueDate": "2026-02-01",
"priority": "high"
}5) Documenter la réponse de succès
Réponse : 201 Created
Headers : Location: /projects/prj_123/tasks/tsk_987
HTTP/1.1 201 Created
Content-Type: application/json
Location: /projects/prj_123/tasks/tsk_987
{
"id": "tsk_987",
"projectId": "prj_123",
"title": "Préparer la démo client",
"dueDate": "2026-02-01",
"priority": "high",
"status": "open",
"createdAt": "2026-01-17T10:15:30Z",
"updatedAt": "2026-01-17T10:15:30Z"
}6) Documenter les erreurs attendues (table de référence)
Listez les erreurs les plus probables, avec un exemple et une action côté client.
| Code | Quand | Exemple de réponse | Action client |
|---|---|---|---|
| 400 | Corps invalide (champ manquant, format) | | Corriger les champs et réessayer |
| 401 | Token absent/expiré | | Rafraîchir l’authentification |
| 403 | Droit insuffisant | | Demander les droits / changer de compte |
| 404 | Projet inexistant | | Vérifier l’identifiant |
| 409 | Conflit (ex. idempotence, état) | | Ne pas dupliquer, récupérer la ressource créée |
Règles de pagination/tri : comment les documenter sans ambiguïté
Même si les mécanismes sont déjà définis, la documentation doit préciser les comportements exacts pour éviter les interprétations divergentes.
Checklist de documentation pour une collection
- Paramètres : noms, types, valeurs par défaut, bornes (max page size).
- Tri : champs autorisés, ordre par défaut, stabilité du tri (important pour pagination).
- Pagination : stratégie (offset/cursor), format des liens/curseurs, comportement en fin de liste.
- Filtres : opérateurs autorisés, champs filtrables, combinaison (AND/OR), encodage.
- Exemples : au moins un exemple simple et un exemple combiné (filtre + tri + pagination).
Exemple documenté : lister les tâches d’un projet
Endpoint : GET /projects/{projectId}/tasks
Query :
limit(int, défaut 20, max 100)cursor(string, optionnel) : curseur opaque renvoyé par l’APIsort(string, défaut-createdAt) : champs autoriséscreatedAt,dueDate,priority(préfixe-pour décroissant)status(string, optionnel) :open|done
GET /projects/prj_123/tasks?status=open&sort=dueDate&limit=2 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>HTTP/1.1 200 OK
Content-Type: application/json
{
"items": [
{
"id": "tsk_101",
"title": "Envoyer l’agenda",
"dueDate": "2026-01-20",
"priority": "medium",
"status": "open",
"createdAt": "2026-01-10T09:00:00Z",
"updatedAt": "2026-01-12T08:00:00Z"
},
{
"id": "tsk_102",
"title": "Préparer la démo",
"dueDate": "2026-02-01",
"priority": "high",
"status": "open",
"createdAt": "2026-01-17T10:15:30Z",
"updatedAt": "2026-01-17T10:15:30Z"
}
],
"page": {
"limit": 2,
"nextCursor": "eyJvZmZzZXQiOjJ9"
}
}Documentez explicitement que nextCursor est à renvoyer tel quel dans cursor, sans décodage côté client.
Contrat OpenAPI : structurer une spécification réutilisable
OpenAPI permet de décrire l’API de manière indépendante du langage : endpoints, paramètres, schémas, réponses, sécurité, exemples. Une bonne spécification évite la duplication via components et rend le contrat testable (linting, génération de clients, tests de conformité).
Organisation recommandée
- paths : opérations par endpoint (GET/POST/…)
- components/schemas : modèles réutilisables (Task, Error, Page…)
- components/responses : réponses standard (ValidationError, NotFound…)
- components/parameters : paramètres communs (projectId, cursor, limit, sort…)
- components/examples : exemples nommés et réutilisables
- securitySchemes : mécanismes d’authentification
Extrait OpenAPI (YAML) : composants réutilisables
openapi: 3.0.3
info:
version: 1.0.0
title: Example Projects API
paths: {}
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
Task:
type: object
required: [id, projectId, title, status, createdAt, updatedAt]
properties:
id: { type: string, example: "tsk_987" }
projectId: { type: string, example: "prj_123" }
title: { type: string, minLength: 3, maxLength: 120, example: "Préparer la démo client" }
dueDate: { type: string, format: date, nullable: true, example: "2026-02-01" }
priority:
type: string
enum: [low, medium, high]
example: high
status:
type: string
enum: [open, done]
example: open
createdAt: { type: string, format: date-time, example: "2026-01-17T10:15:30Z" }
updatedAt: { type: string, format: date-time, example: "2026-01-17T10:15:30Z" }
ErrorResponse:
type: object
required: [error]
properties:
error:
type: object
required: [code, message]
properties:
code: { type: string, example: "VALIDATION_ERROR" }
message: { type: string, example: "Invalid request body" }
details:
type: array
items:
type: object
properties:
field: { type: string, example: "title" }
issue: { type: string, example: "required" }
parameters:
ProjectId:
name: projectId
in: path
required: true
schema: { type: string }
Limit:
name: limit
in: query
required: false
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
Cursor:
name: cursor
in: query
required: false
schema: { type: string }
SortTasks:
name: sort
in: query
required: false
schema:
type: string
default: "-createdAt"
description: "Allowed: createdAt, dueDate, priority. Prefix '-' for descending."
responses:
ValidationError:
description: Invalid request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
missingTitle:
value:
error:
code: VALIDATION_ERROR
message: Invalid request body
details:
- field: title
issue: required
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"Extrait OpenAPI (YAML) : décrire un endpoint en réutilisant les composants
paths:
/projects/{projectId}/tasks:
post:
summary: Create a task in a project
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/ProjectId"
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [title]
properties:
title: { type: string, minLength: 3, maxLength: 120 }
dueDate: { type: string, format: date }
priority: { type: string, enum: [low, medium, high], default: medium }
examples:
createHighPriority:
value:
title: "Préparer la démo client"
dueDate: "2026-02-01"
priority: high
responses:
"201":
description: Created
headers:
Location:
schema: { type: string }
description: URL of the created task
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
"400": { $ref: "#/components/responses/ValidationError" }
"404": { $ref: "#/components/responses/NotFound" }
/projects/{projectId}/tasks:
get:
summary: List tasks in a project
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/ProjectId"
- $ref: "#/components/parameters/Limit"
- $ref: "#/components/parameters/Cursor"
- $ref: "#/components/parameters/SortTasks"
- name: status
in: query
required: false
schema: { type: string, enum: [open, done] }
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
required: [items, page]
properties:
items:
type: array
items: { $ref: "#/components/schemas/Task" }
page:
type: object
required: [limit]
properties:
limit: { type: integer }
nextCursor: { type: string, nullable: true }
examples:
firstPage:
value:
items: []
page:
limit: 20
nextCursor: nullConseil pratique : gardez les exemples OpenAPI cohérents avec ceux de la documentation “humaine” (mêmes IDs, mêmes champs, mêmes scénarios) afin de réduire les divergences.
Maintenir la documentation à jour : pratiques opérationnelles
Définir une source de vérité
- Contrat d’abord : la spécification OpenAPI est la référence. La documentation narrative (guides) s’appuie dessus.
- Ou implémentation d’abord : si l’API évolue vite, imposez au minimum une mise à jour de la spec dans la même livraison que le code.
- Règle d’équipe : aucune modification d’endpoint/paramètre/schéma n’est “done” sans mise à jour du contrat et d’au moins un exemple.
Rendre les exemples réalistes et testables
- Utilisez des exemples qui ressemblent à la production (formats d’ID, dates, statuts, valeurs limites).
- Montrez les headers réellement nécessaires (auth, idempotence, content-type).
- Ajoutez au moins un exemple d’erreur par endpoint critique (validation, droits, ressource absente).
- Alignez les exemples sur des scénarios d’intégration complets (voir plus bas) pour qu’ils puissent servir de base à des tests automatisés.
Gérer les changements : changelog orienté intégration
Un changelog utile répond à : “qu’est-ce qui change pour moi, et que dois-je faire ?”. Pour chaque entrée :
- Date et version de la spec (ou tag de release).
- Type : ajout, modification, dépréciation, suppression.
- Impact : breaking ou non, et pourquoi.
- Action : migration recommandée, exemple avant/après.
## 2026-01-17 (spec 1.1.0)
- Added: query parameter `status` on GET /projects/{projectId}/tasks (open|done)
Impact: non-breaking
Action: optional; clients may filter tasks by status
- Changed: Task.priority now defaults to `medium`
Impact: non-breaking
Action: remove client-side default if duplicatedScénarios d’intégration bout-en-bout (CRUD) : une doc qui se lit comme un tutoriel
Au lieu de documenter chaque endpoint isolément, proposez un parcours complet. Cela réduit les erreurs d’intégration (ordre des appels, dépendances, IDs à réutiliser) et sert de base à des tests de conformité.
Scénario : gérer une tâche dans un projet
Étape 1 — Créer une tâche
POST /projects/prj_123/tasks
Content-Type: application/json
Authorization: Bearer <token>
{
"title": "Préparer la démo client",
"dueDate": "2026-02-01",
"priority": "high"
}À retenir : stocker l’id renvoyé (tsk_987) et l’URL Location si présente.
Étape 2 — Lire la tâche
GET /projects/prj_123/tasks/tsk_987
Authorization: Bearer <token>HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "tsk_987",
"projectId": "prj_123",
"title": "Préparer la démo client",
"dueDate": "2026-02-01",
"priority": "high",
"status": "open",
"createdAt": "2026-01-17T10:15:30Z",
"updatedAt": "2026-01-17T10:15:30Z"
}Étape 3 — Mettre à jour la tâche
Documentez clairement les champs modifiables et la manière dont les champs omis sont traités (inchangés). Exemple : mise à jour partielle.
PATCH /projects/prj_123/tasks/tsk_987
Content-Type: application/json
Authorization: Bearer <token>
{
"status": "done"
}HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "tsk_987",
"projectId": "prj_123",
"title": "Préparer la démo client",
"dueDate": "2026-02-01",
"priority": "high",
"status": "done",
"createdAt": "2026-01-17T10:15:30Z",
"updatedAt": "2026-01-17T11:02:00Z"
}Étape 4 — Lister et vérifier via pagination/tri
GET /projects/prj_123/tasks?status=done&sort=-updatedAt&limit=10
Authorization: Bearer <token>À vérifier côté client : le tri est respecté, et la pagination est stable (pas de doublons/absences lors du parcours par curseur).
Étape 5 — Supprimer la tâche
DELETE /projects/prj_123/tasks/tsk_987
Authorization: Bearer <token>HTTP/1.1 204 No ContentCas à documenter : suppression d’une ressource déjà supprimée (réponse attendue), et effets de bord éventuels (ex. recalcul de compteurs, événements).
Checklist de qualité pour une documentation et un contrat solides
- Chaque endpoint a : but, prérequis, paramètres, schémas, exemples, erreurs, et comportements par défaut.
- Les exemples sont cohérents entre guide et OpenAPI, et réutilisent les mêmes identifiants.
- Les paramètres communs (pagination/tri, IDs) sont factorisés dans
components/parameters. - Les réponses d’erreur sont standardisées via
components/responseset un schéma d’erreur unique. - Un changelog décrit l’impact et l’action attendue, pas seulement la liste des commits.
- Au moins un scénario bout-en-bout (CRUD) est documenté et peut être rejoué comme test d’intégration.