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:
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 )
}
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" )
}
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:
Strings and static str
Status codes
JSON
Result types
async fn handler () -> & ' static str {
"Hello, World!"
}
async fn dynamic_handler () -> String {
format! ( "Time: {}" , std :: time :: SystemTime :: now ())
}
use axum :: http :: StatusCode ;
async fn no_content () -> StatusCode {
StatusCode :: NO_CONTENT
}
async fn with_body () -> ( StatusCode , & ' static str ) {
( StatusCode :: CREATED , "Resource created" )
}
use axum :: Json ;
use serde :: Serialize ;
#[derive( Serialize )]
struct User {
id : u64 ,
name : String ,
}
async fn get_user () -> Json < User > {
Json ( User {
id : 1 ,
name : "Alice" . to_string (),
})
}
use axum :: http :: StatusCode ;
async fn may_fail () -> Result < String , StatusCode > {
if some_condition () {
Ok ( "Success" . to_string ())
} else {
Err ( StatusCode :: INTERNAL_SERVER_ERROR )
}
}
fn some_condition () -> bool { true }
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