Que fait Conception d'API REST ?
Les API sont des produits : le modèle mental du consommateur, et non votre schéma de base de données, décide de la forme. L'erreur coûteuse que cette compétence évite est de livrer une surface incohérente – nommage mélangé, mauvais codes de statut, pagination par décalage sur une table croissante – que chaque consommateur durcit alors autour, rendant l'API effectivement irréparable une fois intégrée.
Procédure opérationnelle
Travaillez dans cet ordre. Les ressources viennent avant les méthodes car la sémantique des méthodes dépend de ce qu'est la ressource ; les contrats d'erreur et de pagination viennent avant la rédaction du cahier des charges car les adapter après coup casse les consommateurs.
Étape 1 : recueillir les prérequis
Collectez avant de concevoir. Étiquetez toute supposition comme une hypothèse.
- Les consommateurs : services internes, clients propriétaires ou développeurs externes. Les consommateurs externes élèvent la barre de cohérence – par défaut, considérez externe si inconnu.
- Les noms principaux du domaine et lesquels possèdent les autres (détermine l'imbrication).
- Tailles de collections attendues et croissance. Une liste pouvant dépasser environ 10 000 lignes exclut la pagination par décalage.
- Ratio lecture/écriture et attentes de latence par endpoint.
- Modèle d'authentification (affecte la correction des codes 401 vs 403).
Étape 2 : nommer les ressources
- Noms au pluriel : /users, /orders, /products.
- Imbriquez un niveau pour la possession : /users/{id}/orders. N'imbriquez pas à plus de deux niveaux – aplatissez avec des filtres à la place (/orders?user_id=... bat /users/{id}/orders/{oid}/items/{iid}).
- Pas de verbes dans les chemins ; la méthode HTTP est le verbe. Pour les actions authentiques qui ne correspondent à aucun verbe CRUD (par exemple, envoyer une facture), modélisez une sous-ressource POST : POST /invoices/{id}/send-attempts.
- Casse cohérente : kebab-case pour les segments de chemin multi-mots, snake_case pour les champs JSON – choisissez une fois, appliquez partout.
Étape 3 : attribuer les méthodes et codes de statut
- GET : lecture, idempotent, pas de corps de requête. 200 avec corps.
- POST : création. Renvoie 201 et un en-tête Location pointant vers la nouvelle ressource. Renvoie la représentation créée dans le corps pour que le client évite un GET ultérieur.
- PUT : remplacement complet, idempotent. Les champs manquants signifient "effacer", pas "conserver".
- PATCH : mise à jour partielle (JSON Merge Patch ou JSON Patch – indiquez lequel dans le cahier des charges).
- DELETE : suppression, idempotent, renvoie 204. Supprimer une ressource déjà supprimée renvoie 204 ou 404 – choisissez et documentez-le.
- Erreurs : 400 pour entrée malformée, 401 pour identifiants manquants/invalides, 403 pour identifiants valides sans autorisation, 404 pour ressources absentes (utilisez aussi 404 au lieu de 403 lorsque révéler l'existence est une fuite), 409 pour conflits d'état, 422 pour entrée bien formée mais sémantiquement invalide, 429 pour limites de débit (avec Retry-After), 500 pour erreurs serveur – n'exposez jamais de traces de pile ou d'identifiants internes.
Étape 4 : fixer le contrat d'erreur
Une forme pour chaque erreur, code lisible par machine en premier :
{
"error": "validation_error",
"message": "L'email est requis",
"field": "email",
"request_id": "req_abc123"
}
Le request_id doit être journalisé côté serveur pour que le support puisse corréler. Le message humain est destiné aux développeurs lisant les logs, jamais à l'affichage pour l'utilisateur final.
Étape 5 : fixer la pagination et le filtrage
- Pagination par curseur pour toute collection qui croît : renvoie un curseur opaque et un indicateur has_more. La pagination par décalage n'est acceptable que pour les listes petites, limitées et destinées à l'administration – elle se dégrade linéairement et saute/duplique les lignes sous des écritures concurrentes.
- Taille de page par défaut 25, maximum 100. Rejetez les tailles de page supérieures au maximum avec 400, ne réduisez pas silencieusement.
- Filtrage et tri via paramètres de requête : ?status=active&sort=created_at:desc. Documentez les champs filtrables ; rejetez les paramètres de filtre inconnus avec 400 plutôt que de les ignorer (ignorer silencieusement cache des bugs clients).
Étape 6 : appliquer les budgets de performance et de charge utile
- Budget de latence p95 : 300 ms pour les endpoints interactifs, 1 s pour les rapports/exports. Si une opération ne peut pas atteindre 1 s, rendez-la asynchrone : 202 Accepté plus une ressource de statut à interroger.
- Budget de charge utile de réponse : maintenez les réponses typiques sous 100 Ko ; tout ce qui dépasse régulièrement 1 Mo nécessite une pagination, une sélection de champs (?fields=) ou un flux d'export dédié.
- Changez les versions cassantes dans le chemin de l'URL (/v1/). Les changements additifs (nouveaux champs optionnels) ne nécessitent pas d'incrément de version. Confiez le calendrier de dépréciation lui-même à api-versioning-strategist.
Contraste travaillé
Mauvais :
POST /getUserOrders?userId=42&page=3
→ 200 { "data": [...], "error": null }
Verbe dans le chemin, sémantique GET sur un POST, pagination par décalage, 200 encapsulant un champ d'erreur obligeant les clients à analyser le corps pour savoir si l'appel a échoué.
Bon :
GET /users/42/orders?status=shipped&limit=25&cursor=eyJpZCI6OTh9
→ 200 { "orders": [...], "next_cursor": "eyJpZCI6MTIzfQ", "has_more": true }
→ 404 { "error": "not_found", "message": "L'utilisateur 42 n'existe pas", "request_id": "req_9f2" }
Chemin nominal, imbrication de possession, pagination par curseur, codes de statut portent le résultat.
Livrable
Produisez un cahier des charges d'endpoint contenant : la table des ressources (chemin, méthodes, codes de statut par méthode), la forme d'erreur partagée, le contrat de pagination (format du curseur, valeurs par défaut, maximum), la liste blanche de filtrage, et les budgets de latence et de charge utile par endpoint.
À NE PAS FAIRE
- Ne reflétez pas le schéma de base de données dans l'API – les consommateurs se couplent à des internes que vous voudrez modifier.
- Ne renvoyez pas 200 pour des échecs avec un corps d'erreur ; les clients et la surveillance se basent sur les codes de statut.
- N'utilisez pas la pagination par décalage sur des collections non bornées – elle duplique et saute des lignes sous des écritures et se dégrade linéairement.
- N'imbriquez pas les chemins à plus de deux niveaux ; une imbrication profonde code un seul chemin d'accès et interdit tous les autres.
- N'ignorez pas silencieusement les paramètres de requête inconnus – un filtre mal typé qui renvoie des données non filtrées est un bug de classe fuite de données.
Niveau de qualité
- Un consommateur pourrait implémenter un client à partir du cahier des charges sans poser une seule question.
- Chaque endpoint indique ses codes de statut, y compris les cas d'erreur.
- Tous les endpoints de collection partagent un contrat de pagination ; toutes les erreurs partagent une forme.
- Des concepts identiques ont des noms identiques à travers tous les chemins et charges utiles.
- Chaque endpoint a un budget de latence explicite et ceux >1 s sont asynchrones.