zanith

Your First Query

Go from zero to querying your database in 60 seconds. No generation step. No waiting.

What you'll build

By the end of this page, you'll have a working Zanith setup that defines a User model, connects to PostgreSQL, and runs a fully typed query.

  1. 01

    Define your schema

    Create a model that describes your database table — its fields, types, and constraints.

  2. 02

    Create the client

    Pass your models and a database connection to createZanith(). The schema compiles into a runtime graph in ~59ms.

  3. 03

    Query with full types

    Call findMany(), create(), or any other method. TypeScript knows the exact shape of your results.

Step 1 — Define a model

A model describes a database table. Each field maps to a column. The TypeScript builder carries type information through the entire chain, so your queries are always type-safe — without generating any code.

TSschema.ts
import { defineModel } from 'zanith';
 
export const User = defineModel((m) => ({
name: 'User',
table: 'users',
fields: {
...m.id(), // uuid primary key with auto-generated default
...m.timestamps(), // createdAt + updatedAt
email: m.string().unique(),
name: m.string().nullable(),
},
relations: {},
}));

Step 2 — Create the client

Pass your models and a database adapter to createZanith(). That's it. No prisma generate. No build step. The schema compiles into a runtime graph in milliseconds — even with 1000 models.

TSapp.ts
import { createZanith } from 'zanith';
import { PgAdapter } from 'zanith/adapters/pg';
import { User } from './schema';
 
const db = await createZanith({
models: { User },
adapter: new PgAdapter({
connectionString: process.env.DATABASE_URL,
}),
});

Step 3 — Query

Now query your data. Every method is fully typed — TypeScript knows thatusers is an array with id, email,name, createdAt, and updatedAt fields.

TSapp.ts
// Find users — fully typed result
const users = await db.user.findMany({
where: { email: { contains: '@example.com' } },
orderBy: { createdAt: 'desc' },
take: 10,
});
// users: Array<{ id: string, email: string, name: string | null, ... }>
 
// Create a new record
const newUser = await db.user.create({
data: { email: '[email protected]', name: 'Alice' },
});
 
// Wrong field names are compile errors:
// db.user.findMany({ where: { emale: 'test' } });
// ^^^^^ TS Error!
 
await db.disconnect();

How the pipeline works

Under the hood, every query passes through a structured pipeline. Your code never touches SQL directly — the engine builds parameterized queries from your schema graph.

defineModel()

schema

createZanith()

graph

db.user

typed API

findMany()

builds AST

SQL Compiler

parameterized

PostgreSQL

executes

The entire pipeline — schema lookup, AST construction, SQL compilation — takes about 4 microseconds per query. The bottleneck is always the database round trip, never the engine.

Next steps

Now that you have a basic setup, explore:

  • TypeScript Setup — how type inference works without code generation
  • Relations — connect models with belongsTo, hasMany, manyToMany
  • Relational queries — auto JOINs, multi-hop traversal
  • Mixins — reusable field groups (timestamps, audit, soft delete)