Quickstart
This page is the smallest generic setup path for trying CrateStack in a Rust service.
1. Include A Schema
Example schema:
auth Principal {
id String
role String?
}
mixin AuditFields {
createdAt DateTime @default(dbgenerated())
updatedAt DateTime @default(dbgenerated())
}
model Post {
@use(AuditFields)
id String @id
title String
@@allow("read", auth() != null)
}Pick the macro that matches the crate's role:
// Server crate
use cratestack::include_server_schema;
include_server_schema!("schema.cstack", db = Postgres);db = Postgres is the argument for a schema with a real datasource { provider = "postgresql" } block. A schema declaring datasource { provider = "none" } instead pairs with db = None — see Procedures-only servers below. The two must agree: passing db = Postgres against a provider = "none" schema (or vice versa) is a compile-time macro error, not a runtime one.
Embedded (mobile / desktop / browser-via-OPFS) and HTTP-client crates use different macros — see Offline-First Embedded SQLite and the client-runtime docs respectively.
2. Build The Generated Runtime
let pool = sqlx::PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
let cool = cratestack_schema::Cratestack::builder(pool).build();3. Use Delegates Directly
You can use CrateStack without mounting the generated REST layer.
let visible_posts = cool
.post()
.find_many()
.where_expr(
cratestack_schema::post::author()
.email()
.eq("owner@example.com")
.and(cratestack_schema::post::published().is_true()),
)
.order_by(cratestack_schema::post::author().email().desc())
.limit(20)
.run(&ctx)
.await?;A relation filter or sort such as author() reads the related model's rows through that model's own read policy and @@soft_delete filter (since 0.13.0): an author the caller may not read behaves as if it did not exist. If the related model declares no rule that allows reading it (@@allow for read, list or all), the filter matches nothing. See Relation filters and sorts.
4. Optionally Bind Auth For Internal Callers
let bound = cool.bind_auth(Some(serde_json::json!({
"id": 7,
"role": "admin"
})))?;
let posts = bound.post().find_many().run().await?;5. Optionally Mount Generated Routers
let app = axum::Router::new().nest(
"/api",
cratestack_schema::axum::model_router(
cool.clone(),
(),
cratestack_codec_cbor::CborCodec,
AppAuthProvider,
),
);The () in second position is the computed-field resolver. A schema with no @computed fields needs no resolver at all, and the macro generates impl ComputedFieldResolver for () so the unit type satisfies the bound. Replace it with your own resolver once you declare a computed field — see Computed Fields.
Procedures-only servers (no database)
Not every server owns a database — see Procedures-Only Servers with No Database for datasource { provider = "none" }, db = None, and the cratestack-api facade.
Read Next
../guides/auth-providerfor the host auth boundary../architecture/transport-architecturefor codec, framing, and envelope rules../overview/banking-readinessfor the regulated-workload primitives../guides/offline-first-sqliteif you want to run the same schema embedded against a local SQLite database — same.cstackfile, sync API on native, OPFS-backed in the browser, no tokio../guides/no-database-proceduresfor procedures-only servers with no database at all