Skip to main content
Extractor for accessing shared application state. State is global to a router and shared across all requests. It’s the recommended way to share things like database connection pools, API clients, and configuration.

Type signature

pub struct State<S>(pub S);

Basic usage

With Router

use axum::{
    Router,
    routing::get,
    extract::State,
};

// The application state
#[derive(Clone)]
struct AppState {
    db: DatabasePool,
    api_key: String,
}

let state = AppState {
    db: create_pool().await,
    api_key: "secret".to_owned(),
};

// Create a router with state
let app = Router::new()
    .route("/", get(handler))
    .with_state(state);

async fn handler(State(state): State<AppState>) {
    // Access the state
    let db = &state.db;
    let api_key = &state.api_key;
    // ...
}
State must implement Clone since it’s cloned for each request. Use Arc for expensive-to-clone types.

With MethodRouter

use axum::{
    routing::get,
    extract::State,
};

#[derive(Clone)]
struct AppState {}

let state = AppState {};

let method_router_with_state = get(handler)
    .with_state(state);

async fn handler(State(state): State<AppState>) {
    // ...
}

With Handler

use axum::{
    handler::Handler,
    extract::State,
};

#[derive(Clone)]
struct AppState {}

async fn handler(State(state): State<AppState>) {
    // ...
}

let state = AppState {};
let handler_with_state = handler.with_state(state);

# async {
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, handler_with_state.into_make_service()).await;
# };

Extractor ordering

State is an extractor, so it must appear before any body extractors (like Json, Form, String, etc.) in your handler signature.
// ✅ Correct: State before Json
async fn correct(State(state): State<AppState>, Json(data): Json<Data>) {}

// ❌ Wrong: Json before State
async fn wrong(Json(data): Json<Data>, State(state): State<AppState>) {}

Combining routers

When combining routers with Router::nest or Router::merge, they must have the same state type:
use axum::{Router, routing::get, extract::State};

#[derive(Clone)]
struct AppState {}

let state = AppState {};

// Create a router that will be nested
let api = Router::new()
    .route("/posts", get(posts_handler));

let app = Router::new()
    .nest("/api", api)
    .with_state(state);

async fn posts_handler(State(state): State<AppState>) {
    // ...
}

Explicit state types

When composing routers in separate functions, you may need to annotate the state type:
use axum::{Router, routing::get, extract::State};

#[derive(Clone)]
struct AppState {}

fn make_app() -> Router {
    let state = AppState {};
    
    Router::new()
        .nest("/api", make_api())
        .with_state(state)
}

// Explicitly specify Router<AppState> to compose with outer router
fn make_api() -> Router<AppState> {
    Router::new()
        .route("/posts", get(posts_handler))
}

async fn posts_handler(State(state): State<AppState>) {
    // ...
}

Substates

You can extract “substates” using FromRef to provide different state to different parts of your app:
use axum::{
    Router,
    routing::get,
    extract::{State, FromRef},
};

// Main application state
#[derive(Clone)]
struct AppState {
    api_state: ApiState,
}

// API-specific state
#[derive(Clone)]
struct ApiState {
    quota: u32,
}

// Enable extracting ApiState from AppState
impl FromRef<AppState> for ApiState {
    fn from_ref(app_state: &AppState) -> ApiState {
        app_state.api_state.clone()
    }
}

let state = AppState {
    api_state: ApiState { quota: 100 },
};

let app = Router::new()
    .route("/", get(handler))
    .route("/api/users", get(api_users))
    .with_state(state);

async fn handler(State(state): State<AppState>) {
    // Access full state
}

async fn api_users(State(api_state): State<ApiState>) {
    // Access only API state
}
You can also derive FromRef:
use axum::extract::FromRef;

#[derive(Clone, FromRef)]
struct AppState {
    api_state: ApiState,
    // Other fields...
}

Shared mutable state

Since state is cloned for each request, you can’t directly get a mutable reference. Use Arc<Mutex<_>> or similar:
use axum::{
    Router,
    routing::get,
    extract::State,
};
use std::sync::{Arc, Mutex};

#[derive(Clone)]
struct AppState {
    data: Arc<Mutex<String>>,
}

async fn handler(State(state): State<AppState>) {
    let mut data = state.data.lock().unwrap();
    *data = "updated value".to_owned();
}

let state = AppState {
    data: Arc::new(Mutex::new("initial".to_owned())),
};

let app = Router::new()
    .route("/", get(handler))
    .with_state(state);
Holding a locked std::sync::Mutex across .await points will result in !Send futures which are incompatible with axum. Use tokio::sync::Mutex if you need to hold a mutex across .await points.

State vs Extension

For global application state shared across all requests, prefer State because:
  • Type-safe: The state type is part of the router’s type
  • Compile-time checked: Wrong state types are caught at compile time
  • Better error messages
Use Extension for request-derived data like authorization info or per-request context.