Routing

Ferro provides an expressive routing API similar to Laravel.

Basic Routes

Define routes in src/routes.rs:

#![allow(unused)]
fn main() {
use ferro::*;

pub fn routes() -> Router {
    Router::new()
        .get("/", home)
        .get("/users", users::index)
        .post("/users", users::store)
        .put("/users/:id", users::update)
        .delete("/users/:id", users::destroy)
}
}

Route Parameters

Parameters are extracted from the URL path:

#![allow(unused)]
fn main() {
#[handler]
pub async fn show(req: Request, id: i64) -> Response {
    // id is extracted from /users/:id
    let user = User::find_by_id(id).one(&db).await?;
    Ok(json!(user))
}
}

Optional Parameters

Use Option<T> for optional parameters:

#![allow(unused)]
fn main() {
#[handler]
pub async fn index(req: Request, page: Option<i64>) -> Response {
    let page = page.unwrap_or(1);
    // ...
}
}

Route Groups

Group routes with shared middleware or prefixes:

#![allow(unused)]
fn main() {
pub fn routes() -> Router {
    Router::new()
        .group("/api", |group| {
            group
                .get("/users", api::users::index)
                .post("/users", api::users::store)
                .middleware(ApiAuthMiddleware)
        })
        .group("/admin", |group| {
            group
                .get("/dashboard", admin::dashboard)
                .middleware(AdminMiddleware)
        })
}
}

Named Routes

#![allow(unused)]
fn main() {
Router::new()
    .get("/users/:id", users::show).name("users.show")
}

Generate URLs:

#![allow(unused)]
fn main() {
let url = route("users.show", [("id", "1")]);
// => "/users/1"
}

Resource Routes

Generate CRUD routes for a resource:

#![allow(unused)]
fn main() {
Router::new()
    .resource("/users", users_controller)
}

This creates:

MethodURIHandler
GET/usersindex
GET/users/createcreate
POST/usersstore
GET/users/:idshow
GET/users/:id/editedit
PUT/users/:idupdate
DELETE/users/:iddestroy

Route Middleware

Apply middleware to specific routes:

#![allow(unused)]
fn main() {
Router::new()
    .get("/dashboard", dashboard)
    .middleware(AuthMiddleware)
}

Or to groups:

#![allow(unused)]
fn main() {
Router::new()
    .group("/admin", |group| {
        group
            .get("/users", admin::users)
            .middleware(AdminMiddleware)
    })
}

Fallback Routes

Handle 404s:

#![allow(unused)]
fn main() {
Router::new()
    .get("/", home)
    .fallback(not_found)
}

Route Constraints

Validate route parameters:

#![allow(unused)]
fn main() {
Router::new()
    .get("/users/:id", users::show)
    .where_("id", r"\d+")  // Must be numeric
}

API Routes

For JSON APIs, typically group under /api:

#![allow(unused)]
fn main() {
Router::new()
    .group("/api/v1", |api| {
        api
            .get("/users", api::users::index)
            .post("/users", api::users::store)
            .middleware(ApiAuthMiddleware)
    })
}

View Routes

For simple views without controller logic:

#![allow(unused)]
fn main() {
Router::new()
    .view("/about", "About", AboutProps::default())
}

Redirect Routes

#![allow(unused)]
fn main() {
Router::new()
    .redirect("/old-path", "/new-path")
    .redirect_permanent("/legacy", "/modern")
}

Route Caching

In production, routes are compiled at build time for optimal performance.