Concevoir une API propre : pagination, filtres, versioning, erreurs
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
}
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égie | Exemple | Avantage | Inconvénient |
|---|---|---|---|
| Dans l’URI | /v2/users | Simple, visible, cachable par URI | « Pollue » l’espace des ressources |
| Dans un en-tête | Accept: application/vnd.exemple.v2+json | URI stable dans le temps | Moins visible, plus dur à tester dans un navigateur |
| Dans un paramètre | /users?version=2 | Simple à ajouter | Peu conventionnel, souvent déconseillé |
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.
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
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 ».
Écris un format d’erreur JSON cohérent (façon RFC 7807 ou maison) que tu utiliserais pour toutes les erreurs
4xxde 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.