axum-idempotent

Crates.ioaxum-idempotent
lib.rsaxum-idempotent
version0.2.1
created_at2025-02-07 18:30:42.039625+00
updated_at2025-09-23 02:52:42.394605+00
descriptionA middleware for handling idempotent requests in axum applications.
homepagehttps://github.com/jimmielovell/axum-idempotent
repositoryhttps://github.com/jimmielovell/axum-idempotent
max_upload_size
id1547300
size132,007
Jimmie Lovell (jimmielovell)

documentation

https://docs.rs/axum-idempotent

README

axum-idempotent

Documentation Crates.io License: MIT Rust

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.

How it Works

The middleware operates in one of two modes:

  1. 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.

  2. 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.

Features

  • Request deduplication using either a direct client-provided key or automatic request hashing.
  • Configurable response caching duration.
  • Fine-grained controls for hashing, including ignoring the request body or specific headers.
  • Observability through a replay header (default: idempotency-replayed) on cached responses.
  • Seamless integration with session-based storage via the ruts crate.

Dependencies and Layer Ordering

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:

  1. CookieManagerLayer (Outermost)

  2. SessionLayer

  3. IdempotentLayer (Innermost)

Example

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"
}

Default Behavior

axum-idempotent is configured with safe defaults to prevent common issues.

Ignored Status Codes

To avoid caching transient server errors or certain client errors, responses with the following HTTP status codes are not cached by default:

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 408 Request Timeout
  • 429 Too Many Requests
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout

Ignored Headers

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.

  • user-agent,
  • accept,
  • accept-encoding,
  • accept-language,
  • cache-control,
  • connection,
  • cookie,
  • host,
  • pragma,
  • referer,
  • sec-fetch-dest,
  • sec-fetch-mode,
  • sec-fetch-site,
  • sec-ch-ua,
  • sec-ch-ua-mobile,
  • sec-ch-ua-platform

License

This project is licensed under the MIT License.

Commit count: 24

cargo fmt