Une API bien conçue est un plaisir à utiliser ; une API mal conçue devient un casse-tête qu'il faut résoudre à nouveau à chaque requête. Dans cet article, je rassemble les REST API best practices qui aident réellement au quotidien — du nommage des ressources aux méthodes HTTP, en passant par les codes de statut et le versionnage. L'objectif est une interface cohérente et prévisible que d'autres développeurs (et vous-même, dans six mois) peuvent deviner sans ouvrir la documentation.
Nommer les ressources : des noms, pas des verbes
L'idée centrale de REST est de modéliser votre système autour de ressources. Les chemins des endpoints doivent contenir des noms, pas des verbes, et utiliser le pluriel pour les collections. L'action est exprimée par la méthode HTTP ; la mettre dans le chemin est redondant.
- Bon :
GET /users,GET /users/42,POST /users - Mauvais :
GET /getUsers,POST /createUser,GET /user/list
Pour les ressources imbriquées, montrez la relation dans le chemin : les commandes d'un utilisateur se lisent naturellement avec GET /users/42/orders. Mais ne dépassez pas environ deux niveaux d'imbrication ; des chemins comme /users/42/orders/7/items/3/reviews deviennent difficiles à maintenir. Proposez plutôt un accès direct à la sous-ressource : GET /order-items/3. Utilisez des minuscules et des tirets dans les URL (/order-items), et évitez les underscores ou le camelCase.
Utiliser les méthodes HTTP selon leur sens
Chaque méthode possède un contrat explicite, et le respecter permet aux intermédiaires (caches, proxys, navigateurs) de se comporter correctement.
- GET — lit une ressource, sans effet de bord (sûre et idempotente).
- POST — crée une nouvelle ressource ; non idempotente (l'appeler deux fois crée deux enregistrements).
- PUT — remplace la ressource entière ; idempotente (répéter la même requête ne change pas le résultat).
- PATCH — met à jour une partie de la ressource.
- DELETE — supprime la ressource ; idempotente.
Règle pratique : GET ne doit jamais modifier de données. Mettre une action « supprimer » sur un endpoint comme GET /users/42/delete est courant mais incorrect, car un préchargement du navigateur ou un crawler pourrait supprimer l'enregistrement à votre insu. Préférez PUT pour une mise à jour complète et PATCH pour une mise à jour partielle.
Renvoyer les bons codes de statut
Le code de statut est le premier signal de ce qui s'est passé, avant même la lecture du corps de la réponse. Renvoyer toujours 200 OK et écrire {"success": false} dans le corps aveugle le client. Les codes que vous utiliserez le plus :
- 200 OK — GET/PUT/PATCH réussi.
- 201 Created — une ressource a été créée via POST ; renvoyez son adresse dans l'en-tête
Location. - 204 No Content — succès sans corps (généralement DELETE).
- 400 Bad Request — requête malformée ou échec de validation.
- 401 Unauthorized — non authentifié ; 403 Forbidden — authentifié mais sans autorisation.
- 404 Not Found — la ressource n'existe pas.
- 409 Conflict — un conflit (par ex. un e-mail en double).
- 422 Unprocessable Entity — syntaxe valide mais validation sémantique échouée (de nombreux frameworks l'utilisent pour les erreurs de validation).
- 429 Too Many Requests — limite de débit dépassée.
- 500 Internal Server Error — erreur inattendue côté serveur.
Utilisez correctement la différence entre 401 et 403 : le premier signifie « je ne sais pas qui vous êtes », le second « je sais qui vous êtes, mais vous n'avez pas le droit de faire cela ».
Des erreurs cohérentes et lisibles par la machine
Renvoyer chaque erreur dans la même structure permet au client d'écrire une seule routine de gestion des erreurs. Fournissez un message lisible par un humain, un code machine stable et des détails de validation par champ :
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "validation_failed",
"message": "Les données soumises sont invalides.",
"fields": {
"email": ["Saisissez une adresse e-mail valide."],
"age": ["Doit être égal ou supérieur à 18 ans."]
}
}
}
Gardez le champ code stable et indépendant de la langue (validation_failed), et utilisez message pour un texte affichable à l'utilisateur. En production, ne divulguez jamais les détails internes des erreurs 500 (stack traces, SQL) ; renvoyez plutôt un request_id pour faire le lien avec le support.
Versionnage : gérer le changement sans le casser
Dès que votre API est en ligne, tout changement rompant la rétrocompatibilité casse les clients existants. C'est pourquoi une stratégie de versionnage doit être décidée dès le départ. L'approche la plus courante et la plus visible consiste à mettre la version dans le chemin :
GET /v1/users
GET /v2/users
Vous pouvez aussi transporter la version dans un en-tête (Accept: application/vnd.api+json; version=2) ; c'est considéré comme plus « pur » mais plus difficile à découvrir et à tester. Pour la plupart des équipes, le versionnage par URL est pratique et clair. Principe clé : ajouter un champ n'est généralement pas cassant, mais supprimer, renommer ou changer le type d'un champ l'est et nécessite une nouvelle version. Publiez un calendrier de dépréciation pour les anciennes versions.
Endpoints de liste : pagination, filtrage, tri
Renvoyer une collection telle quelle fonctionne pour de petits jeux de données, mais à mesure que la table grandit, cela submerge le serveur comme le client. Ajoutez la pagination aux endpoints de liste dès le début. Il existe deux approches courantes :
- Par offset :
GET /users?page=3&per_page=20— simple, permet de sauter à un numéro de page ; mais elle dérive si les données changent fréquemment. - Par curseur :
GET /users?limit=20&cursor=eyJpZCI6MTQ0fQ— plus stable et performante sur de grands jeux de données qui changent souvent.
Exprimez le filtrage et le tri avec des paramètres de requête : GET /users?status=active&sort=-created_at. Un - en tête dans le tri est une convention courante pour l'ordre décroissant. Renvoyer le nombre total et les informations de page suivante dans un bloc meta facilite la navigation du client.
Quelques principes supplémentaires
- HTTPS partout : une API transportant des jetons et des données personnelles ne doit fonctionner que sur TLS.
- Cohérence JSON : tenez-vous à un seul style de nommage des champs (
snake_caseoucamelCase) et ne le changez pas dans l'API. - ISO 8601 et UTC pour les dates : le format
2026-06-27T14:30:00Zélimine l'ambiguïté. - Documentation : publiez un schéma lisible par la machine avec OpenAPI (Swagger), qui vous donne à la fois la documentation et la génération de code client.
- Limitation de débit et authentification : ajoutez une limitation de débit pour prévenir les abus et signalez son état avec les en-têtes appropriés.
Questions fréquentes
Dois-je utiliser PUT ou PATCH ?
Utilisez PUT lorsque vous envoyez la ressource entière et la remplacez ; utilisez PATCH lorsque vous n'envoyez que les quelques champs qui changent. PUT est idempotente : envoyer le même corps complet plusieurs fois ne change pas le résultat. PATCH sert aux mises à jour partielles, et son corps ne transporte que les champs à modifier.
Dois-je garder la version dans l'URL ou dans un en-tête ?
Les deux sont valides. Le versionnage par URL (/v1/...) est le choix de la plupart des équipes car il est facile à découvrir et à tester dans un navigateur. Le versionnage par en-tête est plus proche de la pureté REST mais plus contraignant en termes d'outillage et de visibilité. Tant que vous restez cohérent, les deux conviennent.
Ne puis-je pas simplement renvoyer 200 pour tout et signaler les erreurs dans le corps ?
Non. Les codes de statut HTTP existent précisément pour cela ; les couches de cache, les proxys et les bibliothèques client s'y fient. Utiliser des codes 4xx/5xx pour les erreurs permet au client de prendre la bonne décision sans analyser le corps de la réponse, et cela améliore l'observabilité.
Vous souhaitez concevoir votre API de zéro ou mettre vos endpoints existants aux normes ? Du modèle de ressources au versionnage et à la documentation, je peux vous aider à bâtir une interface REST propre et cohérente. Contactez-moi et consolidons ensemble l'API de votre projet.