Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tokio-rs/axum/llms.txt

Use this file to discover all available pages before exploring further.

Handlers are async functions that process HTTP requests and produce responses. They form the core logic of your application.

Basic handlers

A handler is any async function that implements the Handler trait:
use axum::{Router, routing::get};

// Handler that returns an empty 200 OK response
async fn unit_handler() {}

// Handler that returns a plain text response
async fn string_handler() -> String {
    "Hello, World!".to_string()
}

let app = Router::new()
    .route("/", get(unit_handler))
    .route("/hello", get(string_handler));

Handler signatures

Handlers can take extractors as arguments and return any type that implements IntoResponse:
1

Zero or more extractors

Extractors pull data from the request. They must implement FromRequestParts or FromRequest.
use axum::{extract::Path, http::HeaderMap};

async fn handler(
    Path(id): Path<u32>,
    headers: HeaderMap,
) -> String {
    format!("ID: {}, Headers: {:?}", id, headers)
}
2

Return type implements IntoResponse

The return type must be convertible to a response.
use axum::http::StatusCode;

// Returns just a status code
async fn handler() -> StatusCode {
    StatusCode::OK
}

// Returns a tuple (status, body)
async fn handler_with_status() -> (StatusCode, &'static str) {
    (StatusCode::CREATED, "Resource created")
}
3

Must be async

All handlers must be async functions.
// ✅ Correct
async fn handler() -> &'static str {
    "response"
}

// ❌ Won't compile - not async
fn handler() -> &'static str {
    "response"
}

Extractors in handlers

Handlers can extract data from requests using extractors:
use axum::{
    extract::{Path, Query},
    body::Bytes,
    http::StatusCode,
};
use serde::Deserialize;

#[derive(Deserialize)]
struct Pagination {
    page: usize,
    per_page: usize,
}

async fn handler(
    Path(user_id): Path<u32>,
    Query(pagination): Query<Pagination>,
    body: Bytes,
) -> Result<String, StatusCode> {
    if let Ok(body_str) = String::from_utf8(body.to_vec()) {
        Ok(format!(
            "User {}, page {}, body: {}",
            user_id, pagination.page, body_str
        ))
    } else {
        Err(StatusCode::BAD_REQUEST)
    }
}
The order of extractors matters. See Extractors for details.

Return types

Handlers can return various types:
async fn handler() -> &'static str {
    "Hello, World!"
}

async fn dynamic_handler() -> String {
    format!("Time: {}", std::time::SystemTime::now())
}

Error handling in handlers

Use Result types to handle errors:
use axum::{
    extract::Json,
    http::StatusCode,
    response::IntoResponse,
};
use serde::Deserialize;

#[derive(Deserialize)]
struct CreateUser {
    name: String,
}

async fn create_user(
    Json(payload): Json<CreateUser>,
) -> Result<String, StatusCode> {
    if payload.name.is_empty() {
        return Err(StatusCode::BAD_REQUEST);
    }

    Ok(format!("Created user: {}", payload.name))
}
For more complex error handling, see Error Handling.

Converting handlers to services

Handlers can be converted to Tower services:
use axum::handler::HandlerWithoutStateExt;

async fn handler() -> &'static str {
    "Hello"
}

let service = handler.into_service();

Applying middleware to handlers

You can apply middleware directly to individual handlers:
use axum::{
    Router,
    routing::get,
    handler::Handler,
};
use tower::limit::ConcurrencyLimitLayer;

async fn handler() -> &'static str {
    "Hello"
}

let layered_handler = handler.layer(ConcurrencyLimitLayer::new(64));

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

Debugging handler errors

Use the #[axum::debug_handler] macro to get better error messages during development:
use axum::debug_handler;

#[debug_handler]
async fn handler() -> String {
    "Hello".to_string()
}
This macro provides clearer compilation errors when your handler signature is incorrect.

Next steps

Extractors

Learn how to extract data from requests

Responses

Explore different response types

Build docs developers (and LLMs) love