Skip to main content
Extension<T> is both an extractor and a response type that works with HTTP extensions to share state across handlers and middleware.

As an extractor

Extension allows handlers to extract values that have been added to request extensions by middleware or layers:
use axum::{Router, routing::get, Extension};
use std::sync::Arc;

#[derive(Clone)]
struct State {
    count: i32,
}

async fn handler(Extension(state): Extension<Arc<State>>) -> String {
    format!("Count: {}", state.count)
}

let state = Arc::new(State { count: 42 });

let app = Router::new()
    .route("/", get(handler))
    .layer(Extension(state));

Optional extensions

If an extension might not be present, use Option<Extension<T>>:
use axum::{Router, routing::get, Extension};

#[derive(Clone)]
struct Config {
    debug: bool,
}

async fn handler(config: Option<Extension<Config>>) -> String {
    match config {
        Some(Extension(cfg)) => format!("Debug: {}", cfg.debug),
        None => "No config available".to_string(),
    }
}

let app = Router::new().route("/", get(handler));
If you extract Extension<T> (without Option) and the extension is missing, the request will be rejected with a 500 Internal Server Error.

As a layer

Extension can be used as a layer to add values to all requests:
use axum::{Router, routing::get, Extension};
use std::sync::Arc;

#[derive(Clone)]
struct AppConfig {
    api_key: String,
}

let config = Arc::new(AppConfig {
    api_key: "secret".to_string(),
});

let app = Router::new()
    .route("/", get(|| async { "Hello" }))
    .layer(Extension(config));

As a response

Extension can be returned from handlers to add values to response extensions:
use axum::{
    Router,
    routing::get,
    Extension,
    response::IntoResponse,
};

#[derive(Clone)]
struct Metadata {
    request_id: String,
}

async fn handler() -> impl IntoResponse {
    (
        Extension(Metadata {
            request_id: "abc-123".to_string(),
        }),
        "Response body"
    )
}

let app = Router::new().route("/", get(handler));

Extension vs State

Extension and State are similar but have important differences:
FeatureExtensionState
Type safetyNo compile-time checksType checked at compile time
Multiple valuesCan have many different typesOne state type per router
Added viaMiddleware/layerwith_state()
Recommended forMiddleware-injected dataApplication state
use axum::{Router, routing::get, Extension, extract::State};
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    db: String,
}

#[derive(Clone)]
struct RequestId(String);

// State: defined when creating the router
async fn with_state(State(state): State<Arc<AppState>>) -> String {
    format!("DB: {}", state.db)
}

// Extension: added by middleware
async fn with_extension(Extension(id): Extension<RequestId>) -> String {
    format!("Request ID: {}", id.0)
}

let state = Arc::new(AppState { db: "postgres".to_string() });

let app = Router::new()
    .route("/state", get(with_state))
    .route("/extension", get(with_extension))
    .layer(Extension(RequestId("req-123".to_string())))
    .with_state(state);
Prefer State for application-level state that’s known at compile time. Use Extension for values added by middleware or for multiple independent shared values.

Type requirements

The type T in Extension<T> must be:
  • Clone - Values are cloned when extracted
  • Send + Sync - Required for async handlers
  • 'static - Cannot contain non-static references
use axum::Extension;
use std::sync::Arc;

// ✅ Good: Arc for shared ownership
#[derive(Clone)]
struct Good {
    data: Arc<String>,
}

// ❌ Bad: Contains non-static reference
// #[derive(Clone)]
// struct Bad<'a> {
//     data: &'a str,
// }

Rejection

Rejects with MissingExtension when:
  • The extension type wasn’t added via layer or middleware
  • A previous extractor removed it from request extensions
Error message:
Extension of type `your::Type` was not found. Perhaps you forgot to add it? See `axum::Extension`.

Example: Request ID middleware

use axum::{
    Router,
    routing::get,
    Extension,
    middleware::{self, Next},
    extract::Request,
    response::Response,
};
use uuid::Uuid;

#[derive(Clone)]
struct RequestId(String);

async fn add_request_id(mut req: Request, next: Next) -> Response {
    let id = RequestId(Uuid::new_v4().to_string());
    req.extensions_mut().insert(id);
    next.run(req).await
}

async fn handler(Extension(RequestId(id)): Extension<RequestId>) -> String {
    format!("Your request ID: {}", id)
}

let app = Router::new()
    .route("/", get(handler))
    .layer(middleware::from_fn(add_request_id));

See also

  • State - Recommended way to share application state
  • ConnectInfo - Extract connection metadata