Skip to main content
The from_fn module provides a simple way to create middleware from async functions, allowing you to intercept and modify requests before they reach handlers and responses before they’re returned to clients.

Core functions

from_fn

pub fn from_fn<F, T>(f: F) -> FromFnLayer<F, (), T>
Create a middleware from an async function without state access. Requirements:
  1. Must be an async fn
  2. Take zero or more FromRequestParts extractors
  3. Take exactly one FromRequest extractor as the second to last argument
  4. Take Next as the last argument
  5. Return something that implements IntoResponse
Basic example:
use axum::{
    Router,
    routing::get,
    response::Response,
    middleware::{self, Next},
    extract::Request,
};

async fn my_middleware(
    request: Request,
    next: Next,
) -> Response {
    // Process request before handler
    let response = next.run(request).await;
    // Process response after handler
    response
}

let app = Router::new()
    .route("/", get(|| async { /* ... */ }))
    .layer(middleware::from_fn(my_middleware));

from_fn_with_state

pub fn from_fn_with_state<F, S, T>(state: S, f: F) -> FromFnLayer<F, S, T>
Create a middleware from an async function with state access. Example with state:
use axum::{
    Router,
    routing::get,
    response::Response,
    middleware::{self, Next},
    extract::{Request, State},
};

#[derive(Clone)]
struct AppState { /* ... */ }

async fn my_middleware(
    State(state): State<AppState>,
    request: Request,
    next: Next,
) -> Response {
    // Access state in middleware
    let response = next.run(request).await;
    response
}

let state = AppState { /* ... */ };

let app = Router::new()
    .route("/", get(|| async { /* ... */ }))
    .route_layer(middleware::from_fn_with_state(state.clone(), my_middleware))
    .with_state(state);

Request/response handling patterns

Modifying requests

Insert headers or modify the request before it reaches handlers:
use axum::{
    extract::Request,
    middleware::Next,
    response::IntoResponse,
};

async fn insert_header(mut req: Request, next: Next) -> impl IntoResponse {
    req.headers_mut()
        .insert("x-custom-header", "value".parse().unwrap());
    
    next.run(req).await
}

Processing request body

Consume and inspect the request body:
use axum::{
    body::{Body, Bytes},
    extract::Request,
    http::StatusCode,
    middleware::Next,
    response::{IntoResponse, Response},
};
use http_body_util::BodyExt;

async fn print_request_body(
    request: Request,
    next: Next,
) -> Result<impl IntoResponse, Response> {
    let (parts, body) = request.into_parts();
    
    // Collect the body bytes
    let bytes = body
        .collect()
        .await
        .map_err(|err| {
            (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
        })?
        .to_bytes();
    
    // Process the bytes
    tracing::debug!("Request body: {:?}", bytes);
    
    // Reconstruct request with buffered body
    let req = Request::from_parts(parts, Body::from(bytes));
    
    Ok(next.run(req).await)
}

Processing request and response

Handle both request and response in the same middleware:
use axum::{
    body::{Body, Bytes},
    extract::Request,
    http::StatusCode,
    middleware::Next,
    response::{IntoResponse, Response},
};
use http_body_util::BodyExt;

async fn print_request_response(
    req: Request,
    next: Next,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    // Process request
    let (parts, body) = req.into_parts();
    let bytes = body.collect().await
        .map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?
        .to_bytes();
    
    tracing::debug!("request body = {:?}", std::str::from_utf8(&bytes));
    let req = Request::from_parts(parts, Body::from(bytes));
    
    // Call handler
    let res = next.run(req).await;
    
    // Process response
    let (parts, body) = res.into_parts();
    let bytes = body.collect().await
        .map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?
        .to_bytes();
    
    tracing::debug!("response body = {:?}", std::str::from_utf8(&bytes));
    let res = Response::from_parts(parts, Body::from(bytes));
    
    Ok(res)
}

Running extractors

Use extractors to access request data:
use axum::{
    Router,
    extract::Request,
    http::{StatusCode, HeaderMap},
    middleware::{self, Next},
    response::Response,
    routing::get,
};

async fn auth(
    // Run the HeaderMap extractor
    headers: HeaderMap,
    // Request must be the last FromRequest extractor
    request: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    match get_token(&headers) {
        Some(token) if token_is_valid(token) => {
            let response = next.run(request).await;
            Ok(response)
        }
        _ => Err(StatusCode::UNAUTHORIZED),
    }
}

fn get_token(headers: &HeaderMap) -> Option<&str> {
    // Extract token from headers
    None
}

fn token_is_valid(token: &str) -> bool {
    // Validate token
    false
}

let app = Router::new()
    .route("/", get(|| async { /* ... */ }))
    .route_layer(middleware::from_fn(auth));

Passing data to handlers

Use request extensions to pass data from middleware to handlers:
use axum::{
    Router,
    http::StatusCode,
    routing::get,
    response::{IntoResponse, Response},
    middleware::{self, Next},
    extract::{Request, Extension},
};

#[derive(Clone)]
struct CurrentUser { /* ... */ }

async fn auth(mut req: Request, next: Next) -> Result<Response, StatusCode> {
    let auth_header = req.headers()
        .get(http::header::AUTHORIZATION)
        .and_then(|header| header.to_str().ok());
    
    let auth_header = if let Some(auth_header) = auth_header {
        auth_header
    } else {
        return Err(StatusCode::UNAUTHORIZED);
    };
    
    if let Some(current_user) = authorize_current_user(auth_header).await {
        // Insert the current user into request extensions
        req.extensions_mut().insert(current_user);
        Ok(next.run(req).await)
    } else {
        Err(StatusCode::UNAUTHORIZED)
    }
}

async fn authorize_current_user(auth_token: &str) -> Option<CurrentUser> {
    // Validate and return user
    None
}

async fn handler(
    // Extract the current user set by middleware
    Extension(current_user): Extension<CurrentUser>,
) {
    // Use current_user
}

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

Tracking metrics

Implement request tracking and metrics:
use axum::{
    extract::{MatchedPath, Request},
    middleware::Next,
    response::IntoResponse,
};
use std::time::Instant;

async fn track_metrics(req: Request, next: Next) -> impl IntoResponse {
    let start = Instant::now();
    let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() {
        matched_path.as_str().to_owned()
    } else {
        req.uri().path().to_owned()
    };
    let method = req.method().clone();
    
    let response = next.run(req).await;
    
    let latency = start.elapsed().as_secs_f64();
    let status = response.status().as_u16().to_string();
    
    let labels = [
        ("method", method.to_string()),
        ("path", path),
        ("status", status),
    ];
    
    metrics::counter!("http_requests_total", &labels).increment(1);
    metrics::histogram!("http_requests_duration_seconds", &labels).record(latency);
    
    response
}

The Next type

pub struct Next
Represents the remainder of the middleware stack, including the handler.

Methods

run

pub async fn run(mut self, req: Request) -> Response
Execute the remaining middleware stack and return the response.

Types

FromFnLayer

pub struct FromFnLayer<F, S, T>
A tower::Layer created from an async function. Created with from_fn or from_fn_with_state.

FromFn

pub struct FromFn<F, S, I, T>
The middleware service created from an async function.

ResponseFuture

pub struct ResponseFuture
The future type returned by FromFn.

See also