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.
Middleware allows you to process requests before they reach handlers and modify responses before they’re sent to clients. Axum supports both custom middleware and Tower middleware.
Middleware with from_fn
The simplest way to create middleware is using middleware::from_fn:
use axum :: {
Router ,
routing :: get,
middleware :: { self , Next },
extract :: Request ,
response :: Response ,
};
async fn my_middleware (
request : Request ,
next : Next ,
) -> Response {
// Do something with the request...
println! ( "Request: {} {}" , request . method (), request . uri ());
// Call the next middleware or handler
let response = next . run ( request ) . await ;
// Do something with the response...
println! ( "Response status: {}" , response . status ());
response
}
let app = Router :: new ()
. route ( "/" , get ( || async { "Hello, World!" }))
. layer ( middleware :: from_fn ( my_middleware ));
Middleware signature
Middleware functions created with from_fn must:
Be an async function
The middleware must be an async fn.
Take extractors (optional)
Can take zero or more FromRequestParts extractors before the request. use axum :: {
extract :: Request ,
http :: HeaderMap ,
middleware :: Next ,
response :: Response ,
};
async fn middleware (
headers : HeaderMap , // Extractor
request : Request ,
next : Next ,
) -> Response {
next . run ( request ) . await
}
Take Request
Must take exactly one Request as second-to-last argument.
Take Next
Must take Next as the last argument.
Return IntoResponse
Must return something that implements IntoResponse.
Extract data from requests in middleware:
use axum :: {
Router ,
routing :: get,
middleware :: { self , Next },
extract :: Request ,
response :: Response ,
http :: { HeaderMap , StatusCode },
};
async fn auth_middleware (
headers : HeaderMap ,
request : Request ,
next : Next ,
) -> Result < Response , StatusCode > {
// Check authorization header
match headers . get ( "authorization" ) {
Some ( token ) if is_valid_token ( token ) => {
Ok ( next . run ( request ) . await )
}
_ => Err ( StatusCode :: UNAUTHORIZED ),
}
}
fn is_valid_token ( _token : & axum :: http :: HeaderValue ) -> bool {
true // Simplified validation
}
let app = Router :: new ()
. route ( "/protected" , get ( || async { "Secret data" }))
. layer ( middleware :: from_fn ( auth_middleware ));
Middleware with state
Access application state in middleware using from_fn_with_state:
use axum :: {
Router ,
routing :: get,
extract :: { State , Request },
middleware :: { self , Next },
response :: Response ,
http :: StatusCode ,
};
#[derive( Clone )]
struct AppState {
api_key : String ,
}
async fn require_api_key (
State ( state ) : State < AppState >,
request : Request ,
next : Next ,
) -> Result < Response , StatusCode > {
let provided_key = request
. headers ()
. get ( "x-api-key" )
. and_then ( | v | v . to_str () . ok ());
if provided_key == Some ( & state . api_key) {
Ok ( next . run ( request ) . await )
} else {
Err ( StatusCode :: UNAUTHORIZED )
}
}
let state = AppState {
api_key : "secret-key" . to_string (),
};
let app = Router :: new ()
. route ( "/" , get ( || async { "Protected" }))
. layer ( middleware :: from_fn_with_state ( state . clone (), require_api_key ))
. with_state ( state );
Modifying requests and responses
Modify request
Modify response
Read request body
Transform the request before it reaches handlers: use axum :: {
extract :: Request ,
middleware :: Next ,
response :: Response ,
http :: header,
};
async fn add_request_id (
mut request : Request ,
next : Next ,
) -> Response {
// Add a custom header to the request
let request_id = uuid :: Uuid :: new_v4 () . to_string ();
request . headers_mut () . insert (
header :: HeaderName :: from_static ( "x-request-id" ),
request_id . parse () . unwrap (),
);
next . run ( request ) . await
}
Transform the response before sending to client: use axum :: {
extract :: Request ,
middleware :: Next ,
response :: Response ,
http :: header,
};
async fn add_security_headers (
request : Request ,
next : Next ,
) -> Response {
let mut response = next . run ( request ) . await ;
// Add security headers
let headers = response . headers_mut ();
headers . insert (
header :: HeaderName :: from_static ( "x-content-type-options" ),
"nosniff" . parse () . unwrap (),
);
headers . insert (
header :: HeaderName :: from_static ( "x-frame-options" ),
"DENY" . parse () . unwrap (),
);
response
}
Buffer and inspect the request body: use axum :: {
body :: { Body , Bytes },
extract :: Request ,
middleware :: Next ,
response :: { Response , IntoResponse },
http :: StatusCode ,
};
use http_body_util :: BodyExt ;
async fn print_request_body (
request : Request ,
next : Next ,
) -> Result < Response , StatusCode > {
let ( parts , body ) = request . into_parts ();
// Buffer the body
let bytes = body
. collect ()
. await
. map_err ( | _ | StatusCode :: INTERNAL_SERVER_ERROR ) ?
. to_bytes ();
// Log the body
if let Ok ( body_str ) = std :: str :: from_utf8 ( & bytes ) {
println! ( "Request body: {}" , body_str );
}
// Reconstruct the request with the buffered body
let request = Request :: from_parts ( parts , Body :: from ( bytes ));
Ok ( next . run ( request ) . await )
}
Layer ordering
Middleware layers are applied in reverse order. The last layer added is executed first.
use axum :: {
Router ,
routing :: get,
middleware :: { self , Next },
extract :: Request ,
response :: Response ,
};
async fn first_middleware ( request : Request , next : Next ) -> Response {
println! ( "First: before" );
let response = next . run ( request ) . await ;
println! ( "First: after" );
response
}
async fn second_middleware ( request : Request , next : Next ) -> Response {
println! ( "Second: before" );
let response = next . run ( request ) . await ;
println! ( "Second: after" );
response
}
let app = Router :: new ()
. route ( "/" , get ( || async { "Handler" }))
. layer ( middleware :: from_fn ( first_middleware ))
. layer ( middleware :: from_fn ( second_middleware ));
// Execution order:
// 1. Second: before
// 2. First: before
// 3. Handler
// 4. First: after
// 5. Second: after
Tower middleware
Use any Tower middleware with Axum:
use axum :: Router ;
use tower :: ServiceBuilder ;
use tower_http :: {
trace :: TraceLayer ,
compression :: CompressionLayer ,
timeout :: TimeoutLayer ,
};
use std :: time :: Duration ;
let app = Router :: new ()
. layer (
ServiceBuilder :: new ()
. layer ( TraceLayer :: new_for_http ())
. layer ( CompressionLayer :: new ())
. layer ( TimeoutLayer :: new ( Duration :: from_secs ( 10 )))
);
Route-specific middleware
Apply middleware to specific routes:
use axum :: {
Router ,
routing :: get,
middleware,
};
async fn admin_only (
request : axum :: extract :: Request ,
next : axum :: middleware :: Next ,
) -> axum :: response :: Response {
// Check admin permissions...
next . run ( request ) . await
}
let app = Router :: new ()
. route ( "/public" , get ( || async { "Public" }))
. route (
"/admin" ,
get ( || async { "Admin" })
. layer ( middleware :: from_fn ( admin_only ))
);
Alternatively, use route_layer:
use axum :: {
Router ,
routing :: get,
middleware,
};
let app = Router :: new ()
. route ( "/admin/users" , get ( || async { "Users" }))
. route ( "/admin/settings" , get ( || async { "Settings" }))
. route_layer ( middleware :: from_fn ( admin_only ))
. route ( "/public" , get ( || async { "Public" }));
async fn admin_only (
request : axum :: extract :: Request ,
next : axum :: middleware :: Next ,
) -> axum :: response :: Response {
next . run ( request ) . await
}
// admin_only only applies to routes added before route_layer
Common middleware patterns
use axum :: {
extract :: Request ,
middleware :: Next ,
response :: Response ,
};
async fn logging_middleware (
request : Request ,
next : Next ,
) -> Response {
let method = request . method () . clone ();
let uri = request . uri () . clone ();
let start = std :: time :: Instant :: now ();
let response = next . run ( request ) . await ;
let duration = start . elapsed ();
println! (
"{} {} - {} ({:?})" ,
method ,
uri ,
response . status (),
duration
);
response
}
Error handling in middleware
Handle middleware errors appropriately:
use axum :: {
extract :: Request ,
middleware :: Next ,
response :: { Response , IntoResponse },
http :: StatusCode ,
};
async fn fallible_middleware (
request : Request ,
next : Next ,
) -> Result < Response , StatusCode > {
// Validate something that might fail
if ! is_valid ( & request ) {
return Err ( StatusCode :: BAD_REQUEST );
}
Ok ( next . run ( request ) . await )
}
fn is_valid ( _request : & Request ) -> bool {
true
}
Next steps
Error Handling Learn how to handle errors from middleware
State Use state in your middleware