Une API REST, c'est du code qui sera consommé par d'autres développeurs, d'autres équipes, d'autres systèmes. Contrairement à une interface graphique que l'on peut ajuster au fil de l'eau, une API engage. Chaque endpoint publié est une promesse. Et chaque modification non rétrocompatible est une promesse rompue.
Pourtant, la majorité des API naissent dans l'urgence. On expose quelques routes, on documente à minima, on itère. Jusqu'au jour où la dette de conception rend toute évolution coûteuse. Cet article pose les principes qui font durer une API REST, au-delà du premier sprint.
Une API, c'est un contrat
Quand vous publiez un endpoint, vous signez un contrat avec tous les consommateurs qui vont l'intégrer. Applications mobiles, partenaires, front-ends internes, scripts d'automatisation… Chacun s'appuie sur la structure de vos réponses, vos codes HTTP, vos conventions de nommage.
Le coût d'un breaking change est rarement mesuré à l'avance. Il ne s'agit pas seulement de modifier votre code. Il faut coordonner la mise à jour de chaque consommateur, gérer la période de transition, maintenir l'ancienne version le temps que tout le monde migre. Sur une API publique, c'est un projet en soi. Sur une API interne avec dix consommateurs, c'est déjà une source de friction considérable.
C'est pourquoi la conception initiale mérite un investissement sérieux. Non pas pour tout prévoir, mais pour poser des fondations suffisamment solides pour absorber les évolutions sans casser l'existant. Les décisions prises dans les premières semaines — nommage, structure des ressources, gestion des erreurs — vous accompagneront pendant des années.
Conventions de nommage
Les conventions de nommage sont le premier signal de qualité d'une API. Un consommateur qui découvre vos endpoints doit pouvoir deviner la structure sans lire la documentation.
Ressources au pluriel
Les URLs désignent des ressources, pas des actions.
Utilisez le pluriel pour les collections :
/users, /orders, /products.
Un élément individuel est identifié par son ID :
/users/42. C'est simple, prévisible
et universel.
Kebab-case pour les URLs
Les URLs utilisent le kebab-case :
/order-items plutôt que /orderItems
ou /order_items. Les URLs sont
case-insensitive par convention, et le kebab-case
est le standard du web.
Hiérarchie logique
La structure des URLs reflète les relations
entre ressources. Un utilisateur possède des commandes :
/users/{id}/orders. Une commande contient
des lignes : /orders/{id}/items.
Limitez-vous à deux niveaux d'imbrication maximum.
Au-delà, l'URL devient illisible et le routage
pénible à maintenir.
Les verbes HTTP, pas dans l'URL
C'est le verbe HTTP qui exprime l'action,
pas l'URL. POST /users crée un utilisateur.
GET /users/42 le récupère.
DELETE /users/42 le supprime.
Bannissez les /createUser,
/deleteOrder et autres
/getProductList.
Versioning
Le versioning est le filet de sécurité qui permet de faire évoluer une API sans casser les consommateurs existants. Encore faut-il choisir la bonne stratégie.
URL path vs header
Deux approches dominent. Le versioning par
URL path (/v1/users)
est explicite, simple à comprendre et facile
à router. Le versioning par header
(Accept-Version: v2) garde des URLs
propres mais complexifie le routage et le debugging.
En pratique, le versioning par URL path est le plus répandu et le plus pragmatique. Réservez le versioning par header aux cas où la pureté des URLs est une contrainte forte, typiquement les API publiques à très large audience.
Quand versionner
Ne versionnez pas à chaque modification. Un nouveau champ dans une réponse n'est pas un breaking change. La suppression d'un champ, le changement de type d'un champ, la modification d'un code de retour : ça, c'est un breaking change.
La règle : tout ce qui peut faire échouer un consommateur existant sans modification de son code justifie une nouvelle version. Tout le reste est une évolution backward-compatible.
Pagination et filtrage
Toute collection de plus de quelques dizaines
d'éléments doit être paginée. Sans pagination,
un GET /users sur une base de
100 000 utilisateurs met votre serveur à genoux
et votre consommateur en timeout.
Cursor vs offset
La pagination par offset
(?page=3&limit=20) est intuitive
mais fragile. Si un élément est ajouté ou supprimé
entre deux pages, des résultats sont dupliqués
ou manquants. La pagination par cursor
(?after=eyJpZCI6NDJ9&limit=20)
résout ce problème en s'appuyant sur un marqueur stable.
Privilégiez le cursor pour les collections qui changent fréquemment. L'offset reste acceptable pour les collections stables ou les cas d'usage qui nécessitent un accès direct à une page donnée.
Filtrage et tri
Le filtrage passe par les query parameters :
/orders?status=pending&created_after=2024-01-01.
Le tri également : /orders?sort=created_at&order=desc.
Documentez les filtres disponibles. Un filtre
non documenté est un filtre qui n'existe pas
pour vos consommateurs.
Le total count est un sujet
sous-estimé. Compter le nombre total d'éléments
peut être extrêmement coûteux sur de grandes tables.
Rendez-le optionnel via un header ou un paramètre
(?include_total=true) et mettez
en cache le résultat quand c'est possible.
Gestion d'erreurs standardisée
La gestion d'erreurs est ce qui sépare une API agréable d'une API pénible. Quand quelque chose échoue, le consommateur a besoin de trois informations : quoi (quel type d'erreur), où (quel champ, quelle ressource) et comment (que faire pour corriger).
RFC 7807 Problem Details
La RFC 7807 (devenue RFC 9457)
définit un format standard pour les erreurs HTTP.
Un objet JSON avec les champs type,
title, status,
detail et instance.
C'est le format adopté par API Platform
et de nombreux frameworks. L'adopter, c'est
offrir à vos consommateurs une structure prévisible
pour parser et afficher les erreurs.
Codes HTTP cohérents
Utilisez les codes HTTP pour ce qu'ils signifient.
400 pour une requête malformée.
401 pour un problème d'authentification.
403 pour un problème d'autorisation.
404 pour une ressource inexistante.
409 pour un conflit.
422 pour une validation métier échouée.
429 pour un rate limit dépassé.
500 pour une erreur serveur.
Et surtout : jamais de réponse 200 avec un body d'erreur. C'est le piège le plus courant et le plus destructeur. Un consommateur qui filtre sur le code HTTP ne verra jamais l'erreur. Un monitoring basé sur les codes de retour sera aveugle. C'est une bombe à retardement.
Documentation comme produit
Une API sans documentation n'est pas une API. C'est une boîte noire que personne n'ose toucher. La documentation doit être traitée comme un produit à part entière, avec le même soin que le code.
OpenAPI et génération automatique
Le standard OpenAPI (ex-Swagger) permet de décrire votre API dans un format machine-readable. À partir de cette spécification, des outils génèrent la documentation interactive, les clients SDK, les mocks et les tests.
En PHP, API Platform génère la spécification OpenAPI automatiquement à partir de vos entités et ressources. C'est un gain de temps considérable et une garantie que la documentation reste synchronisée avec le code.
Exemples et sandbox
Une documentation sans exemples est une documentation incomplète. Chaque endpoint doit montrer un exemple de requête et de réponse, avec des données réalistes. Mieux encore : proposez un environnement sandbox où les consommateurs peuvent tester les appels sans impacter les données de production.
Sécurité
La sécurité d'une API ne se limite pas à l'authentification. C'est un ensemble de couches qui protègent vos données et vos systèmes contre les usages malveillants.
Authentification et autorisation
OAuth2 est le standard pour les API qui exposent des données utilisateur. Les API keys suffisent pour les intégrations machine-to-machine simples. Dans tous les cas, ne réinventez pas la roue. Utilisez des bibliothèques éprouvées et des tokens à durée de vie limitée.
Rate limiting
Le rate limiting protège votre API
contre les abus, qu'ils soient intentionnels
(attaque DDoS) ou accidentels (boucle infinie
chez un consommateur). Retournez un 429
avec un header Retry-After
pour indiquer quand le consommateur peut réessayer.
CORS et validation des entrées
Configurez le CORS strictement. N'autorisez que les origines légitimes. Validez toutes les entrées, y compris les query parameters et les headers. Une API qui fait confiance aux données entrantes est une API vulnérable.
Le HTTPS est non négociable. Toute API servie en HTTP est un vecteur d'interception. Redirigez systématiquement le trafic HTTP vers HTTPS et activez HSTS.
Performance
Une API lente est une API abandonnée. La performance se travaille à plusieurs niveaux, du cache HTTP jusqu'à la structure des réponses.
Cache HTTP
Les headers ETag et Last-Modified
permettent aux consommateurs de valider leur cache
sans retélécharger les données. Quand un
GET retourne un 304 Not Modified,
c'est du temps de transfert et du calcul serveur
économisés. Sur une API à fort trafic,
l'impact est significatif.
Compression et champs sparse
Activez la compression gzip
sur toutes les réponses JSON.
Proposez des champs sparse
(/users?fields=name,email)
pour que les consommateurs ne récupèrent
que les données dont ils ont besoin.
Moins de données transférées, moins de sérialisation,
moins de charge réseau.
Le piège du N+1
Les endpoints qui retournent des ressources
avec des relations imbriquées sont un terrain
fertile pour le problème N+1.
Un GET /orders?include=items,customer
qui exécute une requête par commande pour charger
les items va exploser en temps de réponse
dès que la collection grossit.
La solution : eager loading côté serveur, avec des jointures optimisées. Et surtout, mesurez. Un endpoint qui répond en 50 ms sur 10 résultats peut répondre en 5 secondes sur 100 si les includes ne sont pas optimisés.
Concevoir pour durer
Une API REST maintenable n'est pas une API sur-conçue. C'est une API dont les conventions sont claires, les erreurs exploitables, la documentation à jour et la sécurité non négociable. Ces principes ne demandent pas plus de temps de développement. Ils demandent plus de réflexion en amont, pour éviter des mois de correction en aval.
Chez x10, nous concevons des API REST pensées pour durer, avec API Platform, une documentation générée automatiquement et des conventions qui tiennent la charge. Besoin d'un accompagnement sur votre développement sur mesure ou votre architecture applicative ? Parlons-en.