Expand description
Idempotency-key middleware.
Protects mutating routes against duplicate execution. On the first request
with a given Idempotency-Key, the handler runs and the captured response
is persisted. Subsequent requests with the same key replay the stored
response if the request body hashes match, or return 422 with a
idempotency_key_conflict code if a different body is sent under the same
key (per the draft IETF spec).
Usage:
ⓘ
use cratestack_axum::idempotency::{IdempotencyLayer, SqlxIdempotencyStore};
let store = std::sync::Arc::new(SqlxIdempotencyStore::new(pool.clone()));
let router = generated_router.layer(IdempotencyLayer::new(store, std::time::Duration::from_secs(24 * 3600)));In Phase 1 the layer is opt-in at the consumer’s router. A follow-up will
wire it into macro-generated routers by default, gated by a
@no_idempotency opt-out attribute already recognised by the parser.
Structs§
- Idempotency
Layer - Tower layer that wires an
IdempotencyStoreinto the request pipeline. - Idempotency
Record - Persisted idempotency record returned on a replay. Banks need an invariant view of the captured response — the store rebuilds this from its persisted columns when the second caller asks to replay.
- Idempotency
Service
Enums§
- Reservation
Outcome - Outcome of an atomic
reserve_or_fetchcall.
Constants§
- IDEMPOTENCY_
TABLE_ DDL - SQL DDL for the idempotency table. Banks typically run migrations through
their own tooling —
cratestackcurrently ships migrations as raw DDL since the migration engine is deferred to Phase 3.
Traits§
Functions§
- decode_
headers - Decode a blob produced by
encode_headersback into aHeaderMap. Returns an empty map on malformed input rather than failing the replay — a corrupt headers blob is a recoverable curiosity, not a reason to drop the response status and body the caller is waiting for. - encode_
headers - Encode a response’s headers into the opaque blob that the store
persists. Format: little-endian length-prefixed
(name, value)pairs. Header values can carry arbitrary bytes (per RFC 9110 they may include any opaque-data octet, with the exception of CR/LF), so a binary blob is the only correct representation — JSON would force lossy UTF-8 coercion on values like opaqueETagtokens that may already be quoted-string blobs. - hash_
request - Stable fingerprint of a request: SHA-256 over method, path + query,
content-type, and body bytes. Used to detect when a duplicate key is
reused with a different payload (the conflict case the draft spec
calls out). The
pathargument should include the query string so modifier-style flags (?dry_run=true,?confirm=true) don’t collide — the middleware passesUri::path_and_queryfor that reason. - is_
idempotent_ target_ method - Returns true if the HTTP method is one we’d guard with idempotency. We apply only to mutating verbs — GETs are already safely repeatable.
- parse_
idempotency_ key - Parse the
Idempotency-Keyrequest header. ReturnsOk(None)if absent. The key must be ASCII and reasonably short to avoid storage abuse.