Routing is the process of matching incoming HTTP requests to handler functions based on the request’s path and method. Axum provides a powerful and flexible routing system through the Router type.
Creating a router
Create a new router using Router::new():
use axum :: Router ;
let app = Router :: new ();
A newly created router will respond with 404 Not Found to all requests until you add routes.
Adding routes
Use the route method to add routes to your router. Each route consists of a path pattern and a MethodRouter that defines which HTTP methods are handled:
use axum :: { Router , routing :: get};
let app = Router :: new ()
. route ( "/" , get ( || async { "Hello, World!" }));
HTTP method routing
Axum provides convenience functions for each HTTP method:
GET
POST
PUT/PATCH
DELETE
use axum :: { Router , routing :: get};
let app = Router :: new ()
. route ( "/users" , get ( list_users ));
async fn list_users () -> & ' static str {
"User list"
}
GET routes also handle HEAD requests automatically, but with the response body removed.
use axum :: { Router , routing :: post};
let app = Router :: new ()
. route ( "/users" , post ( create_user ));
async fn create_user () -> & ' static str {
"User created"
}
use axum :: { Router , routing :: {put, patch}};
let app = Router :: new ()
. route ( "/users/:id" , put ( update_user ))
. route ( "/users/:id" , patch ( partial_update ));
async fn update_user () -> & ' static str {
"User updated"
}
async fn partial_update () -> & ' static str {
"User partially updated"
}
use axum :: { Router , routing :: delete};
let app = Router :: new ()
. route ( "/users/:id" , delete ( delete_user ));
async fn delete_user () -> & ' static str {
"User deleted"
}
Path patterns
Axum supports dynamic path segments using named parameters:
use axum :: { Router , routing :: get, extract :: Path };
// Single parameter
let app = Router :: new ()
. route ( "/users/:id" , get ( get_user ));
async fn get_user ( Path ( id ) : Path < String >) -> String {
format! ( "User ID: {}" , id )
}
// Multiple parameters
let app = Router :: new ()
. route ( "/users/:user_id/team/:team_id" , get ( get_team ));
async fn get_team ( Path (( user_id , team_id )) : Path <( String , String )>) -> String {
format! ( "User {} in team {}" , user_id , team_id )
}
Wildcard captures
Capture the rest of the path with wildcard parameters:
use axum :: { Router , routing :: get, extract :: Path };
let app = Router :: new ()
. route ( "/files/*path" , get ( serve_file ));
async fn serve_file ( Path ( path ) : Path < String >) -> String {
format! ( "Serving file: {}" , path )
}
Combining routes
Chaining routes
Chain multiple route calls to add multiple routes:
use axum :: { Router , routing :: {get, post, delete}};
let app = Router :: new ()
. route ( "/" , get ( root ))
. route ( "/users" , get ( list_users ) . post ( create_user ))
. route ( "/users/:id" , get ( get_user ) . delete ( delete_user ));
Merging routers
Combine multiple routers with merge:
use axum :: { Router , routing :: get};
let user_routes = Router :: new ()
. route ( "/users" , get ( list_users ))
. route ( "/users/:id" , get ( get_user ));
let post_routes = Router :: new ()
. route ( "/posts" , get ( list_posts ))
. route ( "/posts/:id" , get ( get_post ));
let app = Router :: new ()
. merge ( user_routes )
. merge ( post_routes );
Nesting routers
Nest a router under a path prefix:
use axum :: { Router , routing :: get};
let api_routes = Router :: new ()
. route ( "/users" , get ( list_users ))
. route ( "/posts" , get ( list_posts ));
let app = Router :: new ()
. nest ( "/api" , api_routes );
// Routes are now available at:
// - /api/users
// - /api/posts
Nesting at the root ("/") is not supported. Use merge instead.
Fallback handlers
Handle requests that don’t match any route:
use axum :: { Router , routing :: get, http :: StatusCode };
let app = Router :: new ()
. route ( "/" , get ( || async { "Home" }))
. fallback ( || async { ( StatusCode :: NOT_FOUND , "Not found" ) });
Route services
For more control, you can route to any Tower Service:
use axum :: { Router , routing :: get_service};
use tower :: service_fn;
use http :: Response ;
use std :: convert :: Infallible ;
let service = tower :: service_fn ( | _req | async {
Ok :: < _ , Infallible >( Response :: new ( axum :: body :: Body :: empty ()))
});
let app = Router :: new ()
. route ( "/" , get_service ( service ));
Placeholder handlers
Understanding placeholder handlers
You can pass types that implement IntoResponse directly as handlers: use axum :: { Router , routing :: get, http :: StatusCode , Json };
use serde_json :: json;
let app = Router :: new ()
// Return a fixed string
. route ( "/" , get ( "Hello, World!" ))
// Return mock data
. route ( "/users" , get ((
StatusCode :: OK ,
Json ( json! ({ "id" : 1 , "name" : "alice" })),
)));
Next steps
Handlers Learn about handler functions and their signatures
State Share state across handlers using with_state