Intégration sécurisée d'API tierces pour garder votre appli opérationnelle pendant les pannes. Apprenez les timeouts, les réessais, les circuit breakers et les vérifications rapides.

Une API tierce peut échouer de manières qui ne ressemblent pas à un arrêt net. Le problème le plus courant, c'est la lenteur : les requêtes restent en attente, les réponses arrivent en retard et votre appli attend. Si ces appels sont dans le chemin critique, un petit incident externe s'accumule à l'intérieur de votre système.
C'est ainsi qu'un ralentissement local devient une panne complète. Les threads ou workers restent bloqués en attente, les files d'attente gonflent, les transactions de base de données restent ouvertes plus longtemps et de nouvelles requêtes commencent à expirer. Rapidement, même des pages qui n'utilisent pas l'API externe semblent cassées parce que le système est surchargé par du travail en attente.
L'impact est concret. Un fournisseur d'identité instable bloque les inscriptions et les connexions. Un timeout sur une passerelle de paiement fige le paiement et laisse les utilisateurs incertains quant à la facturation. Un retard de messagerie bloque les réinitialisations de mot de passe et les confirmations de commande, ce qui déclenche une deuxième vague de réessais et de tickets support.
L'objectif est simple : isoler les échecs externes pour que les workflows centraux continuent de tourner. Cela peut vouloir dire laisser un utilisateur passer une commande et confirmer le paiement plus tard, ou accepter une inscription même si l'email de bienvenue échoue.
Un bon indicateur de réussite : quand un fournisseur est lent ou en panne, votre appli doit quand même répondre rapidement et clairement, et la portée du problème doit rester limitée. Par exemple, la plupart des requêtes principales terminent toujours dans votre budget de latence normal, les échecs restent confinés aux fonctionnalités qui dépendent réellement de cette API, les utilisateurs voient un statut clair (en file, en attente, réessayer plus tard) et la récupération se fait automatiquement quand le fournisseur revient.
La plupart des échecs sont prévisibles, même si leur timing ne l'est pas. Nommez-les à l'avance et vous pouvez décider quoi réessayer, quoi arrêter et quoi afficher à l'utilisateur.
Les catégories courantes :
Tous les erreurs ne se valent pas. Les problèmes transitoires valent souvent la peine d'être réessayés car un nouvel appel peut réussir (petits défauts réseau, timeouts, 502/503 et certains 429 après attente). Les problèmes permanents ne se corrigent généralement pas tout seuls (identifiants invalides, endpoints erronés, requêtes mal formées, refus de permission).
Traiter toutes les erreurs de la même façon transforme un petit incident en indisponibilité. Réessayer des échecs permanents gaspille du temps, épuise plus vite les quotas et crée un arriéré qui ralentit tout le reste. Ne jamais réessayer des échecs transitoires oblige les utilisateurs à répéter des actions et perd du travail qui aurait pu aboutir quelques instants plus tard.
Accordez une attention particulière aux workflows où une pause ressemble à une rupture : paiement, connexion, réinitialisation de mot de passe et notifications (email/SMS/push). Un pic de deux secondes sur une API marketing est ennuyeux. Un pic de deux secondes sur l'autorisation de paiement bloque les revenus.
Une question utile : "Cet appel est-il requis pour terminer la tâche principale de l'utilisateur maintenant ?" Si oui, il faut des timeouts serrés, des réessais prudents et un chemin d'échec clair. Si non, déplacez-le dans une file et gardez l'appli réactive.
Un timeout est le temps maximum que vous êtes prêt à attendre avant d'abandonner et d'avancer. Sans limite claire, un fournisseur lent peut accumuler des requêtes en attente et bloquer le travail important.
Il est utile de séparer deux types d'attente :
Choisir des valeurs n'est pas une question de perfection. Il s'agit d'adapter l'attente à la patience humaine et à votre workflow.
Une manière pratique de choisir les timeouts est de partir de l'expérience :
Le compromis est réel. Trop long et vous bloquez des threads, workers et connexions DB. Trop court et vous provoquez des faux échecs et des réessais inutiles.
Les réessais aident quand un échec est probablement temporaire : un bref problème réseau, un pépin DNS ou un 500/502/503 ponctuel. Dans ces cas, une seconde tentative peut réussir et l'utilisateur ne remarquera rien.
Le risque, c'est une tempête de réessais. Quand de nombreux clients échouent en même temps et réessaient ensemble, ils peuvent surcharger le fournisseur (et vos propres workers). Le backoff et le jitter évitent cela.
Un budget de réessai vous garde raisonnable. Limitez les tentatives et plafonnez le temps total pour que les workflows centraux ne restent pas bloqués à cause d'un tiers.
Ne réessayez pas les erreurs clients prévisibles comme 400/422 (validation), 401/403 (auth) ou 404. Elles échoueront presque toujours encore et ne feront qu'ajouter de la charge.
Une autre garde-fou : ne réessayez les opérations d'écriture (POST/PUT) que si vous avez mis en place l'idempotence, sinon vous risquez des doubles facturations ou des enregistrements dupliqués.
L'idempotence signifie que vous pouvez exécuter la même requête plusieurs fois et obtenir le même résultat final. C'est important parce que les réessais sont normaux : les réseaux lâchent, les serveurs redémarrent et les clients expirent. Sans idempotence, un réessai « utile » crée des doublons et de vrais problèmes financiers.
Imaginez un paiement : l'API est lente, votre appli timeoute et vous réessayez. Si le premier appel a en réalité réussi, le réessai peut provoquer une seconde facturation. Le même risque existe pour la création d'une commande, le démarrage d'un abonnement, l'envoi d'un email/SMS, l'émission d'un remboursement ou la création d'un ticket support.
La solution est d'attacher une clé d'idempotence (ou un ID de requête) à chaque appel « faire quelque chose ». Elle doit être unique par action utilisateur, pas par tentative. Le fournisseur (ou votre service) utilise cette clé pour détecter les duplicatas et renvoyer le même résultat au lieu de répéter l'action.
Traitez la clé d'idempotence comme une partie du modèle de données, pas comme un header qu'on espère ne pas oublier.
Générez une clé quand l'utilisateur commence l'action (par exemple quand il clique sur Payer), puis stockez-la avec votre enregistrement local.
À chaque tentative :
Si vous êtes le « fournisseur » pour des appels internes, appliquez le même comportement côté serveur.
Un circuit breaker est un interrupteur de sécurité. Quand un service externe commence à échouer, vous cessez de l'appeler pendant une courte période au lieu d'ajouter encore des requêtes qui vont probablement expirer.
Les circuit breakers ont généralement trois états :
Quand le breaker est ouvert, votre appli doit faire quelque chose de prévisible. Si une API de validation d'adresse est en panne lors d'une inscription, acceptez l'adresse et marquez-la pour révision ultérieure. Si une vérification de risque de paiement est indisponible, placez la commande en revue manuelle ou désactivez temporairement l'option en l'expliquant.
Choisissez des seuils qui correspondent à l'impact utilisateur :
Gardez les fenêtres de refroidissement courtes (secondes à une minute) et limitez les sondes en mode semi-ouvert. L'objectif est de protéger d'abord les workflows centraux, puis de récupérer rapidement.
Quand une API externe est lente ou en panne, votre objectif est de laisser l'utilisateur avancer. Cela signifie avoir un Plan B honnête sur ce qui s'est passé.
Un repli est ce que fait votre appli quand l'API ne répond pas à temps. Les options incluent utiliser des données en cache, passer en mode dégradé (masquer des widgets non essentiels, désactiver des actions optionnelles), demander une saisie utilisateur plutôt que d'appeler l'API (saisie manuelle d'adresse) ou afficher un message clair avec la prochaine étape.
Soyez honnête : ne dites pas qu'une action est terminée si ce n'est pas le cas.
Si le travail n'a pas besoin d'être terminé pendant la requête utilisateur, poussez-le dans une file et répondez vite. Candidats courants : envoi d'emails, synchronisation vers un CRM, génération de rapports et envoi d'événements analytiques.
Échouez vite pour les actions essentielles. Si une API n'est pas requise pour finaliser le paiement (ou la création de compte), ne bloquez pas la requête. Acceptez la commande, mettez l'appel externe en file et réconciliez plus tard. Si l'API est requise (par exemple autorisation de paiement), échouez rapidement avec un message clair et ne faites pas attendre l'utilisateur.
Ce que l'utilisateur voit doit correspondre à ce qui se passe en coulisses : un statut clair (terminé, en attente, échoué), une promesse que vous pouvez tenir (reçu maintenant, confirmation plus tard), un moyen de réessayer et un enregistrement visible dans l'UI (journal d'activité, badge en attente).
Les limites de débit sont la façon pour un fournisseur de dire : « Vous pouvez nous appeler, mais pas trop souvent. » Vous les atteindrez plus vite que prévu : pics de trafic, jobs en arrière-plan qui se déclenchent en même temps ou un bug qui boucle sur des erreurs.
Commencez par contrôler le nombre de requêtes que vous générez. Regroupez quand c'est possible, mettez en cache les réponses même pour 30 à 60 secondes quand c'est sûr, et limitez le rythme côté client pour ne pas exploser plus vite que ce que le fournisseur accepte.
Quand vous recevez un 429 Too Many Requests, traitez-le comme un signal de ralentir.
Retry-After quand il est fourni.Limitez aussi la concurrence. Un seul workflow (par ex. synchronisation des contacts) ne doit pas consommer tous les workers et priver les flux critiques comme la connexion ou le paiement. Des pools séparés ou des plafonds par fonctionnalité aident.
Chaque appel tiers a besoin d'un plan d'échec. Vous n'avez pas besoin de perfection. Vous avez besoin d'un comportement prévisible quand le fournisseur a une mauvaise journée.
Décidez ce qui se passe si l'appel échoue maintenant. Un calcul de taxe pendant le checkout peut être indispensable. La synchronisation d'un contact marketing peut généralement attendre. Ce choix guide le reste.
Choisissez des timeouts par type d'appel et appliquez-les de façon cohérente. Ensuite, définissez un budget de réessai pour ne pas continuer à marteler une API lente.
Si une requête peut créer quelque chose ou facturer de l'argent, ajoutez des clés d'idempotence et stockez un enregistrement de la requête. Si une demande de paiement timeoute, un réessai ne doit pas entraîner une double facturation. Le suivi aide aussi le support à répondre : « Est-ce que c'est passé ? »
Quand les erreurs augmentent, arrêtez d'appeler le fournisseur pendant une courte période. Pour les appels indispensables, affichez un chemin "Réessayer" clair. Pour les appels qui peuvent attendre, mettez le travail en file et traitez-le plus tard.
Suivez la latence, le taux d'erreur et les événements d'ouverture/fermeture du breaker. Alertez sur des changements soutenus, pas sur des incidents isolés.
La plupart des pannes d'API ne commencent pas grandes. Elles le deviennent parce que votre appli réagit de la pire façon : elle attend trop, réessaye trop agressivement et mobilise les mêmes workers qui font tourner le reste.
Ces motifs causent des cascades :
De petites corrections évitent de grosses pannes : ne réessayez que les erreurs probablement transitoires (timeouts, certains 429, certains 5xx) et plafonnez les tentatives avec backoff et jitter ; gardez des timeouts courts et intentionnels ; exigez l'idempotence pour toute opération qui crée ou facture ; et concevez pour des pannes partielles.
Avant de déployer une intégration en prod, faites un passage rapide avec un état d'esprit d'échec. Si vous ne pouvez pas répondre "oui" à un point, considérez-le comme un blocage de release pour les workflows centraux comme l'inscription, le checkout ou l'envoi de messages.
Si un fournisseur de paiement commence à timer out, le bon comportement est "le checkout se charge toujours, l'utilisateur voit un message clair et vous n'attendez pas indéfiniment", pas "tout reste bloqué jusqu'à l'expiration".
Imaginez un checkout qui appelle trois services : une API de paiement pour prélever la carte, une API de taxe pour calculer la TVA et une API d'email pour envoyer le reçu.
L'appel de paiement est le seul qui doive être synchrone. Les problèmes de taxe ou d'email ne devraient pas bloquer l'achat.
Supposons que l'API taxe prenne parfois 8 à 15 secondes. Si le checkout attend, les utilisateurs abandonnent leur panier et votre appli bloque des workers.
Un flux plus sûr :
Résultat : moins de paniers abandonnés et moins de commandes bloquées quand le fournisseur taxe est lent.
L'email de reçu compte, mais il ne doit jamais bloquer la capture du paiement. Si l'API d'email échoue, le circuit breaker doit s'ouvrir après quelques échecs rapides et arrêter les appels pendant une courte fenêtre.
Au lieu d'envoyer l'email en ligne, placez un job "envoyer reçu" en file avec une clé d'idempotence (par exemple order_id + email_type). Si le fournisseur est en panne, la file réessaie en arrière-plan et le client voit toujours un achat réussi.
Résultat : moins de tickets support pour des confirmations manquantes et pas de revenus perdus parce que le checkout échoue pour des raisons non liées au paiement.
Choisissez un workflow qui vous fait le plus de mal quand il casse (checkout, inscription, facturation) et faites-en votre intégration de référence. Puis copiez les mêmes valeurs par défaut partout.
Un ordre de déploiement simple :
Documentez vos valeurs par défaut et tenez-les banales : un connect timeout unique, un request timeout, un nombre max de réessais, une fourchette de backoff, un temps de cooldown pour le breaker et les règles sur ce qui est réessayable.
Faites un exercice d'échec avant d'étendre au workflow suivant. Forcez des timeouts (ou bloquez le fournisseur en environnement de test), puis confirmez que l'utilisateur voit un message utile, que les replis fonctionnent et que les réessais en file ne s'accumulent pas indéfiniment.
Si vous développez rapidement de nouveaux produits, il vaut la peine de transformer ces valeurs de fiabilité en un modèle réutilisable. Pour les équipes utilisant Koder.ai (koder.ai), cela signifie souvent définir une fois les règles de timeout, réessai, idempotence et breaker, puis appliquer le même pattern à travers les nouveaux services au fur et à mesure que vous générez et itérez.