Skip to main content
Extractors allow you to pull data from HTTP requests in a type-safe way. They implement either the FromRequestParts or FromRequest trait.

How extractors work

Extractors are types that implement FromRequest or FromRequestParts. When used as handler arguments, Axum automatically extracts the data:
use axum::{
    extract::{Path, Query},
    http::HeaderMap,
};
use serde::Deserialize;

#[derive(Deserialize)]
struct Params {
    page: u32,
}

async fn handler(
    Path(id): Path<u32>,
    Query(params): Query<Params>,
    headers: HeaderMap,
) -> String {
    format!("ID: {}, Page: {}", id, params.page)
}

Common extractors

Axum provides many built-in extractors:
Extract data from the URL path:
use axum::{Router, routing::get, extract::Path};
use serde::Deserialize;

// Single parameter
async fn get_user(Path(id): Path<u32>) -> String {
    format!("User ID: {}", id)
}

// Multiple parameters as tuple
async fn get_team(
    Path((user_id, team_id)): Path<(u32, u32)>
) -> String {
    format!("User {} in team {}", user_id, team_id)
}

// Multiple parameters as struct
#[derive(Deserialize)]
struct TeamParams {
    user_id: u32,
    team_id: u32,
}

async fn get_team_struct(
    Path(TeamParams { user_id, team_id }): Path<TeamParams>
) -> String {
    format!("User {} in team {}", user_id, team_id)
}

let app = Router::new()
    .route("/users/:id", get(get_user))
    .route("/users/:user_id/team/:team_id", get(get_team));

The order of extractors

The order of extractors in your handler signature matters!
Extractors are processed in order:
  1. FromRequestParts extractors come first (Path, Query, State, headers, etc.)
  2. FromRequest extractors come last (body extractors like Json, Form, Bytes)
use axum::{
    extract::{Path, Query, State},
    Json,
    http::HeaderMap,
};

#[derive(Clone)]
struct AppState {}

// ✅ Correct order
async fn correct(
    State(state): State<AppState>,     // FromRequestParts
    Path(id): Path<u32>,                // FromRequestParts
    Query(params): Query<Params>,       // FromRequestParts
    headers: HeaderMap,                  // FromRequestParts
    Json(payload): Json<CreateUser>,    // FromRequest (body)
) {}

// ❌ Wrong - body extractor before Path
async fn wrong(
    Json(payload): Json<CreateUser>,    // FromRequest - too early!
    Path(id): Path<u32>,                // Won't compile
) {}

Optional extractors

Use Option<T> to make an extractor optional:
use axum::{
    extract::{Path, Query},
    Router,
    routing::get,
};

async fn handler(
    path: Option<Path<String>>,
    query: Option<Query<Params>>,
) -> String {
    match (path, query) {
        (Some(Path(p)), Some(Query(q))) => 
            format!("Path: {}, Query: {:?}", p, q),
        _ => "Missing data".to_string(),
    }
}

The Request extractor

Extract the entire request:
use axum::{
    extract::Request,
    http::Method,
};

async fn handler(request: Request) -> String {
    format!("Method: {}, URI: {}", request.method(), request.uri())
}
Request is a FromRequest extractor, so it must come last (or second-to-last if using Next in middleware).

FromRequest vs FromRequestParts

Understanding the difference:
Extractors that only need access to the request head (method, URI, headers, extensions):
  • Path
  • Query
  • State
  • HeaderMap
  • Method
  • Uri
Multiple FromRequestParts extractors can be used in any order.
Extractors that consume the request body:
  • Json
  • Form
  • Bytes
  • String
  • Request
Only one FromRequest extractor can be used per handler, and it must come last (except for Next in middleware).

Custom extractors

Create your own extractors by implementing FromRequestParts or FromRequest:
use axum::{
    async_trait,
    extract::FromRequestParts,
    http::{request::Parts, StatusCode},
};

struct ApiToken(String);

#[async_trait]
impl<S> FromRequestParts<S> for ApiToken
where
    S: Send + Sync,
{
    type Rejection = StatusCode;

    async fn from_request_parts(
        parts: &mut Parts,
        _state: &S
    ) -> Result<Self, Self::Rejection> {
        if let Some(auth) = parts.headers.get("authorization") {
            if let Ok(value) = auth.to_str() {
                return Ok(ApiToken(value.to_string()));
            }
        }
        Err(StatusCode::UNAUTHORIZED)
    }
}

// Use in handlers
async fn protected(token: ApiToken) -> String {
    format!("Token: {}", token.0)
}

Derive macros

Use the FromRequest derive macro to create custom extractors more easily:
use axum::{
    extract::{FromRequest, rejection::JsonRejection},
    http::StatusCode,
    response::{IntoResponse, Response},
};

// Custom JSON extractor with better error messages
#[derive(FromRequest)]
#[from_request(via(axum::Json), rejection(AppError))]
struct AppJson<T>(T);

struct AppError(JsonRejection);

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        (StatusCode::BAD_REQUEST, "Invalid JSON").into_response()
    }
}

Next steps

State

Learn how to share state across handlers

Responses

Understand how to return different response types