Un endpoint qui renvoie 200 OK ne veut pas dire qu'il est bien conçu. J'ai vu passer des API parfaitement fonctionnelles que personne ne voulait intégrer, parce que le contrat était flou, les erreurs imprévisibles et la doc périmée depuis deux versions. Concevoir et documenter des endpoints REST, ce n'est pas un exercice de style: c'est ce qui décide si un développeur tiers va réussir à brancher votre service en une après-midi ou abandonner au bout de trois heures.
La bonne nouvelle, c'est qu'il existe des conventions solides. La mauvaise, c'est que la plupart des articles s'arrêtent aux codes HTTP et au pluriel dans les URLs. Le vrai sujet commence après.
Points clés à retenir
- Un endpoint se conçoit comme un contrat, pas comme une route.
- Les codes HTTP racontent une histoire; les messages d'erreur la racontent en détail.
- La doc doit être un artefact vérifié, pas un fichier qui dort.
- Nommer une ressource au pluriel ne suffit pas: la hiérarchie compte.
- Le versionnement se décide avant le premier client, pas après.
- Un exemple exécutable vaut trois paragraphes d'explication.
Concevoir une API REST : des endpoints qui tiennent dans le temps
Quand je reprends le code d'une API que j'ai écrite il y a quelques années, je me rends compte d'une chose: les endpoints qui ont survécu sont ceux qui répondaient à une seule question métier évidente. Les autres, ceux que j'avais « factorisés » pour être génériques, ont tous fini par être cassés, contournés, ou dupliqués à côté.
Un endpoint REST acceptable, c'est une URL qui se lit comme une phrase, un verbe HTTP qui fait ce qu'il promet, et une réponse dont la forme ne surprend jamais. Le reste, c'est de la décoration.
Convention de nommage d'une API REST : ce qui marche vraiment
La règle du pluriel sur les ressources est partout, donc autant l'appliquer, mais elle ne sauve personne. Ce qui sauve, c'est de ne jamais mettre un verbe dans l'URL. Un POST /orders/42/cancel se défend quand l'action est vraiment une transition d'état, mais un GET /getUserById trahit une conception qui n'a pas réfléchi à la ressource.
Quelques principes sur lesquels je ne bouge plus:
- Les ressources au pluriel:
/invoices,/invoices/1234/lines. Jamais/invoice. - Pas de verre dans le chemin. Le verbe, c'est la méthode HTTP.
- Les filtres passent par des query params typés:
?status=paid&created_after=2026-01-01. - Hiérarchie courte. Au-delà de trois niveaux d'imbrication, une ressource indépendante avec une foreign key est plus lisible.
- Identifiants opaques. Ni auto-incrément exposé, ni UUID v1 (il fuit le timestamp de création).
Franchement, une API dont les URLs se devinent à moitié, c'est déjà une bonne API.
Les erreurs font partie du contrat, pas de la décoration
Voilà le point que je vois le plus souvent bâclé. Une API qui renvoie 400 Bad Request avec un corps {"error": "invalid"} oblige chaque intégrateur à deviner ce qui s'est passé. Et deviner, sur une API en production, ça coûte cher en support.
La bonne approche: un format d'erreur stable, documenté, et qui ne change jamais. Je m'appuie sur une variation de RFC 7807 (Problem Details for HTTP APIs), avec un type stable, un title lisible, un status qui reprend le code HTTP, et un detail qui explique le contexte précis.
{
"type": "https://api.example.com/problems/insufficient-funds",
"title": "Solde insuffisant",
"status": 422,
"detail": "Le compte 8842 ne peut pas débiter 320,00 EUR (solde: 45,10 EUR).",
"instance": "/transfers/abc-123",
"balance": 45.10,
"requested": 320.00
}
Deux choses importantes dans ce que je viens d'écrire. Le type est une URI: c'est un identifiant stable, il ne change jamais, et il peut servir de clé de traitement côté client. Et detail est en français parce que l'utilisateur final le lira peut-être. Si votre API est appelée par des machines uniquement, gardez title en anglais et mettez le texte localisé ailleurs.
Un détail qui m'a coûté une migration douloureuse: ne mettez jamais de code numérique « code interne » dans le corps d'erreur si vous n'êtes pas prêt à le geler pour dix ans. Un "code": 4471 devient un contrat de fait dès le premier client qui l'utilise.
Documenter une API REST : quand la doc devient un artefact testé
Un fichier OpenAPI qui n'est pas validé en intégration continue dérive. Toujours. En quelques semaines, il annonce un champ qui n'existe plus, oublie un nouveau enum, ou décrit une pagination qui a changé de forme. J'ai vu un limit passer de 100 à 50 sans que personne ne mette à jour le YAML — résultat, un client tiers qui pagine sur 100 reçoit des pages tronquées et ne comprend pas.
La seule méthode que j'ai trouvée qui tienne: traiter la spec comme du code. Elle est versionnée, elle est lintée, elle est comparée avant chaque release.
Les outils qui font le travail à votre place
Trois briques, dans l'ordre où je les ai installées:
- Spectral pour le lint du fichier OpenAPI (règles de nommage, présence de descriptions, exemples obligatoires sur les schémas).
- openapi-diff en CI: si une PR retire un champ d'une réponse ou ajoute un paramètre requis, le build casse. C'est brutal, et c'est exactement ce qu'il faut.
- Schemathesis ou équivalent pour fuzz-tester l'implémentation à partir de la spec: on découvre des cas limites qu'aucun test manuel n'aurait couverts.
Le retour sur investissement est mesurable. Sur un projet interne que j'ai repris, on est passés de « trois tickets par semaine sur des ambiguïtés de doc » à moins d'un par mois. Le temps investi dans la chaîne: deux jours de mise en place, une demi-journée par mois d'entretien.
Versionner et déprécier sans casser les clients
La question du versionnement arrive toujours trop tard. Un client est déjà en production, une équipe mobile a déjà livré, et quelqu'un demande si on peut ajouter un champ obligatoire. Non, on ne peut pas.
Ce qui marche pour moi: le versionnement dans l'URL (/v1/, /v2/) pour les changements cassants, rien pour les changements compatibles. Et surtout, une politique de dépréciation écrite, avec des headers HTTP normalisés:
| Header | Rôle | Exemple |
|---|---|---|
Deprecation | Signale que l'endpoint est obsolète | @1785715200 |
Sunset | Date à laquelle l'endpoint disparaît | Sat, 01 Aug 2026 00:00:00 GMT |
Link | Pointe vers la doc de migration | <https://docs.example.com/migrate-v2>; rel="deprecation" |
Ces trois headers ne coûtent rien à ajouter et changent la vie des intégrateurs. Ils transforment une suppression brutale en une échéance connue. Côté doc, ils apparaissent dans le rendu OpenAPI et dans le changelog généré.
Un exemple d'API REST de bout en bout
Prenons un cas simple: une ressource « transferts d'argent ». Sans entrer dans tout le code, voici comment je structurerais les endpoints et à quoi ressemblerait le contrat.
Voir la structure complète des endpoints de transferts
GET /v1/transfers— liste paginée, filtres?status,?created_after,?account_idPOST /v1/transfers— création, renvoie201avec l'objet complet et un headerLocationGET /v1/transfers/{id}— lecture unitaire,404si inexistantPOST /v1/transfers/{id}/cancellation— transition d'état, renvoie202si traitement asynchroneGET /v1/accounts/{id}/transfers— vue filtrée depuis la ressource parente
Deux choses à remarquer. Les URLs ne contiennent aucun verbe conjugué, sauf cancellation qui est un nom — c'est volontaire, c'est la ressource « annulation » qui est créée. Et 202 Accepted est utilisé partout où le traitement n'est pas synchrone, ce qui évite les timeouts silencieux côté client.
Comment savoir que la doc ne ment pas
La seule preuve qu'une documentation est vraie, c'est qu'un test la vérifie. Deux niveaux:
- Un test de contrat côté consommateur (Pact, ou plus simplement un client généré depuis la spec qui s'exécute contre l'API de staging).
- Un test de validation de réponse côté producteur: la réponse réelle est validée contre le schéma OpenAPI à chaque requête de test.
C'est le seul moyen que je connaisse pour qu'un endpoint documenté et un endpoint réel ne se désynchronisent pas. Le reste, c'est de la foi.
Ce qui reste à décider, c'est vous
Une API REST bien conçue, ça se voit à un détail: quand quelqu'un la découvre, il n'a pas besoin de lire trois pages de doc pour comprendre comment faire un premier appel. L'URL se devine, la réponse est prévisible, l'erreur est explicite.
Tout le reste (verbes HTTP, codes, conventions de nommage) n'est que la mécanique. La vraie question, celle qui décide si votre API sera utilisée ou contournée, c'est: est-ce qu'un développeur qui ne vous connaît pas peut réussir à intégrer votre service en une heure, sans vous appeler? Si la réponse est non, aucune convention de nommage ne vous sauvera.