Routing

Rejoice uses file-based routing. Every .rs file in src/routes/ becomes a route automatically.

File-to-URL Mapping

File PathURL
src/routes/index.rs/
src/routes/about.rs/about
src/routes/contact.rs/contact
src/routes/blog/index.rs/blog
src/routes/blog/post.rs/blog/post
src/routes/users/[id].rs/users/:id

Naming Convention

File names with underscores are converted to hyphens in URLs:

FileURL
src/routes/about_us.rs/about-us
src/routes/contact_form.rs/contact-form

Basic Route

Route files export functions named after HTTP methods like get or post:

src/routes/index.rs
use rejoice::{Req, Res, html};

pub async fn get(req: Req, res: Res) -> Res {
    res.html(html! {
        h1 { "Hello, World!" }
    })
}

Index Routes

Files named index.rs handle the directory's root path:

  • src/routes/index.rs/
  • src/routes/blog/index.rs/blog
  • src/routes/users/index.rs/users

Dynamic Routes

Use square brackets for dynamic path segments in file names or directory names.

Dynamic Files

src/routes/users/[id].rs handles /users/:id:

src/routes/users/[id].rs
use rejoice::{Req, Res, html};

pub async fn get(req: Req, res: Res, id: String) -> Res {
    res.html(html! {
        h1 { "User " (id) }
    })
}

The parameter is passed as the last argument to your handler function.

Dynamic Directories

You can also use dynamic segments in directory names:

src/routes/users/[id]/posts/index.rs handles /users/:id/posts:

src/routes/users/[id]/posts/index.rs
use rejoice::{Req, Res, html};

pub async fn get(req: Req, res: Res, id: String) -> Res {
    res.html(html! {
        h1 { "Posts for user " (id) }
    })
}

Multiple Dynamic Segments

Routes can have multiple dynamic parameters:

src/routes/users/[user_id]/posts/[post_id]/index.rs handles /users/:user_id/posts/:post_id:

src/routes/users/[user_id]/posts/[post_id]/index.rs
use rejoice::{Req, Res, html};

pub async fn get(req: Req, res: Res, user_id: String, post_id: String) -> Res {
    res.html(html! {
        h1 { "Post " (post_id) " by user " (user_id) }
    })
}

Parameters are passed to the handler in the order they appear in the URL path.

Examples

FileURLParameters
users/[id].rs/users/123id = "123"
blog/[slug].rs/blog/hello-worldslug = "hello-world"
users/[id]/posts/index.rs/users/5/postsid = "5"
users/[uid]/posts/[pid].rs/users/5/posts/42uid = "5", pid = "42"

Route Function Signatures

Stateless Routes

For apps without shared state (using routes!()):

// Basic route
pub async fn get(req: Req, res: Res) -> Res

// Dynamic route
pub async fn get(req: Req, res: Res, id: String) -> Res

Stateful Routes

For apps with shared state (using routes!(AppState)):

// Basic route
pub async fn get(state: AppState, req: Req, res: Res) -> Res

// Dynamic route  
pub async fn get(state: AppState, req: Req, res: Res, id: String) -> Res

Nested Directories

Create nested routes by adding subdirectories:

Next Steps