API REST back-end : documentation claire et contrat d’API

Capítulo 8

Temps de lecture estimé : 11 minutes

+ Exercice

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) :

Continuez dans notre application.
  • Écoutez le fichier audio avec l'écran éteint.
  • Obtenez un certificat à la fin du programme.
  • Plus de 5000 cours à découvrir !
Ou poursuivez votre lecture ci-dessous...
Download App

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éfaut medium.

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.

CodeQuandExemple de réponseAction client
400Corps invalide (champ manquant, format)
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": [
      {"field": "title", "issue": "required"}
    ]
  }
}
Corriger les champs et réessayer
401Token absent/expiré
{"error":{"code":"UNAUTHENTICATED","message":"Missing or invalid token"}}
Rafraîchir l’authentification
403Droit insuffisant
{"error":{"code":"FORBIDDEN","message":"Not allowed to write tasks in this project"}}
Demander les droits / changer de compte
404Projet inexistant
{"error":{"code":"NOT_FOUND","message":"Project not found"}}
Vérifier l’identifiant
409Conflit (ex. idempotence, état)
{"error":{"code":"CONFLICT","message":"Duplicate request"}}
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’API
  • sort (string, défaut -createdAt) : champs autorisés createdAt, 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: null

Conseil 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 duplicated

Scé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 Content

Cas à 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/responses et 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.

Répondez maintenant à l’exercice sur le contenu :

Quel ensemble d’éléments décrit le mieux un endpoint d’API REST de façon à servir de contrat stable et testable pour l’intégration ?

Tu as raison! Félicitations, passez maintenant à la page suivante

Vous avez raté! Essayer à nouveau.

Une documentation orientée usage formalise un contrat partageable : elle précise intention, prérequis, paramètres et contraintes, exemples complets, succès (codes/headers), erreurs avec actions côté client, et règles de collection (pagination/tri/filtres) pour éviter toute ambiguïté.

Couverture de livre électronique gratuite Développement back-end : concevoir une API REST propre
100%

Développement back-end : concevoir une API REST propre

Nouveau cours

8 pages

Téléchargez l'application pour obtenir une certification gratuite et écouter des cours en arrière-plan, même avec l'écran éteint.