Codes d'erreur API et meilleures pratiques
1) Pourquoi normaliser les erreurs
Prévisibilité pour les clients : format unique et comportement des retraits.
Accélération du débogue : 'trace _ id '/' request _ id', stable 'error _ code'.
Sécurité : Ne fuyez pas SQL/stack traces/configs.
Observabilité : rapports sur la taxonomie des erreurs (validation, quotas, délais, etc.).
2) Principes de base
1. Un format de réponse unique pour tous les 4xx/5xx (et pour les 2xx avec des erreurs partielles, un schéma séparé).
2. Une sémantique HTTP claire : le statut fidèle est le plus important.
3. Deux niveaux de code : Transport ('status') et domaine stable 'error _ code'.
4. Retriable vs Non-retriable : indiquez explicitement et donnez-nous un conseil sur le back-off.
5. Sécurité par défaut : détails - seulement au client avec des droits ; pas de sentiers intérieurs.
6. Localisation : le code machine reste stable, le texte est traduit.
3) Format d'erreur unique (basé sur RFC 7807)
Recommandé par JSON (extension « application/problème + json ») :json
{
"type": "https://api. example. com/errors/validation_failed",
"title": "Validation failed",
"status": 422,
"error_code": "VAL_001",
"detail": "Field 'email' must be a valid address",
"instance": "req_01HZY...93",
"trace_id": "a1b2c3d4e5f6",
"retriable": false,
"errors": [
{"field": "email", "code": "email_invalid", "message": "Invalid email"}
],
"hint": "Fix payload and retry",
"meta": {"docs": "https://docs. example. com/errors#VAL_001"}
}
Obligatoire : 'type', 'titre', 'status', 'error _ code', 'trace _ id'.
En option : 'errors []' (par champs), 'retriable', 'hint', 'meta'.
- `Content-Type: application/problem+json`
- `X-Request-ID`/`Traceparent` (W3C)
- (pour 429/503) 'Retry-After' (secondes ou date)
4) Sémantique des statuts HTTP (fusion des « classiques » et des pratiques)
2xx (succès avec nuances)
200 OK est un succès ordinaire.
201 Created - une ressource (Location) a été créée.
202 Accepté - asynchrone dans la file d'attente (donnez 'status _ url').
207 Multi-Status est un succès partiel (éviter si possible).
4xx (erreur client)
400 Bad Request est syntaxe/format, mais pas validation des champs (mieux que 422).
401 Unauthorized - non/jeton incorrect. Allons à WWW-Authenticate.
403 Forbidden est un jeton validé, mais il manque des droits (RBAC/ABAC/limites).
404 Not Found - ressource/endpoint manquant.
409 Conflict est un conflit de version/état (locking optimiste, idempotency).
410 Gone - endpoint définitivement enlevé.
412 Precondition Failed - ETag/If-Match n'est pas passé.
415 Unsupported Media Type est un 'Content-Type'incorrect.
422 Entity Unprocessable - Validation des règles commerciales.
429 Too Many Requests - dépassements de quotas/vitesse (voir § 7).
5xx (erreur de serveur)
500 Internal Server Error - erreur soudaine ; ne pas divulguer les détails.
502 Bad Gateway est une erreur d'apstream.
503 Service Unavailable - dégradation/surchauffe, donner « Retry-After ».
504 Gateway Timeout est un backend temporel.
5) Taxonomie des domaines 'error _ cod'
Nous recommandons les gammes :- « AUTH _ » - authentification/autorisation.
- « VAL _ » est la validation des données d'entrée.
- « RATELIMIT _ » : quotas et vitesse.
- 'ID...._ 'est une idempotence/duplicata.
- 'CONFLICT _ '- version/état.
- « BOU _ » - dépendances (PSP/DNS/SMTP).
- 'PAY _ 'est une erreur d'entreprise du domaine de paiement.
- 'SEC _' est la sécurité (signatures, HMAC, mTLS).
- « INT _ » est une soudaine interne.
- Stabilité dans le temps (back-compat).
- Descriptions et exemples dans le répertoire d'erreurs (docs + machine-readable JSON).
6) Retriable vs Non-retriable
Champs :- `retriable: true|false`
- Si « vrai » est nécessairement « Retry-After » (en secondes) ou un contrat « exponentiel (à partir de 1-2 s, max 30-60 s) ».
Retriable habituellement : '502/503/504', certains '500', '429' (après la fenêtre).
Non-retriable: `400/401/403/404/409/410/415/422`.
7) Rate limit & quota erreurs (429)
Corps :json
{
"type": "https://api. example. com/errors/rate_limited",
"title": "Rate limit exceeded",
"status": 429,
"error_code": "RATELIMIT_RPS",
"detail": "Too many requests",
"retriable": true
}
Titres :
- `Retry-After: 12`
- `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
- Для квот: `X-Quota-Limit`, `X-Quota-Remaining`, `X-Quota-Reset`
8) Idempotentialité et conflits
Dans les demandes d'enregistrement - 'Idempotency-Key' (unique dans les 24-72 h).
Conflit de réopération → 409 Conflict avec 'error _ code : "IDEMP_REPLAY"'.
Conflit de version de la ressource sur ETag → 412 Precondition Failed.
Dans la réponse, joignez 'resource _ id'/' status _ url' pour une nouvelle requête sécurisée.
9) Validation et 422
Retournez la liste des erreurs par champ :json
{
"status": 422,
"error_code": "VAL_001",
"errors": [
{"field":"email","code":"email_invalid","message":"Invalid email"},
{"field":"age","code":"min","message":"Must be >= 18"}
]
}
Règles :
- Ne dupliquez pas la même chose entre 400 et 422 pour la validation d'entreprise.
- Les messages sont des messages humains ; 'code' est une machine.
10) Sécurité des erreurs
Jamais : stack traces, SQL, chemins de fichiers, noms d'hôtes privés.
Éditer le PII ; Suivez le RGPD/DSAR.
Pour la signature/NMAS : distinguez 'SEC _ SIGNATURE _ MISMATCH' (403) et 'SEC _ TIMESTAMP _ SKEW' (401/403) avec l'indication « vérifier le temps ± 5 min ».
11) Corrélation et observabilité
Ajoutez toujours 'trace _ id '/' X-Request-ID' et allez dans les logs/pistes.
Agrégez les erreurs par 'error _ code' et 'status' → « top bug », « new vs known ».
Alert : sursaut 5xx/422/429, latence p95, partager des erreurs.
12) gRPC/GraphQL/Webhooks - mappings
gRPC ↔ HTTP
GraphQL
Transport 200, mais 'errors []' à l'intérieur - ajouter 'extensions. code` и `trace_id`.
Pour « fatal » (authentification/quotas), c'est mieux que le vrai HTTP 401/403/429.
Webhooks
Ne pensez qu'à 2xx du destinataire.
Retrai avec le back-off exponentiel, « X-Webhook-ID », « X-Signature ».
410 du destinataire - arrêter les retraits (endpoint supprimé).
13) Versioning des erreurs
'type '/' error _ code' est stable ; le nouveau n'est qu'à ajouter.
Si vous modifiez le schéma corporel, augmentez la version mineure de l'API ou 'problème + json ; v=2`.
Documentation : tableau des codes + exemples ; les erreurs changelog.
14) Documentation (fragments OpenAPI)
Réponses globales
yaml components:
responses:
Problem:
description: Problem Details content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
schemas:
Problem:
type: object required: [type, title, status, error_code, trace_id]
properties:
type: { type: string, format: uri }
title: { type: string }
status: { type: integer }
error_code: { type: string }
detail: { type: string }
instance: { type: string }
trace_id: { type: string }
retriable: { type: boolean }
errors:
type: array items:
type: object properties:
field: { type: string }
code: { type: string }
message: { type: string }
Exemple d'endpoint
yaml paths:
/v1/users:
post:
responses:
'201': { description: Created }
'401': { $ref: '#/components/responses/Problem' }
'422': { $ref: '#/components/responses/Problem' }
'429': { $ref: '#/components/responses/Problem' }
'500': { $ref: '#/components/responses/Problem' }
15) Test et qualité
Tests contractuels : correspondance 'application/problème + json', champs obligatoires.
Essais negatifs : toutes les branches 401/403/404/ 409/422/429/500.
Chaos/latency : contrôle des retraits sur 5xx/ 503/504/429 (« Retry-After »).
Tests de sécurité : pas de messages internes, masque PII correct.
Backward-compat : les anciens clients comprennent les nouveaux champs (ajoutez, ne cassez pas).
16) Chèque de mise en œuvre
- Un seul 'problème + json' + stable 'error _ code'.
- Sémantique HTTP/gRPC/GraphQL correcte.
- Retriable/non-retriable + 'Retry-After '/recommandations back-off.
- Titres de rate-limit et 429 comportements.
- Idempotentialité ('Idempotency-Key', 409/412).
- Sécurité : pas de traces/secrets, édition PII.
- 'trace _ id '/' X-Request-ID' dans toutes les erreurs.
- Documentation du catalogue d'erreurs et exemples.
- Surveillance par taxonomie des erreurs.
- Autotests de scénarios négatifs.
17) Mini-FAQ
En quoi 400 est-il différent de 422 ?
400 est une requête cassée (syntaxe/type de contenu). 422 est validé par la syntaxe, mais les règles commerciales n'ont pas passé.
Quand 401 et quand 403 ?
401 - non/jeton incorrect ; 403 - Le token est là, il n'y a pas assez de droits.
Ai-je toujours besoin de Retry-After ?
Pour 429/503 - oui ; pour les autres, il est préférable de faire une recommandation explicite.
Résultat
Les erreurs bien conçues sont un contrat : un statut HTTP correct, un seul 'problème + json', un 'error _ code' stable, des indices explicites sur les retraits et une sécurité stricte. Normalisez le format, documentez la taxonomie, ajoutez la télémétrie et les tests - et votre API deviendra prévisible, sécurisée et conviviale pour les intégrateurs.