| Crates.io | dynami |
| lib.rs | dynami |
| version | 0.1.0 |
| created_at | 2026-01-07 06:24:10.503668+00 |
| updated_at | 2026-01-07 06:24:10.503668+00 |
| description | Automatic Axum router generation from directory structure with file-system based routing |
| homepage | https://github.com/ErdemGKSL/dynami |
| repository | https://github.com/ErdemGKSL/dynami |
| max_upload_size | |
| id | 2027611 |
| size | 95,187 |
A Rust library for automatic Axum router generation from directory structure. Generate type-safe routers for Axum 0.8+ by organizing your route handlers into a folder hierarchy.
d_ prefix for path parameters (e.g., d_id -> /{id})d_*rest -> /{*rest})Add to your Cargo.toml:
[dependencies]
dynami = "0.1"
[build-dependencies]
dynami = "0.1"
src/routes/
├── mod.rs # Root router (optional AppState here)
├── get.rs # GET /
├── post.rs # POST /
└── api/
├── mod.rs # API router
├── get.rs # GET /api
└── d_id/
├── mod.rs # Dynamic router
└── get.rs # GET /api/{id}
use dynami::format_routes;
fn main() {
format_routes("./src/routes").unwrap();
println!("cargo:rerun-if-changed=src/routes");
}
cargo build
The library will generate:
router() functions in each mod.rspub mod declarations for subdirectoriesmod routes;
#[tokio::main]
async fn main() {
let app = routes::router();
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
dynami::generate!The library uses the dynami::generate! macro to mark generated code blocks:
use axum::Router;
use axum::routing::get;
pub fn router() -> Router {
let mut router = Router::new();
// You can add custom routes here - they won't be overwritten
router = router.route("/custom", get(custom_handler));
dynami::generate! {
// This block is auto-generated - don't edit manually
router = router.route("/", get(get::handler));
router = router.nest("/api", api::router());
}
router
}
Key benefits:
use axum::routing::{get, post} only if both are used)dynami::generate!, the file won't be updated anymoreCreate files named after HTTP methods:
get.rs -> GET handlerpost.rs -> POST handlerput.rs -> PUT handlerdelete.rs -> DELETE handlerpatch.rs -> PATCH handleroptions.rs -> OPTIONS handlerhead.rs -> HEAD handlerUse the d_ prefix for path parameters:
d_id/ -> /{id} routed_user_id/ -> /{user_id} routed_*rest/ -> /{*rest} wildcard route (captures remaining path)Add your state struct to the root mod.rs:
#[derive(Clone)]
pub struct AppState {
pub db: Database,
}
pub fn router() -> Router<AppState> {
let mut router = Router::new();
dynami::generate! {
router = router.route("/", get(get::handler));
}
router
}
The library will:
AppState struct (or any struct with "State" in its name)Router<AppState> for all routersState extractors in default handlersroutes/
├── mod.rs
├── get.rs # GET / - list all
└── post.rs # POST / - create new
Generates:
use axum::Router;
use axum::routing::{get, post};
pub fn router() -> Router {
let mut router = Router::new();
dynami::generate! {
router = router.route("/", get(get::handler).post(post::handler));
}
router
}
routes/
├── mod.rs
├── get.rs
└── api/
├── mod.rs
├── get.rs
└── users/
├── mod.rs
└── get.rs
Generates in routes/mod.rs:
pub mod api;
use axum::Router;
use axum::routing::get;
pub fn router() -> Router {
let mut router = Router::new();
dynami::generate! {
router = router.route("/", get(get::handler));
router = router.nest("/api", api::router());
}
router
}
// routes/mod.rs
use axum::Router;
use axum::routing::get;
pub mod api;
// Custom handler defined locally
async fn health_check() -> &'static str {
"OK"
}
pub fn router() -> Router {
let mut router = Router::new();
// Manual route - generator will detect this and skip generating GET /
router = router.route("/health", get(health_check));
dynami::generate! {
// Only routes not already defined above will be generated
router = router.nest("/api", api::router());
}
router
}
// routes/mod.rs
use sqlx::PgPool;
#[derive(Clone)]
pub struct AppState {
pub pool: PgPool,
}
pub fn router() -> Router<AppState> {
let mut router = Router::new();
dynami::generate! {
router = router.route("/", get(get::handler));
}
router
}
// routes/get.rs
use axum::extract::State;
use axum::response::IntoResponse;
use super::AppState;
pub async fn handler(State(state): State<AppState>) -> impl IntoResponse {
// Use state.pool here
"Hello World"
}
Empty method files get default handlers:
// Without state
use axum::response::IntoResponse;
pub async fn handler() -> impl IntoResponse {
"OK"
}
// With state (when AppState is detected)
use axum::extract::State;
use axum::response::IntoResponse;
pub async fn handler(State(state): State<AppState>) -> impl IntoResponse {
"OK"
}
dynami::generate! block - If you remove it, the file won't be updatedd_* prefix (e.g., d_*rest)This project is open source and available under the MIT License.
Contributions are welcome! Please feel free to submit a Pull Request.