#[vespera::route] marks a pub async fn as an HTTP handler. Vespera reads the function signature to extract path parameters, query parameters, request body, and response types for the OpenAPI spec.
#[vespera::route(
get, // HTTP method (default: get)
path = "/{id}", // Path suffix (appended to file-based prefix)
tags = ["users", "admin"], // OpenAPI tags
description = "Get user by ID" // OpenAPI operation description
)]
pub async fn get_user(Path(id): Path<u32>) -> Json<User> { ... }| Parameter | Type | Default | Description |
|---|---|---|---|
| method | get, post, put, patch, delete, head, options | get | HTTP method |
path | string | "" | Path suffix appended to the file-based prefix |
tags | string array | [] | OpenAPI tags for grouping in Swagger UI |
description | string | "" | OpenAPI operation description |
Vespera reads your handler's extractor types and maps them to OpenAPI parameters and request bodies automatically:
| Extractor | OpenAPI Location | Notes |
|---|---|---|
Path<T> | Path parameters | T can be a primitive or a struct |
Query<T> | Query parameters | Struct fields become individual query params |
Json<T> | Request body (application/json) | |
Form<T> | Request body (application/x-www-form-urlencoded) | |
TypedMultipart<T> | Request body (multipart/form-data) | Typed with schema |
Multipart | Request body (multipart/form-data) | Untyped, generic object |
TypedHeader<T> | Header parameters | |
State<T> | Ignored | Internal — not part of the API |
Extension<T> | Ignored | Internal — not part of the API |
// Single path param
#[vespera::route(get, path = "/{id}")]
pub async fn get_user(Path(id): Path<u32>) -> Json<User> { ... }
// Multiple path params via struct
#[derive(Deserialize)]
pub struct PostParams {
pub user_id: u32,
pub post_id: u32,
}
#[vespera::route(get, path = "/{user_id}/posts/{post_id}")]
pub async fn get_post(Path(params): Path<PostParams>) -> Json<Post> { ... }#[derive(Deserialize, Schema)]
pub struct ListUsersQuery {
pub page: Option<u32>,
pub limit: Option<u32>,
pub search: Option<String>,
}
#[vespera::route(get)]
pub async fn list_users(Query(q): Query<ListUsersQuery>) -> Json<Vec<User>> { ... }#[derive(Deserialize, Schema)]
pub struct CreateUserRequest {
pub name: String,
pub email: String,
}
#[vespera::route(post)]
pub async fn create_user(Json(req): Json<CreateUserRequest>) -> Json<User> { ... }use vespera::Validated;
use garde::Validate;
#[derive(Deserialize, Schema, Validate)]
pub struct CreateUserRequest {
#[garde(length(min = 3, max = 32))]
pub username: String,
#[garde(email)]
pub email: String,
}
#[vespera::route(post)]
pub async fn create_user(
Validated(Json(req)): Validated<Json<CreateUserRequest>>,
) -> Json<User> { ... }#[vespera::route(get)]
pub async fn list_users(
State(db): State<DbPool>, // ignored by OpenAPI
Query(q): Query<ListQuery>, // included in OpenAPI
) -> Json<Vec<User>> { ... }#[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() }))
}pub async fn — private or non-async functions are ignored#[vespera::route] attributesrc/routes/ (or your configured dir)path attribute value