sql javascript 104 lines · 3 tabs

Idempotent POST Requests with an Idempotency-Key Middleware in Express

Shared by codesnips Aug 2026
3 tabs
CREATE TABLE idempotency_keys (
    id              BIGSERIAL PRIMARY KEY,
    idempotency_key TEXT        NOT NULL,
    request_path    TEXT        NOT NULL,
    request_fingerprint TEXT    NOT NULL,
    status          TEXT        NOT NULL DEFAULT 'in_progress',
    response_code   INTEGER,
    response_body   JSONB,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    locked_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
    CONSTRAINT idempotency_keys_status_chk
        CHECK (status IN ('in_progress', 'completed')),
    CONSTRAINT idempotency_keys_unique
        UNIQUE (idempotency_key, request_path)
);

-- Background sweeper reaps stale in_progress rows and expired records.
CREATE INDEX idempotency_keys_sweep_idx
    ON idempotency_keys (status, locked_at);
3 files · sql, javascript Explain with highlit

This snippet implements the Idempotency-Key pattern for unsafe HTTP requests in an Express API. The idea is simple but easy to get wrong: when a client retries a POST (a timeout, a dropped connection, a double-click), the server must recognize the retry and return the original response instead of performing the side effect twice. This is essential for anything money-related — charging a card, creating an order — and for any endpoint fronted by an at-least-once delivery system like a webhook sender.

The migrations/idempotency_keys.sql tab defines the durable store. Each row is keyed by a composite UNIQUE on (idempotency_key, request_path) so the same key can safely appear on different endpoints. A request_fingerprint column records a hash of the request body, letting the middleware detect the dangerous case where a client reuses a key with a different payload. The status column models a small state machine (in_progress then completed), and response_code/response_body cache the eventual reply.

In idempotencyMiddleware.js, the flow begins with an atomic INSERT ... ON CONFLICT DO NOTHING. If the insert wins, this is the first time the key is seen and the request proceeds. If it conflicts, an existing record is loaded: a completed record replays the stored status and body verbatim, while an in_progress record means a concurrent request is still running, so the middleware returns 409 to signal the client to retry later. A mismatched request_fingerprint yields 422, since replaying under a conflicting body would be incorrect.

The clever part is capturing the response. The middleware wraps res.json so that when the handler finally responds, the real status code and body are persisted back into the row and the record is flipped to completed. This makes the cache self-populating without the route handlers knowing anything about idempotency.

The payments route tab shows the payoff: router.post('/charges', ...) mounts the middleware and writes an ordinary handler. Note the trade-offs — the store needs a TTL sweep, the fingerprint uses a stable JSON serialization, and in_progress rows from crashed requests should be reaped by a background job. Used carefully, this turns retries from a liability into a safe default.


Related snips

Share this code

Here's the card — post it anywhere.

Idempotent POST Requests with an Idempotency-Key Middleware in Express — share card
Link copied