Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

State

Application state is any value shared across handlers. You provide it to the router, and handlers extract it via State<T>.

Providing State

Pass state to Router::with_state():

#![allow(unused)]
fn main() {
use volter::*;

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

let state = AppState { db_url: "postgres://localhost/db".into() };
let app: Router<AppState> = Router::with_state(state);
}

The state type must implement Clone because the router clones it when creating boxed services at setup time.

Extracting State

Use State<T> as a handler parameter:

#![allow(unused)]
fn main() {
async fn dashboard(State(state): State<AppState>) -> String {
    format!("Connected to {}", state.db_url)
}
}

The T in State<T> is checked at compile time. If you declare Router::with_state(AppState) but a handler expects State<OtherState>, the code won’t compile.

Why No ? Operator Needed

State<T> extraction never fails — the state type is guaranteed to match at compile time. The rejection type is Infallible, so you can use State anywhere in the extractor chain without error handling.

Stateless Routes

When no state is needed, use Router::new() (state defaults to ()):

#![allow(unused)]
fn main() {
let app: Router = Router::new()
    .route("/health", get(health_check));

async fn health_check() -> &'static str {
    "OK"
}
}

Handlers that don’t extract State work with any state type, including ().

When to Use State

  • Database connection pools
  • Configuration values
  • HTTP clients
  • Any value that should be available to every handler

For per-request values (like an authenticated user), use Extension<T> instead.