Why This Matters
Networks are unreliable. A request might succeed but the response gets lost, so the client retries. Without idempotency, that retry could charge a customer twice, send two emails, or create duplicate records. In distributed systems, you must assume every request might be delivered more than once.
Idempotency is the property that performing an operation multiple times produces the same result as performing it once. It is the foundation of reliable communication in any system where retries, timeouts, or message duplication can occur.
Define Terms
Visual Model
The full process at a glance. Click Start tour to walk through each step.
Idempotency keys let servers detect and safely handle duplicate requests.
Code Example
// Idempotent API with idempotency keys
class PaymentService {
constructor() {
this.processedKeys = new Map(); // key -> result
this.balance = 1000;
}
charge(amount, idempotencyKey) {
// Check if we already processed this key
if (this.processedKeys.has(idempotencyKey)) {
console.log(`Duplicate request detected (key: ${idempotencyKey})`);
return this.processedKeys.get(idempotencyKey);
}
// Process the charge
this.balance -= amount;
const result = {
status: "success",
charged: amount,
remaining: this.balance
};
// Store result for future duplicate detection
this.processedKeys.set(idempotencyKey, result);
console.log(`Charged $${amount} (key: ${idempotencyKey})`);
return result;
}
}
const payments = new PaymentService();
const key = "pay_abc123";
// First request - processes normally
console.log(payments.charge(50, key));
// Retry with same key - returns cached result
console.log(payments.charge(50, key));
// Balance only decreased once
console.log("Balance:", payments.balance); // 950, not 900
// Naturally idempotent operations
// SET operations are idempotent: SET x = 5 (same result every time)
// DELETE is idempotent: deleting an already-deleted item is fine
// GET is idempotent: reading data has no side effects
// NOT idempotent without a key
// INCREMENT: x++ changes the result each time
// INSERT: creates duplicates
// TRANSFER: moves money multiple timesInteractive Experiment
Try these exercises to build intuition:
- Add an expiration time to idempotency keys (e.g., keys expire after 24 hours). Why is this necessary for production systems?
- Classify these HTTP methods as idempotent or not: GET, POST, PUT, DELETE, PATCH. Which ones are naturally idempotent?
- Modify the payment service to handle concurrent requests with the same key. What happens if two threads check simultaneously?
- Think about email sending: how would you make a "send welcome email" operation idempotent?
Quick Quiz
Coding Challenge
Write a class called `IdempotentCounter` with a method `increment(operationId)` that increments an internal counter. If the same operationId is used twice, the counter should NOT increment again. Include a `getCount()` method that returns the current count.
Real-World Usage
Idempotency is critical in every payment system and API:
- Stripe: Every API request accepts an
Idempotency-Keyheader. If a charge request is retried with the same key, Stripe returns the original result without charging again. - AWS SQS: Messages may be delivered more than once. Consumers must handle duplicates using message deduplication IDs.
- HTTP methods: GET, PUT, and DELETE are designed to be idempotent by the HTTP specification. POST is not, which is why POST-based APIs need explicit idempotency keys.
- Database migrations: Well-designed migrations use IF NOT EXISTS clauses to be safely re-runnable.
- Webhooks: Webhook endpoints must be idempotent because providers often retry delivery on failure.