Skip to main content

Pour aller plus loin : cache, idempotence et webhooks

Le cache HTTP avec ETag

Pourquoi retélécharger la carte si elle n'a pas changé ? Le serveur associe à chaque version d'une ressource une empreinte, l'ETag. Le client la renvoie à la requête suivante, et le serveur répond 304 Not Modified, sans body, si rien n'a bougé.

GET /v1/cocktails/3

HTTP/1.1 200 OK
ETag: "a1b2c3"
{"id": 3, "name": "Segfault Sunrise", ...}
GET /v1/cocktails/3
If-None-Match: "a1b2c3"

HTTP/1.1 304 Not Modified

Essayez avec GitHub, qui ne décompte pas les réponses 304 de votre quota :

curl -i https://api.github.com/users/torvalds            # notez l'etag
curl -i https://api.github.com/users/torvalds -H 'If-None-Match: W/"..."'

L'ETag sert aussi à éviter les modifications concurrentes : un PUT avec If-Match: "a1b2c3" échoue en 412 Precondition Failed si quelqu'un a modifié la ressource entre-temps.

Les clés d'idempotence

POST n'est pas idempotent : si la connexion coupe après l'envoi d'un paiement, le client ne sait pas s'il a été pris en compte. Le renvoyer, c'est risquer de payer deux fois.

POST /v1/orders
Idempotency-Key: 8e03978e-40d5-43e8-bc93-6894a57f9324
Content-Type: application/json

{"cocktail_id": 3}

Le serveur mémorise la clé et la réponse. Si la même clé revient, il renvoie la réponse mémorisée au lieu de recréer une commande.

Commander deux fois le même cocktail parce que le Wi-Fi a coupé ? Plus jamais.

Les webhooks

Plutôt que d'interroger l'API toutes les 5 secondes pour savoir si la commande est prête (polling), le client donne une URL, et c'est l'API qui l'appelle quand l'événement se produit.

POST https://mon-appli.example/webhooks/api-ritif
Content-Type: application/json
X-Signature-256: sha256=5d7f1c...

{"event": "order.ready", "order_id": 42, "created_at": "2026-10-14T10:34:30Z"}

Problème : n'importe qui peut envoyer une requête à cette URL. L'émetteur signe donc le body avec un secret partagé (HMAC), et le récepteur vérifie la signature :

import hashlib, hmac

def is_valid(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
  • La signature se calcule sur le body brut, avant tout parsing JSON.
  • La comparaison se fait en temps constant (timingSafeEqual, compare_digest), pour ne pas laisser deviner la signature octet par octet.
  • Le récepteur répond vite (2xx) et traite l'événement ensuite. Sinon, l'émetteur considère l'envoi comme échoué et réessaie.

GitHub, Stripe ou Shopify fonctionnent tous ainsi. GitHub, par exemple, envoie sa signature dans le header X-Hub-Signature-256.