Crate axum_folder_router

Source
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

§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.