File-Based Routing

Vespera maps your src/routes/ folder structure directly to URL paths. The vespera!() macro scans the folder at compile time — no manual Router::new().route(...) calls needed.

Folder to URL Mapping

src/routes/
├── mod.rs           → /
├── users.rs         → /users
├── posts.rs         → /posts
└── admin/
    ├── mod.rs       → /admin
    └── stats.rs     → /admin/stats

The final URL for a handler is: file path prefix + #[route] path attribute.

// In src/routes/users.rs
#[vespera::route(get, path = "/{id}")]
pub async fn get_user(...) // → GET /users/{id}

Handler Requirements

Handlers must be pub async fn. Private or non-async functions are silently ignored by the scanner.

// Ignored — private
async fn get_users() -> Json<Vec<User>> { ... }
 
// Ignored — not async
pub fn get_users() -> Json<Vec<User>> { ... }
 
// Discovered
pub async fn get_users() -> Json<Vec<User>> { ... }

Route Attribute

// GET /users (default method is GET)
#[vespera::route]
pub async fn list_users() -> Json<Vec<User>> { ... }
 
// POST /users
#[vespera::route(post)]
pub async fn create_user(Json(user): Json<User>) -> Json<User> { ... }
 
// GET /users/{id}
#[vespera::route(get, path = "/{id}")]
pub async fn get_user(Path(id): Path<u32>) -> Json<User> { ... }
 
// PUT /users/{id} with tags and description
#[vespera::route(put, path = "/{id}", tags = ["users"], description = "Update user")]
pub async fn update_user(...) -> ... { ... }

Attribute Parameters

ParameterTypeDescription
methodget, post, put, patch, delete, head, optionsHTTP method (default: get)
pathstringPath suffix appended to the file-based prefix
tagsstring arrayOpenAPI tags for grouping in Swagger UI
descriptionstringOpenAPI operation description

Custom Route Folder

The default folder is src/routes/. Change it with the dir parameter or the VESPERA_DIR environment variable:

// Scans src/api/ instead of src/routes/
let app = vespera!(dir = "api");

Error Handling

Return Result<T, E> from handlers. Both T and E are included in the OpenAPI response schemas:

#[derive(Serialize, Schema)]
pub struct ApiError {
    pub message: String,
}
 
#[vespera::route(get, path = "/{id}")]
pub async fn get_user(
    Path(id): Path<u32>,
) -> Result<Json<User>, (StatusCode, Json<ApiError>)> {
    if id == 0 {
        return Err((
            StatusCode::NOT_FOUND,
            Json(ApiError { message: "Not found".into() }),
        ));
    }
    Ok(Json(User { id, name: "Alice".into() }))
}
Contents
Edit this page
문의 및 의견 제출
contact@devfive.kr
Copyright © DEVFIVE. All Rights Reserved.
DEVFIVE