1. The Problem: The Peril of Duplicate Requests in Distributed Systems
In the complex landscape of modern distributed systems and microservices architectures, network failures, timeouts, and client-side retries are not exceptions; they are an inherent part of the environment. Imagine a scenario: a user clicks 'Place Order' on an e-commerce platform. The client sends a request to the order service. Due to a momentary network glitch, the client doesn't receive a timely response and, thinking the request failed, automatically retries the same operation. Without careful design, this seemingly innocent retry can lead to disastrous consequences:
- Financial Inaccuracies: A customer might be charged twice for the same product, or a transfer of funds could be duplicated, leading to significant financial discrepancies and customer dissatisfaction.
- Data Corruption: Inventory levels could be decremented multiple times for a single purchase, or a user registration might create duplicate accounts, polluting your database and impacting data analytics.
- Inconsistent States: Business processes can enter an invalid state. For example, a subscription service might activate the same plan multiple times, leading to confusion and manual reconciliation efforts.
- Resource Wastage: Duplicate processing consumes valuable computational resources, increasing operational costs and potentially degrading system performance for other users.
These issues erode trust, incur significant operational overhead for manual fixes, and directly impact the bottom line. The core problem is that many API operations are inherently non-idempotent; executing them multiple times with the same parameters yields different outcomes.
2. The Solution Concept & Architecture: Embracing Idempotency
Idempotency, in the context of API design, means that an operation can be called multiple times without causing different effects beyond the initial call. It ensures that executing the same request multiple times has the same outcome as executing it once. This doesn't mean the response will always be identical (e.g., a '201 Created' might become a '200 OK' or '409 Conflict' on subsequent calls), but the state change on the server remains consistent.
To achieve idempotency, we introduce a unique identifier, often called an Idempotency Key, with each request. The server uses this key to track whether an operation with that specific key has been processed before. If it has, the server can either return the original result or simply acknowledge that the operation was already handled, without re-executing the side effects.
High-Level Architecture:
- Client Generates Key: The client (e.g., mobile app, web frontend, another microservice) generates a globally unique idempotency key (e.g., a UUID) for each potentially idempotent request. This key is typically sent in a custom HTTP header, like
Idempotency-Key. - Server Receives Request: Upon receiving a request with an
Idempotency-Key, the API gateway or the target microservice first checks if an operation with that key is already in progress or has been completed. - Idempotency Store: A dedicated, fast-access store (like Redis or a distributed cache) is used to track the state of idempotency keys. This store maps the key to the request's status (e.g., 'processing', 'completed', 'failed') and potentially the original response.
- Conditional Processing:
- If the key is found and the operation is 'processing', the server can wait and return the final result once available, or return a '409 Conflict' indicating it's already being handled.
- If the key is found and the operation is 'completed' (or 'failed'), the server returns the previously stored result immediately.
- If the key is not found, the server marks the key as 'processing', executes the business logic, stores the final result, and then marks the key as 'completed'.
This pattern prevents the core business logic from executing multiple times, even if the request is duplicated.
3. Step-by-Step Implementation: Node.js with Redis & PostgreSQL
Let's implement an idempotent API for placing an order using Node.js, Express, Redis as an idempotency store, and PostgreSQL for our primary data storage.
First, ensure you have Node.js, Redis, and PostgreSQL installed and running. Install necessary packages:

Muhammad Tahir
Building web & mobile apps since 2021. Passionate about clean code and real-world impact.
Related Posts


