Expand description
§Axum Routes
Create an axum::Router from an enum.
You can then use this enum to resolve the routes and avoid hardcoding
routes in your project.
#[routes]
enum RoutesUsers {
#[post("/", handler = create_user)]
CreateUser,
#[get("/{id}", handler = get_user)]
GetByID,
#[put("/{id}", handler = edit_user)]
EditUser,
#[delete("/{id}", handler = delete_user)]
DeleteByID,
#[get("/other/{id}", handler = get_other_resource)]
GetOtherResourceByID,
}
#[routes]
enum Routes {
#[nest("/users")]
Users(RoutesUsers),
#[get("/", handler = main)]
Main,
}
async fn create_user() {} // axum handler
async fn get_user() {}
async fn edit_user() {}
async fn delete_user() {}
async fn get_other_resource() {}The route path (ie “/{id}”) is exactly what axum supports (underneath
it uses the matchit crate). Newer version of axum supports the
{parameter} format instead of the old :parameter format.
§Resolving routes
You want to avoid as much as possible to hardcode your routes when developping a web app. It is so easy to put a typo in it, and you end up with broken links in your app.
You can use the resolve! macro to generate your routes.
You have compile-time validation of the types of parameters you pass
(any type that implements the ToString trait), and run-time
validation of the number of parameters you pass (resolve! returns an error
if the number of parameters is not valid).
Here, we resolve a nested route with one parameter:
let resolved = axum_routes::resolve!(Routes::Users(RoutesUsers::GetByID), 42).expect("should not fail");
assert_eq!("/users/42", resolved);This will resolve the whole path, starting at the root node (Users enum), to the GetByID route. But you can also use the nested route alone:
let resolved = axum_routes::resolve!(RoutesUsers::GetByID, 42).expect("should not fail");
assert_eq!("/42", resolved);If the final route has multiple parameters, and that you resolve the whole route, you need to pass all parameters, in the right order:
let user_id = 42;
let resource_id = 21;
let resolved = axum_routes::resolve!(Routes::Users(RoutesUsers::GetOtherResourceByID), user_id, resource_id).expect("should not fail");
assert_eq!("/users/42/other/21", resolved);If you pass the wrong number of arguments, the macro will return an error:
let resolved = axum_routes::resolve!(RoutesUsers::GetByID, 42, 21).expect("this will fail");§Create the axum::Router
The router! macro lets you create and customize the axum::Router from
your defined enums.
First, let’s define our router as an enum:
#[routes]
enum MyRoutes {
#[get("/admin", handler = admin_handler)]
ProtectedAdmin,
#[get("/assets/{asset}", handler = assets)]
Assets,
}
fn main() {
let router: axum::Router = axum_routes::router!(MyRoutes);
// .. serve the Router
}§Applying layer, route_layer, fallback, with_state, ..
Now, we can modify our enum to let axum-routes know that we want to customize
the routes, like applying layers, setting the fallback handler, add a state with
with_state. (See documentation of MethodRouter).
#[routes]
enum MyRoutes {
#[get("/admin", handler = admin_handler, customize = protected_admin)]
ProtectedAdmin,
#[get("/assets/{asset}", handler = assets, customize = assets_customize)]
Assets,
}You can see that we use the customize identifier for the ProtectedAdmin
and Assets variants.
So, when we generate the axum::Router associated with the MyRoutes enum,
we use the router! macro and pass the customizers to it.
let router = axum_routes::router!(
self::MyRoutes,
protected_admin = ${
// We do have a `protected_layer` that we will share using Arc
let layer = Arc::clone(protected_layer);
move |route| {
route.layer(layer)
}
},
assets_customize = $|route| {
// For example, apply a rate limiter layer (must be declared above)
route.layer(my_rate_limiter_layer)
},
);Note the $ before the expression that return the closure, it is used
to tell axum-routes that this will customize a MethodRouter.
This also works when nesting routers:
#[routes]
enum MyRoutes {
#[nest("/nested", customize = custom_nested)]
Nested(OtherRoutes),
}
#[routes]
enum OtherRoutes {
#[get("/test", handler = handler)]
Test,
}
fn main() {
let router = axum_routes::router!(
self::MyRoutes,
custom_nested = #|router| {
router.with_state(State::new())
.fallback(fallback_handler)
.layer(ratelimiter)
},
);
}Here we are customizing a axum::Router, so we have to use the #.
Macros§
- resolve
- Resolve a route.
- router
- Create the
axum::Routerinstance. The first parameter of the macro must be the path to a type that was created from theroutesmacro.
Attribute Macros§
- routes
- The main macro to create an
axum::Routerfrom an enum.