Routing
Routes are values. A path plus a MethodRouter makes a Route. An App
collects routes, mounts other apps, and generates the OpenAPI 3.1 document
from those same values, so the docs cannot drift from the code.
Function API
Section titled “Function API”use siderite::prelude::*;
let app = App::new() .title("Users") .version("1.0.0") .route( "/users", post(create_user).status(StatusCode::CREATED).tag("users") .get(list_users).summary("List users"), );get,post,put,patch,delete,head,optionscreate aMethodRouterand can be chained..on(Method, h)takes any method.- Metadata builders
.summary(),.description(),.tag(),.operation_id(),.deprecated(),.hidden(),.status(StatusCode),.response_model::<T>()apply to the most recently added method, so order matters. .layer(layer)wraps every method added so far on that path in a middleware layer (for exampleRouteCache). It runs after routing and only for that path; methods chained after it are not wrapped.App::routes(iter)acceptsRoutevalues (Route::new(path, router)), which is what the macros below produce.- Path templates use
{name}parameters ({*rest}for a catch-all). Duplicate(path, method)pairs and paths without a leading/are rejected when the app is built.
Route macros
Section titled “Route macros”#[get], #[post], #[put], #[patch], #[delete], #[head],
#[options], and #[ws] (a GET) attach a route to an async fn.
/// Create a user.////// Stores the user and returns it.#[post("/users", status = 201, response_model = UserOut, tag = "users")]async fn create_user(Json(input): Json<UserIn>) -> Json<UserOut> { /* .. */ }The first argument is the path (a string literal). All other arguments are
optional and may appear in any order; each may appear once (tag and tags
are separate keys and are combined).
Metadata
Section titled “Metadata”| Argument | Default | Effect |
|---|---|---|
operation_id |
function name | OpenAPI operationId |
summary |
first line of the doc comment | OpenAPI summary |
description |
remaining doc-comment lines | OpenAPI description |
status |
200 | Replaces a 200 OK at runtime and moves the documented success response. Handlers returning another status (WithStatus) are left alone. Range: 100..=599. |
response_model = T |
handler return type | Documents the success body as T (application/json). Does not convert the response. |
tag / tags(...) |
none | OpenAPI tags |
deprecated |
off | Marks the operation deprecated |
hidden |
off | Serves the route, omits it from OpenAPI |
Explicit arguments replace the doc-derived summary and description.
Compile-time diagnostics
Section titled “Compile-time diagnostics”Reported with spans on the offending token: path not a string literal or not
starting with /, unbalanced braces, invalid or duplicate {param} names,
unknown or duplicate arguments, status outside 100..=599, non-async or
generic handlers, and route attributes on items other than functions.
What the macro emits
The function is left untouched (it stays callable). Next to it, with the same visibility, the macro emits:
#[doc(hidden)]#[allow(non_snake_case, dead_code)]fn __siderite_route_create_user() -> ::siderite::Route { ::siderite::Route::new( "/users", ::siderite::post(create_user) .operation_id("create_user") .summary("Create a user.") .description("Stores the user and returns it.") .tag("users") .status(::siderite::http::StatusCode::from_u16(201) .unwrap_or(::siderite::http::StatusCode::OK)) .response_model::<UserOut>(), )}deprecated and hidden add .deprecated() / .hidden(). #[ws] uses
::siderite::get. The unwrap_or fallback is unreachable: the status is
validated at compile time.
routes![]
Section titled “routes![]”App::new().routes(routes![create_user, users::list])expands to a Vec of the generated __siderite_route_* functions. Only the
last path segment is rewritten (a leading :: is kept). Generic arguments
are rejected. routes![] is an empty Vec; a trailing comma is fine.
Because the generated function is separate from the handler, refer to
handlers by a path where they are defined (users::list), not through a
use users::list; re-import of the handler alone.
Mounting
Section titled “Mounting”App::mount(prefix, app) (alias nest) serves app below prefix.
- Child operations are merged into the parent OpenAPI document under the
prefixed path (
/maps to the prefix itself). - Component schemas from all apps share one registry, so a type used in several places is emitted once.
- Two different Rust types claiming the same schema name, duplicate
operation ids, and duplicate
(path, method)pairs are configuration errors when the app is built. - Startup/shutdown hooks of mounted apps are lifted to the root.
- Title, version, and docs settings come from the root app only.
- State registered with
with_stateapplies to the routes of the app it was called on. - Root-app middleware also wraps 404s and the docs endpoints; a mounted child’s middleware wraps only its own routes.
FastAPI differences
Section titled “FastAPI differences”- Routes are values. Decorators only generate a sibling function, so nothing is registered at import time and handlers stay plain functions.
- Request validation is driven by types (
Json<T>,Query<T>), not by default-argument markers. - Dependency injection is explicit:
Depends<T>,Provided<T>,State<T>. statusis per operation and replaces only a200.- Routers are
Apps (mount) rather than a separateAPIRoutertype.