Idempotency in APIs goes far beyond storing a key and replaying a response. The real complexity emerges in edge cases: concurrent retries racing each other, the same idempotency key reused with a different request body, partial failures where a downstream provider succeeded but the local process crashed before recording the result, and message queue consumers receiving duplicate events. A robust implementation requires atomic ownership acquisition via a unique constraint on a scoped key (tenant + operation + key), hashing the validated command (not raw bytes) to detect mismatched retries, an explicit state machine (IN_PROGRESS, COMPLETED, FAILED_RETRYABLE, FAILED_REPLAYABLE, UNKNOWN_REQUIRES_RECOVERY), and a recovery worker for downstream uncertainty. The outbox/inbox pattern handles event deduplication. Expiry windows, response storage trade-offs (full body vs. resource reference), and schema-change replay are all contract decisions. A checklist and concrete failure-mode tests are provided.

25m read timeFrom blog.dochia.dev
Post cover image
Table of contents
Idempotency is about the effectWhat you need to rememberSame key, different commandHash the command, not the bytesThe first insert decides who owns executionThe provider timeout is where your guarantee endsReplay is a contract, not a convenienceYour queue consumer has the same bugExpiry is part of the API contractFailure replay is a policy decisionWhen one transaction cannot cover the operationWhen not to build a general idempotency layerFailure modes worth testingChecklist before shippingThe second request is not a repeat until proven

Sort: