Nest a [Router
] at some path.
This allows you to break your application into smaller pieces and compose them together.
use axum::{ routing::{get, post}, Router, }; let user_routes = Router::new().route("/:id", get(|| async {})); let team_routes = Router::new().route("/", post(|| async {})); let api_routes = Router::new() .nest("/users", user_routes) .nest("/teams", team_routes); let app = Router::new().nest("/api", api_routes); // Our app now accepts // - GET /api/users/:id // - POST /api/teams # async { # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); # };
Note that nested routes will not see the original request URI but instead have the matched prefix stripped. This is necessary for services like static file serving to work. Use OriginalUri
if you need the original request URI.
Take care when using nest
together with dynamic routes as nesting also captures from the outer routes:
use axum::{ extract::Path, routing::get, Router, }; use std::collections::HashMap; async fn users_get(Path(params): Path<HashMap<String, String>>) { // Both `version` and `id` were captured even though `users_api` only // explicitly captures `id`. let version = params.get("version"); let id = params.get("id"); } let users_api = Router::new().route("/users/:id", get(users_get)); let app = Router::new().nest("/:version/api", users_api); # async { # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); # };
Nested routes are similar to wildcard routes. The difference is that wildcard routes still see the whole URI whereas nested routes will have the prefix stripped:
use axum::{routing::get, http::Uri, Router}; let nested_router = Router::new() .route("/", get(|uri: Uri| async { // `uri` will _not_ contain `/bar` })); let app = Router::new() .route("/foo/*rest", get(|uri: Uri| async { // `uri` will contain `/foo` })) .nest("/bar", nested_router); # async { # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); # };
If a nested router doesn't have its own fallback then it will inherit the fallback from the outer router:
use axum::{routing::get, http::StatusCode, handler::Handler, Router}; async fn fallback() -> (StatusCode, &'static str) { (StatusCode::NOT_FOUND, "Not Found") } let api_routes = Router::new().route("/users", get(|| async {})); let app = Router::new() .nest("/api", api_routes) .fallback(fallback); # let _: Router = app;
Here requests like GET /api/not-found
will go into api_routes
but because it doesn‘t have a matching route and doesn’t have its own fallback it will call the fallback from the outer router, i.e. the fallback
function.
If the nested router has its own fallback then the outer fallback will not be inherited:
use axum::{ routing::get, http::StatusCode, handler::Handler, Json, Router, }; async fn fallback() -> (StatusCode, &'static str) { (StatusCode::NOT_FOUND, "Not Found") } async fn api_fallback() -> (StatusCode, Json<serde_json::Value>) { ( StatusCode::NOT_FOUND, Json(serde_json::json!({ "status": "Not Found" })), ) } let api_routes = Router::new() .route("/users", get(|| async {})) .fallback(api_fallback); let app = Router::new() .nest("/api", api_routes) .fallback(fallback); # let _: Router = app;
Here requests like GET /api/not-found
will go to api_fallback
.
When combining [Router
]s with this method, each [Router
] must have the same type of state. If your routers have different types you can use [Router::with_state
] to provide the state and make the types match:
use axum::{ Router, routing::get, extract::State, }; #[derive(Clone)] struct InnerState {} #[derive(Clone)] struct OuterState {} async fn inner_handler(state: State<InnerState>) {} let inner_router = Router::new() .route("/bar", get(inner_handler)) .with_state(InnerState {}); async fn outer_handler(state: State<OuterState>) {} let app = Router::new() .route("/", get(outer_handler)) .nest("/foo", inner_router) .with_state(OuterState {}); # let _: axum::Router = app;
Note that the inner router will still inherit the fallback from the outer router.
Router::route
] for more details.*
).path
is empty.