L'importance de l'idempotence dans vos API
Une requête idempotente garantit que son exécution multiple produit le même effet que son exécution unique. Pourquoi c'est fondamental, et comment l'implémenter en Go.
L’idempotence est un concept fondamental dans la conception des API, en particulier lorsqu’elles exposent des endpoints permettant des opérations d’écriture (POST, PUT, DELETE).
Une requête idempotente garantit que son exécution multiple produit le même effet que son exécution unique. Cela évite les incohérences, améliore la résilience des systèmes et facilite la gestion des erreurs.
Pourquoi l’idempotence est-elle importante ?
Les API peuvent être appelées plusieurs fois involontairement pour diverses raisons : un client réseau peut retransmettre une requête en cas de timeout, une interruption de connexion peut pousser l’utilisateur à réessayer l’opération, un traitement asynchrone peut engendrer des doublons accidentels.
Sans idempotence, ces répétitions peuvent provoquer des effets indésirables comme des transactions en double, des facturations erronées ou la création d’objets redondants.
Petits exemples
Imaginons un bête système de commande (on est d’accord c’est juste pour l’exemple hein) :
package main
import (
"encoding/json"
"net/http"
"sync"
)
type Order struct {
ID int `json:"id"`
Item string `json:"item"`
}
var (
orders = []Order{}
nextID = 1
mutex = &sync.Mutex{}
)
func createOrder(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
var order Order
if err := json.NewDecoder(r.Body).Decode(&order); err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
mutex.Lock()
order.ID = nextID
nextID++
orders = append(orders, order)
mutex.Unlock()
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(order)
}
Dans ce cas, si un client soumet plusieurs fois la même requête (par exemple en raison d’un timeout), une nouvelle commande sera créée à chaque fois — sympa le fake CA — avec un ID différent, ce qui entraîne des doublons indésirables.
Rendons cette route idempotente
L’approche la plus simple consiste à utiliser un identifiant unique pour chaque requête fourni par le client (ici requestId) :
type Order struct {
ID int `json:"id"`
Item string `json:"item"`
RequestID string `json:"request_id"`
}
var (
orders = map[string]Order{}
nextID = 1
mutex = &sync.Mutex{}
)
func createOrder(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
var order Order
if err := json.NewDecoder(r.Body).Decode(&order); err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
mutex.Lock()
defer mutex.Unlock()
// Si on a déjà traité cette requête, on renvoie la réponse initiale
if existingOrder, found := orders[order.RequestID]; found {
json.NewEncoder(w).Encode(existingOrder)
return
}
order.ID = nextID
nextID++
orders[order.RequestID] = order
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(order)
}
Si un client soumet plusieurs fois une requête avec le même requestId, l’API retournera simplement la réponse initiale au lieu de créer un nouvel enregistrement.
D’autres stratégies
Si vous n’avez pas la possibilité d’ajouter un identifiant unique à la requête, d’autres approches existent :
Hash du corps de la requête — Calculer une empreinte unique du contenu (SHA-256 par exemple) et l’utiliser comme clé d’identification.
Journal des opérations — Maintenir un journal des requêtes traitées permettant d’ignorer les doublons.
Contraintes en base de données — Imposer des contraintes d’unicité au niveau de la BDD, comme une clé unique sur un champ spécifique.
Méthodes HTTP appropriées — Favoriser PUT (idempotent par nature) au lieu de POST lorsque possible.
En conclusion
L’idempotence est un principe essentiel pour la conception d’API robustes. En adoptant des stratégies comme l’utilisation d’identifiants uniques pour les requêtes, vous améliorez la fiabilité et l’intégrité de vos systèmes tout en réduisant les erreurs et les incohérences.
Appliquer ces bonnes pratiques permet d’éviter bien des maux dans les architectures modernes.
Besoin d'aide sur ce sujet ?
Réserver un créneau