Crate axum_folder_router

Source
Expand description

§axum_folder_router Macro Documentation

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.1.0"
axum = "0.7"

§Basic Usage

The macro scans a directory for route.rs files and automatically creates an Axum router based on the file structure:

use anyhow;
use axum::Router;
use axum_folder_router::folder_router;
use tokio;

#[derive(Clone, Debug)]
struct AppState {
    _foo: String,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create app state
    let app_state = AppState {
        _foo: "".to_string(),
    };

    // Generate the router using the macro
    let folder_router: Router<AppState> = folder_router!("./examples/simple/api", AppState);

    // 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(())
}

§File Structure Convention

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()
}

pub async fn post() -> impl IntoResponse {
"Posted successfully".into_response()
}

§Supported Features

§HTTP Methods

The macro supports all standard HTTP methods:

  • get
  • post
  • put
  • delete
  • patch
  • head
  • options

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

use axum::{
  extract::State,
  response::IntoResponse
};


pub async fn get(State(state): State<AppState>) -> impl IntoResponse {
    format!("State: {:?}", state)
}

§Limitations

  • Compile-time Only: The routing is determined at compile time, so dynamic route registration isn’t supported.
  • File I/O: The macro performs file I/O during compilation, which may have implications in certain build environments.
  • Single State Type: All routes share the same state type, though you can use FromRef for more granular state extraction.

§Best Practices

  1. Consistent Structure: Maintain a consistent file structure to make your API organization predictable.
  2. Individual Route Files: Use one route.rs file per route path for clarity.
  3. Module Organization: Consider grouping related functionality in directories.
  4. Documentation: Add comments to each route handler explaining its purpose.

Macros§

folder_router
Creates an Axum router by scanning a directory for route.rs files.