Examples · 01 — The schema this page uses
Real schemas. Real SQL. No magic on the wire.
Every example below uses the same .zanith schema. A typed call on the left, the parameterized SQL Zanith generates on the right. Read both — the gap between them is the engine.
Same SQL. Two ways to write it.
Object syntax in CRUD methods and field-reference syntax in the relational query builder — both compile to identical parameterized SQL.
Shared .zanith schema+ expand
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
createdAt DateTime @default(now())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
body String
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
comments Comment[]
createdAt DateTime @default(now())
}
model Comment {
id Int @id @default(autoincrement())
body String
postId Int
post Post @relation(fields: [postId], references: [id])
}
enum Role { USER ADMIN EDITOR }Every operator compiles. Nothing interpolated.
Comparison, string match, lists, boolean logic, null checks — each becomes a bound parameter. Sourced from engine/src/expression.
Operator reference · 14 total
| op | shape | sql |
|---|---|---|
| equals | { field: T } | field = $1 |
| not | { not: T } | field <> $1 |
| gt · gte | { gt: T } · { gte: T } | field > $1 · field >= $1 |
| lt · lte | { lt: T } · { lte: T } | field < $1 · field <= $1 |
| in | { in: T[] } | field IN ($1, $2, …) |
| notIn | { notIn: T[] } | field NOT IN ($1, …) |
| contains | { contains: string } | field ILIKE $1 |
| startsWith | { startsWith: string } | field ILIKE $1 |
| endsWith | { endsWith: string } | field ILIKE $1 |
| AND | { AND: WhereInput[] } | (a AND b) |
| OR | { OR: WhereInput[] } | (a OR b) |
| NOT | { NOT: WhereInput } | NOT (…) |
| null | { field: null } | field IS NULL |
| not null | { not: null } | field IS NOT NULL |
Joins that compile to a single statement.
Declare what you want — Zanith builds the JOINs from your schema. Filters on related models flatten into EXISTS subqueries.
one query, automatic JOINs
EXISTS — no N+1
query-builder projections
Counting and ranking, first-class — not bolted on.
groupBy, having, and window functions compile to standard SQL — no client-side aggregation.
groupBy + having
posts per author above threshold
aggregate + filter
join + COUNT/AVG in one pass
ROW_NUMBER
top N per group via subquery
Writes return what they wrote. Transactions roll back on throw.
Every write uses RETURNING so the row comes back typed. Bulk inserts batch into a single roundtrip; transactions wrap BEGIN/COMMIT atomically.
create
INSERT + RETURNING *
compile 2.1µs
upsert
ON CONFLICT DO UPDATE
compile 3.4µs
createMany
bulk VALUES list
compile 7.6µs
transaction
BEGIN · throw → ROLLBACK
When you need raw SQL, the values are still bound.
db.raw`…` tagged templates refuse to interpolate values — every ${x} becomes a bound parameter.
Safety contract
- · Values cannot be string-concatenated into the template — every ${x} becomes a bound parameter.
- · Identifiers (table/column names) must come from a fixed allowlist, not user input.
- · Result type is unknown unless you specify a generic — no schema-graph inference for raw queries.
Microseconds on the engine. Network latency is the bill.
Figures below are the engine's compile overhead — not total query time. Sourced from engine/test/benchmark/execution.test.ts (VOICE.md §7).
Simple filter findMany
2.4µs
execution.test.ts
Join + projection + where
17.2µs
execution.test.ts
Single-row insert
2.8µs
execution.test.ts
Upsert ON CONFLICT
2.7µs
execution.test.ts
Bulk insert · 10 rows
7.6µs
execution.test.ts
GROUP BY + COUNT + SUM
5.5µs
execution.test.ts
Full benchmark receipts → /proof
Every operator documented. Every option typed.
Filters & where
Object syntax + field references — every operator.
/docs/queries/filtersRelations
include, nested where, multi-hop joins.
/docs/queries/relationsAggregates
groupBy, having, _count, _sum, _avg.
/docs/queries/aggregatesInsert & upsert
create, createMany, upsert, RETURNING *.
/docs/queries/insertTransactions
BEGIN/COMMIT/ROLLBACK, typed tx context.
/docs/advanced/transactionsRaw SQL
db.raw tagged template — values still bound.
/docs/advanced/raw-sqlelsewhere