← API REST : concevoir, mûrir, documenter

Concevoir une API propre : pagination, filtres, versioning, erreurs

≈ 25 minutes · Prérequis : Niveau 3 : HATEOAS, le sommet de la maturité REST

Le gain du jour : les quatre décisions de conception qui séparent une API REST « qui marche » d’une API REST que les autres équipes aiment utiliser.

Pagination : ne jamais renvoyer une collection entière

Dès qu’une collection peut dépasser quelques dizaines d’éléments, il faut la découper. Deux stratégies dominent :

Pagination par décalage (offset/limit), simple mais fragile si la collection change pendant la navigation :

GET /users?page=2&per_page=20
{
  "data": [ /* 20 utilisateurs */ ],
  "page": 2,
  "per_page": 20,
  "total": 143
}

Pagination par curseur (cursor-based), plus robuste face aux insertions/suppressions concurrentes, utilisée par la plupart des grandes API (Stripe, GitHub) :

GET /users?limit=20&cursor=eyJpZCI6NDJ9
{
  "data": [ /* 20 utilisateurs */ ],
  "next_cursor": "eyJpZCI6NjJ9",
  "has_more": true
}
Pourquoi le curseur bat l’offset à grande échelle

Avec page=2&per_page=20, si un élément est supprimé pendant que le client feuillette, toute la pagination se décale et certains éléments sont sautés ou dupliqués. Le curseur encode « à partir de quel élément continuer », ce qui reste stable même si la collection change entre deux appels.

Filtrage et tri : des paramètres de requête, jamais de nouvelles URI

Filtrer et trier une collection ne créent pas de nouvelles ressources — donc pas de nouvelles URI, seulement des paramètres de requête (query parameters) sur l’URI existante :

GET /orders?status=pending&sort=-created_at&customer_id=42

Ici : status=pending filtre, sort=-created_at trie par date décroissante (le - signale l’ordre descendant, convention très répandue), customer_id=42 filtre encore. L’URI de base reste /orders : c’est toujours la même ressource collection, simplement vue sous un angle différent.

Versioning : préparer le jour où l’API doit casser

Une API évolue. Un jour, un changement incompatible (retirer un champ, changer un type) sera nécessaire. Trois stratégies principales :

StratégieExempleAvantageInconvénient
Dans l’URI/v2/usersSimple, visible, cachable par URI« Pollue » l’espace des ressources
Dans un en-têteAccept: application/vnd.exemple.v2+jsonURI stable dans le tempsMoins visible, plus dur à tester dans un navigateur
Dans un paramètre/users?version=2Simple à ajouterPeu conventionnel, souvent déconseillé
Le choix pragmatique

Le versioning dans l’URI (/v1/, /v2/) est le plus simple à comprendre pour les développeurs qui consomment ton API, et le plus facile à tester (il suffit de changer l’URL dans un navigateur). C’est le choix par défaut de la majorité des API publiques (Stripe, GitHub, Twitter), malgré l’objection théorique qu’une « ressource » ne devrait pas changer d’identité entre deux versions.

Erreurs : un format cohérent, sur toute l’API

Le code de statut (leçon 2) dit quelle catégorie d’erreur s’est produite ; le corps de la réponse doit dire pourquoi, avec un format identique partout dans l’API — sinon chaque endpoint devient un cas particulier à gérer côté client.

// 422 Unprocessable Entity
{
  "error": {
    "code": "validation_failed",
    "message": "La requête contient des champs invalides",
    "details": [
      { "field": "email", "issue": "format invalide" },
      { "field": "age", "issue": "doit être un entier positif" }
    ]
  }
}

Un format d’erreur standard permet au client d’écrire un seul morceau de code qui gère toutes les erreurs de l’API, plutôt qu’un traitement différent par endpoint.

RFC 7807 : un standard tout fait

Plutôt que d’inventer son propre format, la RFC 7807 — Problem Details for HTTP APIs propose un format JSON standardisé (type, title, status, detail, instance) reconnu par de nombreux frameworks — un bon point de départ si aucune contrainte n’impose un format maison.

Vérifie ta compréhension

1. Pourquoi la pagination par curseur est-elle préférée à la pagination par offset dans une collection qui change souvent ?

2. Comment filtrer une collection d'API REST sans créer de nouvelles ressources ?

À toi de jouer

  1. Conçois l’URI complète (verbe, chemin, paramètres de requête) pour : « les 10 premières commandes en attente du client 42, triées par montant décroissant ».

  2. Écris un format d’erreur JSON cohérent (façon RFC 7807 ou maison) que tu utiliserais pour toutes les erreurs 4xx de ta propre API — et vérifie qu’il peut représenter à la fois une erreur de validation à plusieurs champs et une simple ressource introuvable.

Pour aller plus loin

📖 Sources principales : RFC 7807 — Problem Details for HTTP APIs, et le guide de conception d’API de Stripe sur le versioning, une référence souvent citée pour ses choix pragmatiques.

🗂 Fiche de référence associée : Verbes, codes, en-têtes — antisèche.