Concevoir des API REST idempotentes et versionnées
Vous déployez une nouvelle fonctionnalité, et soudain votre API reçoit des requêtes POST en double de la part de clients qui réessaient après un timeout. Votre base de données contient maintenant deux commandes identiques. Ou vous déployez un changement incompatible, et les applications mobiles plantent car elles attendaient l'ancien format de réponse. Ce sont deux des problèmes les plus courants et douloureux dans la conception d'API : les opérations non idempotentes et les changements incompatibles. Ce guide vous montre comment résoudre les deux avec des patterns pratiques pour l'idempotence et le versioning dans les API REST.
Pourquoi l'idempotence est importante dans les API REST
L'idempotence signifie que faire la même requête plusieurs fois a le même effet que la faire une fois. Les méthodes HTTP ont des sémantiques d'idempotence définies : GET, HEAD, PUT, DELETE et OPTIONS sont idempotentes, tandis que POST et PATCH ne le sont pas. Mais les API du monde réel doivent souvent accepter POST pour des opérations comme la création de ressources ou le traitement de paiements. Les pannes réseau, les timeouts clients et la logique de retry peuvent causer des doublons. Sans idempotence, vous risquez des doubles débits, des enregistrements en double ou un état incohérent.
Techniques pour des API REST idempotentes
1. Utilisez des clés d'idempotence pour les requêtes POST
L'approche la plus courante consiste à laisser les clients envoyer une clé unique (par exemple, un UUID) dans un en-tête Idempotency-Key. Le serveur stocke la clé et le résultat de l'opération. Si la même clé est reçue à nouveau, le serveur renvoie le résultat stocké au lieu de réexécuter l'opération.
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 1000,
"currency": "USD"
}
Étapes de mise en œuvre :
- Le client génère une clé unique (UUID v4) pour chaque opération logique.
- Le serveur vérifie si la clé existe dans un stockage persistant (par exemple, Redis ou base de données).
- Si non, traite la requête et stocke la clé avec le statut et le corps de la réponse.
- Si oui, renvoie la réponse stockée sans retraiter.
Définissez une expiration raisonnable (par exemple, 24 heures) pour éviter une croissance illimitée du stockage.
2. Tirez parti des requêtes conditionnelles
Pour les mises à jour, utilisez les en-têtes ETag et If-Match pour éviter les mises à jour perdues. Le serveur renvoie un ETag représentant l'état actuel. Le client le renvoie avec If-Match ; si la ressource a changé, le serveur renvoie 412 Precondition Failed. Cela rend les requêtes PUT sûres contre les conditions de course.
PUT /articles/123
If-Match: "abc123"
Content-Type: application/json
{"title": "Titre mis à jour"}
3. Concevez PUT pour une idempotence naturelle
Préférez PUT à POST lorsque le client peut déterminer l'URL de la ressource. Par exemple, PUT /users/{userId} est naturellement idempotent car les appels répétés écrasent la même ressource. Utilisez POST uniquement lorsque le serveur attribue l'ID.
4. Gérez la détection des doublons avec des contraintes uniques
Combinez les clés d'idempotence avec des contraintes uniques de base de données sur les clés métier (par exemple, numéro de commande). Si un doublon passe, la base de données le rejette, et vous pouvez renvoyer un 409 Conflict avec un message clair.
Stratégies de versioning pour les API REST
Les API évoluent. De nouveaux champs, un comportement modifié ou des points de terminaison supprimés peuvent casser les clients existants. Le versioning vous permet d'introduire des changements sans les perturber. Il existe plusieurs stratégies courantes :
| Stratégie | Exemple | Avantages | Inconvénients |
|---|---|---|---|
| Chemin d'URI | /v1/users |
Explicite, facile à router, compatible avec le cache | Les URLs changent, peut encombrer |
| Paramètre de requête | /users?version=1 |
Simple, optionnel | Facile à omettre, pas RESTful |
| En-tête personnalisé | Accept-Version: v1 |
Garde les URLs propres | Plus difficile à tester dans le navigateur |
| Type de média | Accept: application/vnd.api.v1+json |
Négociation de contenu, REST pur | Complexe, moins courant |
Pour la plupart des équipes, le versioning par chemin d'URI est le choix le plus pragmatique. Il est visible, facile à documenter et fonctionne bien avec les passerelles API et les CDN.
Comment versionner sans casser les clients
- Commencez avec v1 dans le chemin dès le premier jour. Ajouter le versioning plus tard est plus difficile.
- Modifications additives uniquement dans une version : nouveaux champs optionnels, nouveaux points de terminaison. Ne supprimez jamais et ne renommez jamais les champs.
- Dépréciez avec élégance : annoncez la fin de vie, fournissez des guides de migration et surveillez l'utilisation des anciennes versions.
- Utilisez les en-têtes Sunset :
Sunset: Sat, 31 Dec 2025 23:59:59 GMTpour informer les clients. - Maintenez au maximum deux versions actives pour limiter la charge de maintenance.
Mise en pratique : un exemple concret
Considérez une API de paiement. Vous voulez créer un paiement de manière idempotente et versionner le point de terminaison.
POST /v1/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 1000,
"currency": "USD"
}
Logique serveur :
- Vérifier si
Idempotency-Keyexiste dans Redis. - Si oui, renvoyer la réponse mise en cache (statut + corps).
- Si non, traiter le paiement, stocker la clé avec la réponse, renvoyer 201 Created.
Pour les mises à jour, utilisez PUT avec ETag :
PUT /v1/payments/123
If-Match: "xyz789"
Content-Type: application/json
{"amount": 1500}
Si l'ETag ne correspond pas, renvoyez 412. Cela empêche d'écraser des modifications concurrentes.
Pièges courants et comment les éviter
- Stocker les clés d'idempotence pour toujours : définissez un TTL (par exemple, 24h) pour éviter le gonflement du stockage.
- Ignorer les conditions de course : utilisez des opérations atomiques (par exemple, Redis SETNX) pour vérifier et définir les clés.
- Versionner trop tard : ajoutez /v1 dès le début.
- Changements incompatibles dans les versions mineures : traitez tout changement qui modifie la structure de réponse comme majeur.
- Ne pas documenter l'idempotence : indiquez clairement quels points de terminaison prennent en charge Idempotency-Key.
FAQ
Qu'est-ce qu'une API REST idempotente ?
Une API REST idempotente garantit que faire la même requête plusieurs fois produit le même résultat que de la faire une fois. C'est crucial pour gérer les retries en toute sécurité, surtout pour les méthodes non idempotentes comme POST.
Quelles méthodes HTTP sont idempotentes ?
GET, HEAD, PUT, DELETE, OPTIONS et TRACE sont idempotentes. POST et PATCH ne le sont pas par défaut, mais vous pouvez les rendre idempotentes en utilisant des techniques comme les clés d'idempotence.
Quelle est la meilleure façon de versionner une API REST ?
Le versioning par chemin d'URI (par exemple, /v1/users) est l'approche la plus courante et pratique. Il est explicite, facile à router et fonctionne bien avec la mise en cache et les passerelles API. D'autres options incluent les paramètres de requête, les en-têtes personnalisés et les types de média, mais elles ont des compromis.
Prêt à tester les réponses JSON de votre API ? Utilisez notre JSON Formatter pour valider et embellir les payloads rapidement.