| Crates.io | axum-idempotent |
| lib.rs | axum-idempotent |
| version | 0.2.1 |
| created_at | 2025-02-07 18:30:42.039625+00 |
| updated_at | 2025-09-23 02:52:42.394605+00 |
| description | A middleware for handling idempotent requests in axum applications. |
| homepage | https://github.com/jimmielovell/axum-idempotent |
| repository | https://github.com/jimmielovell/axum-idempotent |
| max_upload_size | |
| id | 1547300 |
| size | 132,007 |
Middleware for handling idempotent requests in axum applications.
This crate provides middleware that ensures idempotency of HTTP requests. When an identical request is made, a cached response is returned instead of re-executing the handler, preventing duplicate operations like accidental double payments.
The middleware operates in one of two modes:
Direct Key Mode (Recommended): By configuring use_idempotency_key_header(), the middleware uses a client-provided header (e.g., Idempotency-Key) value directly as the cache key. This is the most performant and observable method, as it avoids server-side hashing and uses an identifier known to both the client and server.
Hashing Mode: If not using a direct key, a unique hash is generated from the request's method, path, headers (configurable), and body. This hash is then used as the cache key.
If a key is found in the session store, the cached response is returned immediately. If not, the request is processed by the handler, and the response is cached before being sent to the client.
idempotency-replayed) on cached responses.This middleware requires a session layer, such as SessionLayer from the ruts crate. For the IdempotentLayer to access the session, it must be placed inside the SessionLayer.
The correct order is:
CookieManagerLayer (Outermost)
SessionLayer
IdempotentLayer (Innermost)
use std::sync::Arc;
use axum::{Router, routing::post};
use ruts::{CookieOptions, SessionLayer};
use axum_idempotent::{IdempotentLayer, IdempotentOptions};
use tower_cookies::CookieManagerLayer;
use ruts::store::memory::MemoryStore;
#[tokio::main]
async fn main() {
// Your session store
let store = Arc::new(MemoryStore::new());
// Configure the idempotency layer to use the "Idempotency-Key" header
let idempotent_options = IdempotentOptions::default()
.use_idempotency_key_header(Some("Idempotency-Key"))
.expire_after(60 * 5); // Cache responses for 5 minutes
// Create the router with the correct layer order
let app = Router::new()
.route("/payments", post(process_payment))
.layer(IdempotentLayer::<MemoryStore>::new(idempotent_options))
.layer(SessionLayer::new(store)
.with_cookie_options(CookieOptions::build().name("session")))
.layer(CookieManagerLayer::new());
// Run the server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn process_payment() -> &'static str {
"Payment processed"
}
axum-idempotent is configured with safe defaults to prevent common issues.
To avoid caching transient server errors or certain client errors, responses with the following HTTP status codes are not cached by default:
In hashing mode, common, the following request-specific headers are ignored by default to ensure that requests from different clients are treated as identical if the core parameters are the same. This does not apply when using use_idempotency_key_header.
This project is licensed under the MIT License.