Skip to content

Core concepts

A few types show up in every siderite application. This page names them so the guides can assume them.

App is the application: routes, mounts, middleware, state, databases, and the OpenAPI document. You build it as a value and then either run it or hand it to AppCli / TestClient.

App::new()
.title("Hello")
.version("1.0.0")
.routes(routes![hello, echo])
.run("127.0.0.1:8000")
.await?;

Duplicate (path, method) pairs, paths without a leading /, and OpenAPI conflicts (operationId, schema names) are reported when the app is built — from run(), into_router_service(), App::openapi(), or TestClient::try_new. They are not panics on the first request.

use siderite::prelude::*; is the intended import for application code. It brings in routing, extractors, responses, Validate / Schema / Model, Db / QuerySet / ForeignKey, and serde’s Serialize / Deserialize. The full list is in Prelude.

Axum and SQLx types do not appear in the public API. Json, Path, Query, and State are siderite newtypes, so you can upgrade those internals without a breaking change of your own.

A handler is an async fn that returns something implementing IntoResponse. Every argument except the last implements FromRequestParts. The last argument may implement FromRequest and consume the body (Json<T>, Form<T>).

Those traits are siderite’s own. Each extractor and response can implement a describe hook so OpenAPI is derived from the signature.

Db is a cheap clone of a pool handle or of an open transaction. There is no global connection registry.

let adults = User::objects(&db)
.filter(User::age.ge(18))
.all()
.await?;

User::objects(&db) captures the handle. Terminal methods (.all(), .get(), .delete(), …) use that handle and take no extra argument. The same code runs inside db.transaction(|tx| async move { … }) if you pass tx instead of db.

Handlers typically receive Db through Provided<Db>, Depends<Db>, or State<Databases>. See Dependency injection and Database routing.

Derive Crate Job
Validate validation Coerce, check constraints, run hooks. Used by extractors.
Schema validation JSON Schema / OpenAPI component. Also implements Dump when the type is Serialize.
Model ORM Table metadata, field constants, QuerySet, persistence.

One struct can derive all of them plus Serialize / Deserialize. The shared #[field(…)] attribute is read by each derive for the keys it understands; unknown keys are compile errors on Model and ignored on Schema so the attributes can be shared.

ForeignKey<T> validates and documents itself as T’s primary key, not as a nested object.

Invalid input is ValidationError → HTTP 422 with an errors array. Missing rows are QueryError::DoesNotExist → 404. Constraint failures are 409 (the database detail is logged, not returned). Everything else that is an ApiError renders as RFC 7807 application/problem+json.

The map is in Errors.

Lookups exist only on suitable field types, so User::age.icontains("x") does not compile. Features a backend cannot run (select_for_update on SQLite, joins on MongoDB) become BackendCapabilityError when the plan is checked, before any I/O. Nothing is silently ignored.