Expand description
folder_router
is a procedural macro for the Axum web framework
that automatically generates router boilerplate based on your file
structure. It simplifies route organization by using filesystem conventions
to define your API routes.
§Installation
Add the dependency to your Cargo.toml
:
[dependencies]
axum_folder_router = "0.3"
axum = "0.8"
See Avoiding Cache Issues on how to fix cargos
caching, which may cause new route.rs
files to be ignored.
§Crate Features
- nightly -
Enables use of unstable
track_path
feature to avoid cache issues. - debug - Adds some debug logging
§Basic Usage
The macro scans a directory for route.rs
files and automatically
creates an Axum router based on the file structure:
use axum::Router;
use axum_folder_router::folder_router;
#[derive(Clone)]
struct AppState;
// Imports route.rs files & generates an ::into_router() fn
#[folder_router("./examples/simple/api", AppState)]
struct MyFolderRouter();
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Create app state
let app_state = AppState;
// Use the init fn generated above
let folder_router: Router<AppState> = MyFolderRouter::into_router();
// Build the router and provide the state
let app: Router<()> = folder_router.with_state(app_state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("Listening on http://{}", listener.local_addr()?);
axum::serve(listener, app).await?;
Ok(())
}
§Folder Structure
The macro converts your file structure into routes:
src/api/
├── route.rs -> "/"
├── hello/
│ └── route.rs -> "/hello"
├── users/
│ ├── route.rs -> "/users"
│ └── [id]/
│ └── route.rs -> "/users/{id}"
└── files/
└── [...path]/
└── route.rs -> "/files/\*path"
Each route.rs
file can contain HTTP method handlers that are automatically mapped to the corresponding route.
§Route Handlers
Inside each route.rs
file, define async functions named after HTTP methods:
use axum::response::{Html, IntoResponse};
pub async fn get() -> impl IntoResponse {
Html("<h1>Hello World!</h1>").into_response()
}
§Detailed Usage
§HTTP Methods
The macro supports all standard HTTP methods as defined in RFC9110.
get
post
put
delete
patch
head
options
trace
connect
And additionally
any
, which matches all methods
§Path Parameters
Dynamic path segments are defined using brackets:
src/api/users/[id]/route.rs -> "/users/{id}"
Inside the route handler:
use axum::{
extract::Path,
response::IntoResponse
};
pub async fn get(Path(id): Path<String>) -> impl IntoResponse {
format!("User ID: {}", id)
}
§Catch-all Parameters
Use the spread syntax for catch-all segments:
src/api/files/[...path]/route.rs -> "/files/\*path"
use axum::{
extract::Path,
response::IntoResponse
};
pub async fn get(Path(path): Path<String>) -> impl IntoResponse {
format!("Requested file path: {}", path)
}
§State Extraction
The state type provided to the macro is available in all route handlers:
All routes share the same state type, though you can use FromRef
for more granular state extraction.
use axum::{
extract::State,
response::IntoResponse
};
pub async fn get(State(state): State<AppState>) -> impl IntoResponse {
format!("State: {:?}", state)
}
§Avoiding Cache Issues
By default newly created route.rs files may be ignored due to cargo’s build-in caching.
§Nightly Rust
If you’re using a nightly toolchain, just enable the nightly
feature.
[dependencies]
axum_folder_router = { version = "0.3", features = ["nightly"] }
This enables us to use the unstable track_path
API to tell cargo to watch for changes in your route directories.
§Stable Rust (requires build.rs
)
On stable, you’ll need to add this build.rs
to your project root:
fn main() {
// Watch routes folder, so it picks up new routes
println!(
"cargo:rerun-if-changed={routes_folder}",
routes_folder = "my/routes" // Replace with your actual routes dir
);
}
Attribute Macros§
- folder_
router - Creates an Axum router module tree & creation function
by scanning a directory for
route.rs
files.