Concevoir une API REST qui vieillit bien
Versioning, gestion d'erreurs, pagination... Les conventions qui font la différence entre une API maintenable et un cauchemar technique.
Une API mal conçue, c'est de la dette technique qui s'accumule à chaque nouveau client. Voici les principes que j'applique pour créer des APIs qui restent maintenables après des années d'évolution.
Nommer les endpoints : la clarté avant tout
Les ressources, pas les actions
❌ GET /getUsers
❌ POST /createUser
❌ PUT /updateUser/123
✅ GET /users
✅ POST /users
✅ PUT /users/123
L'URL décrit la ressource, le verbe HTTP décrit l'action.
Pluriel et cohérence
Choisissez une convention et tenez-vous-y :
✅ /users, /products, /orders
❌ /user, /products, /order (incohérent)
Relations imbriquées avec modération
✅ GET /users/123/orders (les commandes d'un utilisateur)
❌ GET /users/123/orders/456/items/789/details (trop profond)
Au-delà de 2 niveaux, préférez des endpoints séparés avec des filtres.
Gestion des erreurs : être explicite
Codes HTTP appropriés
| Code | Usage | |------|-------| | 200 | Succès | | 201 | Ressource créée | | 400 | Erreur de validation | | 401 | Non authentifié | | 403 | Non autorisé | | 404 | Ressource non trouvée | | 500 | Erreur serveur |
Format d'erreur standardisé
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Les données fournies sont invalides",
"details": [
{
"field": "email",
"message": "Format d'email invalide"
}
]
}
}
Toujours le même format, toujours le même niveau de détail. Le client sait à quoi s'attendre.
Pagination : ne jamais retourner tout
Le pattern offset/limit classique
GET /users?offset=0&limit=20
Simple, mais problématique sur les grandes tables (performance dégradée au fur et à mesure).
Le cursor-based pour les gros volumes
GET /users?cursor=abc123&limit=20
Le curseur encode la position dans la liste. Plus performant, mais plus complexe à implémenter.
La réponse paginée
{
"data": [...],
"pagination": {
"total": 1234,
"limit": 20,
"offset": 0,
"hasMore": true
}
}
Versioning : anticiper le changement
Dans l'URL (recommandé)
GET /v1/users
GET /v2/users
Clair, explicite, facile à router.
Dans les headers
GET /users
Accept: application/vnd.api+json; version=2
Plus "RESTful" en théorie, mais moins pratique au quotidien.
Règle d'or
- Les ajouts de champs sont rétrocompatibles
- Les suppressions/modifications de champs nécessitent une nouvelle version
- Maintenir maximum 2 versions en parallèle
Authentification : JWT avec prudence
Le flow classique
- Le client POST ses credentials sur
/auth/login - L'API retourne un JWT
- Le client envoie le JWT dans le header
Authorization: Bearer xxx
Les pièges à éviter
❌ Stocker des données sensibles dans le JWT (visible côté client)
❌ Tokens qui n'expirent jamais
❌ Pas de mécanisme de révocation
✅ Payload minimal (user_id, rôles)
✅ Expiration courte (15min - 1h) + refresh token
✅ Blacklist des tokens révoqués
Documentation : l'investissement qui paie
OpenAPI (Swagger) minimum
paths:
/users:
get:
summary: Liste des utilisateurs
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Liste paginée des utilisateurs
La doc générée automatiquement est toujours à jour.
Exemples concrets
Pour chaque endpoint, fournir :
- Un exemple de requête
- Un exemple de réponse succès
- Un exemple de réponse erreur
Ce que j'aurais aimé savoir plus tôt
- Commencer simple — n'ajoutez pas de fonctionnalités "au cas où"
- Valider les entrées agressivement — une API trop permissive est un cauchemar à maintenir
- Logger tout — vous en aurez besoin pour débugger en prod
- Tester avec des clients réels — les tests unitaires ne suffisent pas
Une bonne API est invisible. Le client l'utilise sans se poser de questions, et vous la maintenez sans avoir peur de tout casser.
