Ressources · 33

Faire évoluer les contrats API sans perdre les clients

Décrire requêtes, réponses et erreurs, tester les versions et rendre les webhooks rejouables.

· 9 min

Banc fictif de vérification API sans personnes, avec passerelle réseau, diagramme abstrait de requêtes et réponses, classeur bordeaux et lampe en laiton

Ce que ce guide permet

  • Écrire le contrat
  • Rendre les erreurs claires
  • Tester la compatibilité
  • Préparer le retrait

Contrôle express

  • Quels clients consomment chaque opération ?
  • Les réponses d’erreur ont-elles un type stable et un statut cohérent ?
  • Un ancien client tolère-t-il le changement ?
  • Les retries créent-ils des doublons ?
  • Comment annoncer et vérifier un retrait ?

Méthode pas à pas

  1. 01

    Inventorier les consommateurs

    Lister opérations, clients, usages, versions et responsables. Distinguer usages observés et supposés avant de modifier un champ.

    Livrable : carte des dépendances du contrat.

  2. 02

    Décrire requêtes et réponses

    Tenir une description OpenAPI avec schémas, exemples réalistes, réponses de succès et d’échec. Vérifier qu’elle reflète le service exécuté.

    Livrable : contrat versionné et tests de conformité.

  3. 03

    Stabiliser les erreurs

    Utiliser les statuts HTTP selon leur sens et, lorsque pertinent, un type de problème documenté selon RFC 9457. Ne pas divulguer de secret dans les détails.

    Livrable : catalogue d’erreurs testées.

  4. 04

    Qualifier le changement

    Examiner champs obligatoires, types, valeurs, comportements, délais et pagination. Tester les anciens clients contre la nouvelle version sur des cas représentatifs.

    Livrable : matrice de compatibilité et décision de version.

  5. 05

    Fiabiliser les notifications

    Pour les webhooks, prévoir authentification, ordre non garanti, doublons et retries. Rendre le traitement idempotent quand une répétition est possible.

    Livrable : scénario de replay et accusé de réception.

  6. 06

    Déployer avec sortie claire

    Publier notes de migration, période de coexistence et contacts. Mesurer l’usage ancien, puis retirer une version seulement après vérification des consommateurs concernés.

    Livrable : plan de migration et preuves de retrait.

Indicateurs de pilotage

IndicateurCe qu’il mesurePremière action
ContratOpérations décrites et conformes au serviceCorriger les divergences
CompatibilitéClients représentatifs testés avant changementAjouter les cas oubliés
ErreursTypes documentés sans donnée sensibleRéviser messages et schémas
MigrationUsage des versions anciennes avec propriétaireAccompagner les clients restants

Erreurs fréquentes

  • Croire qu’un numéro de version suffit à préserver la compatibilité
  • Documenter seulement les réponses 200
  • Envoyer une trace interne ou un secret dans une erreur
  • Supposer qu’un webhook arrive une seule fois et dans l’ordre

Questions fréquentes

OpenAPI remplace-t-il un test ?

Non. La description doit être comparée aux réponses réelles et aux besoins des consommateurs.

Faut-il toujours créer une nouvelle version ?

Non. La décision dépend de l’effet sur les clients et du comportement du contrat ; les changements incompatibles exigent un plan explicite.

Pourquoi des erreurs typées ?

Elles donnent aux clients une façon stable de distinguer les problèmes et de choisir une action.

ETag, cache HTTP et modifications concurrentes

Deux clients, trois versions : rendre un conflit visible

Dans ce scénario fictif, A et B travaillent sur la même représentation. Le serveur émet un ETag fort pour chaque nouvelle version enregistrée. If-Match permet de refuser une modification fondée sur une version qui n’est plus courante. Les cinq étapes sont des extraits explicatifs, sans connexion ni écriture réelle.

1. A lit la version initiale

A reçoit la représentation v1 et conserve "v1" avec les données qu’il modifie. Un tag copié sans sa représentation ne suffit pas à comprendre le conflit.

Extrait de requête

GET /demo/resource HTTP/1.1
Host: api.example.test

Extrait de réponse

HTTP/1.1 200 OK
Date: Thu, 22 Oct 2026 00:00:00 GMT
Cache-Control: no-store
ETag: "v1"
2. B enregistre avant A

B a lui aussi lu v1. Son PUT satisfait If-Match: "v1". La nouvelle représentation exacte est enregistrée sous "v2" ; le serveur répond ici 204 et fournit ce nouveau validateur.

Extrait de requête

PUT /demo/resource HTTP/1.1
Host: api.example.test
If-Match: "v1"

Extrait de réponse

HTTP/1.1 204 No Content
Date: Thu, 22 Oct 2026 00:00:00 GMT
Cache-Control: no-store
ETag: "v2"
3. A tente une écriture périmée

La version courante est maintenant v2. Le PUT de A porte encore If-Match: "v1" : 412, sans appliquer sa modification. Préserver son travail local pour permettre une comparaison.

Extrait de requête

PUT /demo/resource HTTP/1.1
Host: api.example.test
If-Match: "v1"

Extrait de réponse

HTTP/1.1 412 Precondition Failed
Date: Thu, 22 Oct 2026 00:00:00 GMT
Cache-Control: no-store
4. A relit et arbitre le conflit

A récupère v2 et compare la version lue, sa modification locale et la version courante. Une décision humaine ou une règle métier explicite choisit les changements à conserver.

Extrait de requête

GET /demo/resource HTTP/1.1
Host: api.example.test

Extrait de réponse

HTTP/1.1 200 OK
Date: Thu, 22 Oct 2026 00:00:00 GMT
Cache-Control: no-store
ETag: "v2"
5. A soumet une modification réconciliée

Après cette décision, A envoie la nouvelle proposition avec If-Match: "v2". Elle est acceptée dans ce scénario et crée v3. Une autre écriture concurrente pourrait encore imposer un nouveau cycle.

Extrait de requête

PUT /demo/resource HTTP/1.1
Host: api.example.test
If-Match: "v2"

Extrait de réponse

HTTP/1.1 204 No Content
Date: Thu, 22 Oct 2026 00:00:00 GMT
Cache-Control: no-store
ETag: "v3"

Extraits HTTP/1.1 fictifs : les corps de requête et de réponse sont omis, même lorsqu’ils seraient possibles. HEAD, 204 et 304 restent sans contenu de réponse. Le domaine api.example.test et la date fixe servent uniquement à la lecture du scénario.

Le protocole détecte une version périmée ; il ne décide pas comment fusionner les contenus. Documenter ce que l’interface conserve en cas de 412, qui arbitre le conflit et quand une nouvelle lecture est nécessaire. Le joker If-Match: * ne remplace pas la comparaison d’un ETag fort précis.

Explorer les comparaisons et le simulateur ETag →

Références primaires et limites

Les normes décrivent les conditions HTTP et le cache. Les dix cas, le laboratoire et le parcours à deux clients sont des illustrations originales. Les résultats affichés sont calculés par ce modèle local, pas des mesures du site ni de votre infrastructure.

Références relues le

Retry-After : attente, budget et décision de reprise

Documenter le droit de répéter avant le délai de reprise

Retry-After explique une attente. Le contrat doit aussi définir les opérations rejouables, les clés, les versions et les états de résultat inconnu.

  • Préciser si le champ contient une durée ou une date et comment le client traite les valeurs absentes ou invalides.
  • Définir le pilote de reprise et le budget global pour éviter les tentatives superposées.
  • Conserver les paramètres, le contexte et la fenêtre d’idempotence sans généraliser un contrat Stripe à toutes les API.

Explorer Retry-After et le modèle de reprise →

Références relues le · RFC 9110 · §9.2.2, §10.2.3 · RFC 6585 · §4 · AWS

OAuth/OIDC : audience et droits

Le contrat API doit nommer son audience et ses droits

Associez la route à une ressource attendue, à des scopes explicites et à une règle sur l’objet. La validation de signature ne suffit pas à établir ce contrat.

  • Documenter les profils acceptés et distinguer access token, ID token et jeton opaque.
  • Tester un jeton destiné à une autre API, un scope absent et un objet d’un autre locataire.
  • Renvoyer un refus maîtrisé et conserver une trace expurgée de tout code ou jeton.

Voir les destinataires et les dix scénarios OAuth/OIDC →

Sources vérifiées le · IETF · RFC 9700 · OAuth 2.0 Security BCP · IETF · RFC 9068 · JWT access token profile · OpenID Foundation · OpenID Connect Core 1.0 · IETF · RFC 8725 · JWT Best Current Practices · IETF · RFC 6749 · OAuth 2.0 framework

Webhooks : signatures et rejeu

Le contrat doit définir exactement les octets authentifiés

Stripe et GitHub n’utilisent pas le même message HMAC. Documentez l’en-tête, le corps brut, les clés de confiance et la politique de fraîcheur sans généraliser un profil fournisseur.

  • Tester un corps reformaté avec la signature originale.
  • Séparer validation cryptographique, fraîcheur et reconnaissance des répétitions.
  • Documenter acquittement durable et statut métier avec des preuves distinctes.

Voir les signatures, les empreintes et les dix cas webhooks →

Sources vérifiées le · Stripe · Webhooks · Stripe · Manage webhook endpoints · GitHub · Validating webhook deliveries · GitHub · Best practices for using webhooks · GitHub · Handling failed webhook deliveries

Erreurs API : types, statuts et preuves

Une erreur API utile relie un type stable, une occurrence et une action possible pour le client. La RFC 9457, publiée en juillet 2023 à la place de la RFC 7807, fournit un format commun. Elle ne transforme pas un message d’erreur en preuve d’autorisation, en consigne de rejeu ou en diagnostic de sécurité complet.

Cinq membres standard expliqués, six types de problèmes présents dans la copie IANA du 26 juin 2026 et huit cas fictifs de recette. Sources consultées le 23 octobre 2026. Les exemples et propositions de tests sont rédigés par CIE.

IETF · M. Nottingham, E. Wilde, S. Dalal · RFC 9457 · July 2023 · obsoletes RFC 7807 · §3.1, §3.2, §4.2, §5

Les cinq membres : rôle et lecture côté client

Les cinq membres : rôle et lecture côté client
MembreType attenduRôleQuestion de recette
typestring · URI referenceIdentifie le type de problème ; about:blank par défaut si absent.Le client décide-t-il sur l’URI plutôt que sur un titre traduit ?
statusnumber · HTTP statusRappelle le statut de cette occurrence ; valeur consultative.Le corps et le statut de la réponse concordent-ils ?
titlestringRésumé lisible du type de problème.La traduction change-t-elle seulement le texte destiné à la personne ?
detailstringExplication lisible de cette occurrence.Le client évite-t-il d’analyser la prose comme un code machine ?
instancestring · URI referenceIdentifie une occurrence particulière.La référence permet-elle un suivi sans exposer des détails internes ?

Ces membres sont optionnels. Un membre d’un type JSON inattendu doit être ignoré par le consommateur. Une extension inconnue doit aussi pouvoir être ignorée. Le type de média JSON prévu est application/problem+json.

Atelier de lecture

Choisissez l’un des huit exemples puis modifiez, si utile, le statut HTTP supposé reçu. Le corps et le type de média restent ceux du cas choisi. L’atelier compare des faits limités ; il ne valide ni toutes les URI, ni le schéma métier, ni l’autorisation, ni le service réel. Le nom du type de média est comparé sans valider tous ses paramètres. La méthode HTTP et les autres règles de transport restent à vérifier.

Cet atelier utilise uniquement les exemples fictifs affichés ci-dessous. Les URI sont montrées comme texte : aucune n’est visitée ou résolue automatiquement. Aucun journal ou message de production n’est demandé.

Six types enregistrés : conserver les URI et les références exactes

Ce registre de types communs n’est pas une liste de tous les problèmes qu’une API peut définir. N/A pour about:blank n’est pas un statut HTTP. Les trois entrées digest conservent ici une référence de type draft telle qu’elle figure dans la copie IANA ; cette mention n’est pas transformée en numéro de RFC finale.

IANA · HTTP Problem Types · Registry updated 2026-06-26 · consulted 2026-10-23

Six types enregistrés : conserver les URI et les références exactes
URI de type et titre IANAStatut recommandéRéférence telle que publiéeDate d’enregistrement
about:blank

See HTTP Status Code

N/A

rfc · rfc9457

2023-05-02
https://iana.org/assignments/http-problem-types#date

Date Not Acceptable

400

rfc · rfc9458 · §6.5.2

2023-05-02
https://iana.org/assignments/http-problem-types#ohttp-key

Oblivious HTTP key configuration not acceptable

400

rfc · rfc9458 · §5.3

2023-05-02
https://iana.org/assignments/http-problem-types#digest-unsupported-algorithms

Unsupported Hashing Algorithms

400

draft · RFC-ietf-httpapi-digest-fields-problem-types-06 · §3.1

2026-06-26
https://iana.org/assignments/http-problem-types#digest-invalid-values

Invalid Digest Values

400

draft · RFC-ietf-httpapi-digest-fields-problem-types-06 · §3.2

2026-06-26
https://iana.org/assignments/http-problem-types#digest-mismatched-values

Mismatched Digest Values

400

draft · RFC-ietf-httpapi-digest-fields-problem-types-06 · §3.3

2026-06-26

IANA · Protocol Registries licensing · CC0 1.0 · Revised 2021-11-10 · CIE commentary and fictional examples identified separately

Les huit cas et les décisions de recette

P01 · Un type métier explicite

HTTP 422 · Content-Type: application/problem+json

{
    "type": "https://api.example.net/problems/invalid-window",
    "title": "Invalid time window",
    "status": 422,
    "detail": "The end must follow the start.",
    "instance": "/problems/events/test-17",
    "errors": [
        {
            "field": "end",
            "code": "before-start"
        }
    ]
}

Lecture : Le statut du corps et celui de la réponse concordent. Le tableau errors est une extension propre à cet exemple, pas un membre standard universel.

Test à ajouter : Tester le code stable before-start et l’association au champ ; ne pas analyser le texte detail pour automatiser la correction.

P02 · Type et status absents

HTTP 404 · Content-Type: application/problem+json

{
    "title": "Ressource introuvable",
    "instance": "/support/test-18"
}

Lecture : Les cinq membres ne forment pas une liste de champs tous obligatoires. Sans type exploitable, about:blank s’applique ; le statut HTTP reçu reste 404.

Test à ajouter : Vérifier que le client accepte cette réponse minimale et un titre localisé, sans inventer un statut stocké dans le corps.

P03 · Le corps annonce 403 mais HTTP indique 200

HTTP 200 · Content-Type: application/problem+json

{
    "type": "https://api.example.net/problems/access-denied",
    "title": "Access denied",
    "status": 403
}

Lecture : Il existe un écart entre deux couches. Le statut conseillé dans le JSON ne remplace pas celui reçu par les clients HTTP génériques.

Test à ajouter : Examiner la génération et les intermédiaires ; conserver les deux valeurs plutôt que déclarer la requête réussie à partir du seul HTTP 200.

P04 · Cinq membres du mauvais type JSON

HTTP 422 · Content-Type: application/problem+json

{
    "type": 17,
    "title": false,
    "status": "422",
    "detail": [
        "Invalid"
    ],
    "instance": null,
    "traceRef": "test-19"
}

Lecture : type, title, status, detail et instance sont de types inattendus. La règle de consommation de la RFC est de les ignorer, sans convertir automatiquement la chaîne "422" en nombre.

Test à ajouter : Signaler le défaut du producteur et tester un client tolérant. traceRef reste une extension ; about:blank est le type par défaut après ignorance de type.

P05 · URI relative et extensions inconnues

HTTP 409 · Content-Type: application/problem+json

{
    "type": "/problems/conflict",
    "title": "Version conflict",
    "status": 409,
    "instance": "?case=test-20",
    "requestRef": "test-20",
    "retryable": false
}

Lecture : Le type commence par un chemin relatif et instance par une référence de requête. L’atelier les affiche sans les résoudre ; la base du document réel serait nécessaire.

Test à ajouter : Définir la base dans le contrat, préférer un type absolu et tester l’ignorance des extensions non reconnues. Le seul mot retryable ne donne pas un droit universel de rejeu.

P06 · Une URI enregistrée à comparer exactement

HTTP 400 · Content-Type: application/problem+json

{
    "type": "https://iana.org/assignments/http-problem-types#date",
    "title": "Date Not Acceptable",
    "status": 400,
    "instance": "/support/test-21"
}

Lecture : Ce type figure dans la copie IANA. Son statut recommandé est 400. Une comparaison sur le seul suffixe #date ou sur title ne suffit pas à identifier le type.

Test à ajouter : Renvoyer le client vers la définition de ce type dans le contexte applicable ; l’enregistrement ne certifie pas l’implémentation.

P07 · Un objet ressemblant, un autre type de média

HTTP 503 · Content-Type: application/json

{
    "type": "https://api.example.net/problems/maintenance",
    "title": "Service maintenance",
    "status": 503,
    "detail": "Try the documented recovery path."
}

Lecture : Le corps ressemble à un Problem Details, mais le nom du type de média reçu est application/json. Une lecture structurelle ne vérifie pas la négociation ou le contrat de réponse.

Test à ajouter : Comparer Content-Type, documentation et client. Ne pas transformer cet exemple en règle de relance : Retry-After et sécurité du rejeu restent des contrôles séparés.

P08 · Un nombre ne suffit pas à faire un statut HTTP

HTTP 400 · Content-Type: application/problem+json

{
    "type": "about:blank",
    "title": "Bad Request",
    "status": 700,
    "instance": "/support/test-22"
}

Lecture : Le membre status est bien un nombre JSON, mais 700 est hors de la plage des codes HTTP à trois chiffres de 100 à 599. Ce défaut est distinct du mauvais type JSON.

Test à ajouter : Réparer la génération et conserver le statut HTTP reçu, ici 400. Un test de type de données seul aurait manqué cette anomalie.

IETF · RFC 9110 · HTTP Semantics · June 2022 · §6.4.1, §15, §15.3.6 · status range and response content

Faire évoluer une erreur sans casser le contrat

Faire évoluer une erreur sans casser le contrat
Changement proposéRisque concret pour le clientEssai CIE recommandé
Traduire title ou detailUn client qui analyse la phrase peut prendre une autre branche.Garder le même type et rejouer deux langues avec le même résultat machine attendu.
Changer l’URI typeUne nouvelle identité de problème peut remplacer l’ancienne.Tester les consommateurs existants et documenter migration, tolérance et repli.
Ajouter une extensionUn client trop strict peut rejeter une réponse auparavant lue.Ajouter un membre inconnu au test du consommateur et vérifier l’ignorance prévue.
Retirer ou renommer une extension métierUne action cliente peut dépendre du champ documenté.Vérifier le contrat propre au type, les usages observés et le plan de compatibilité.
Modifier le statut HTTPProxy, cache ou SDK peuvent traiter autrement la réponse.Comparer réponse brute et corps, avec le chemin de génération et les intermédiaires.
Ajouter du détail techniqueChemins internes, secrets ou données de compte peuvent être exposés.Tester le message public, la référence d’occurrence et les journaux à accès contrôlé séparément.

Conservez un exemple de réponse brute par cas de refus, sa définition de type, le résultat du test du client et la version du contrat. Une cohérence entre deux nombres ne démontre pas que le serveur a choisi le bon refus ni qu’un nouvel essai est sans effet de bord.

Provenance et limites de cette copie

IANA · 6 types · registry 2026-06-26 · reviewed 2026-10-23 · CC0 1.0

IANA XML SHA-256: 2b6a7ae5a63a5f6afccd8884658393d5268d16b63d624e883fd5fe5dcf5aa57b

RFC 9457 TXT SHA-256: f2b3db92fb0bf3489cb3841a0da0c0d88dff40797b64d40b6123085183886c7b

GraphQL : erreurs, listes et données partielles

Contrat GraphQL : l’erreur n’efface pas toujours les données

Définir ce que le client peut conserver quand un résolveur échoue : le contrat comprend la forme de data, la portée de null et le chemin d’erreur.

  • Comparer data absente, data partielle et data: null.
  • Conserver le chemin original, même si un parent devient null.
  • Tester les droits, les quotas et les états d’interface séparément.

Explorer les réponses GraphQL et leur nullabilité →

GraphQL Specification · September 2025 · Published edition · September 2025

API : dépréciation et retrait

Préparer le retrait au-delà du numéro de version

Une version publiée ne dit pas quand les consommateurs doivent migrer. Relier les annonces de dépréciation, les dates de retrait et les tests des clients existants.

  • Distinguer Deprecation, Sunset et les liens vers les politiques.
  • Vérifier que Sunset n’est pas antérieur à Deprecation lorsque les deux dates sont fournies.
  • Mesurer les consommateurs restants et valider le remplacement avant une bascule.

Comparer les annonces de dépréciation et de retrait →

IETF · RFC 9745 · Deprecation · Standards Track · March 2025 · §2–5 · consulted 2026-10-23

IETF · RFC 8594 · Sunset · Informational · May 2019 · §3–5, §8 · consulted 2026-10-23

Références officielles

Date de consultation des références : . La méthode et la fiche de travail proposent des contrôles à adapter à votre contexte ; elles ne constituent pas une certification.