Installation
Get Zanith running in your project in under a minute.
Install
Install the core package and your preferred database driver. Postgres has two drivers; SQLite ships as a separate adapter.
npm install zanith # Postgres — pick one:npm install pg # node-postgres (most popular)npm install postgres # postgres.js (modern alternative) # Or SQLite:npm install better-sqlite3How Zanith works — the pipeline
.ts or .zn
in-memory
Traditional ORMs
- ×Change schema → regenerate (minutes)
- ×500k lines generated for 1000 models
- ×TS compiler crashes on large schemas
- ×Full rebuild on every change
Zanith
- ✓Change schema → restart (59ms)
- ✓Zero lines generated
- ✓No generated files to compile
- ✓Incremental graph patch (0.7ms)
Create your first schema
- 01
Define your models
Each model maps to a database table. Fields map to columns. Relations map to foreign keys.
- 02
Create the client
Pass your models and a database adapter to createZanith(). No generation step.
- 03
Query with full types
findMany(), create(), update(), delete() — all return typed results. Wrong field names are compile errors.
import { defineModel, defineEnum } from 'zanith'; export const Role = defineEnum({ name: 'Role', values: ['ADMIN', 'USER', 'MODERATOR'],}); export const User = defineModel((m) => ({ name: 'User', table: 'users', fields: { ...m.id(), // uuid primary key ...m.timestamps(), // createdAt + updatedAt email: m.string().unique(), name: m.string().nullable(), role: m.enum(Role).default('USER'), }, relations: { posts: m.hasMany(() => Post, { foreignKey: 'authorId' }), },})); export const Post = defineModel((m) => ({ name: 'Post', table: 'posts', fields: { ...m.id(), ...m.timestamps(), title: m.string(), content: m.text().nullable(), published: m.boolean().default(false), authorId: m.uuid(), }, relations: { author: m.belongsTo(User, { field: 'authorId', references: 'id' }), },}));Connect and query
Create a Zanith client, pass your models and a database adapter, and start querying.
import { createZanith } from 'zanith';import { PgAdapter } from 'zanith/adapters/pg';import { User, Post, Role } from './schema/user'; const db = await createZanith({ models: { User, Post }, enums: [Role], adapter: new PgAdapter({ connectionString: 'postgresql://user:pass@localhost:5432/mydb', }),}); // Fully typed — email: string, name: string | nullconst users = await db.user.findMany({ where: { email: { contains: '@example.com' } }, orderBy: { createdAt: 'desc' }, take: 10,}); // Relational query — auto JOINconst posts = await db.post.query() .with({ author: true }) .select(({ post, author }) => ({ title: post.title, authorEmail: author.email, })) .where(({ post }) => post.published.eq(true)) .limit(20) .execute(); await db.disconnect();Scalar types
Every field builder method maps to a PostgreSQL type and a TypeScript type.
| Builder | PostgreSQL | TypeScript |
|---|---|---|
m.string() | TEXT | string |
m.text() | TEXT | string |
m.uuid() | UUID | string |
m.int() | INTEGER | number |
m.float() | DOUBLE PRECISION | number |
m.boolean() | BOOLEAN | boolean |
m.datetime() | TIMESTAMP | Date |
m.bigint() | BIGINT | bigint |
m.json() | JSONB | unknown |
m.decimal() | NUMERIC | number |
m.bytes() | BYTEA | Buffer |
Field modifiers
| Modifier | Effect | Example |
|---|---|---|
nullable() | Field can be NULL | m.string().nullable() |
unique() | UNIQUE constraint | m.string().unique() |
default(value) | Default value | m.boolean().default(false) |
index() | Database index | m.uuid().index() |
comment(text) | Documentation | m.string().comment('Login email') |
map(col) | Custom column name | m.string().map('first_name') |
array() | PostgreSQL array | m.string().array() |
autoUpdate() | Auto-update on change | m.datetime().autoUpdate() |
Built-in mixins
Mixins are reusable field groups. Spread them into your model fields.
| Mixin | Provides | Fields |
|---|---|---|
m.id() | UUID primary key | id: uuid, pk, default(uuid()) |
m.intId() | Auto-increment PK | id: int, pk, default(autoincrement()) |
m.timestamps() | Created + updated | createdAt: datetime, updatedAt: datetime autoUpdate |
m.auditable() | Audit trail | createdById: uuid?, updatedById: uuid? |
m.orgScoped() | Multi-tenant FK | organizationId: uuid, indexed |
m.softDelete() | Soft delete | deletedAt: datetime? |
Try Studio
Once you have a running Zanith client, the same binary launches a full web UI against your connection — no separate install.
npx zanith studio --allow-sql# studio listening on http://127.0.0.1:4321Next steps
Now that you have a basic setup, explore:
- Relations — belongsTo, hasMany, manyToMany
- Relational queries — auto JOINs, multi-hop
- Filters — contains, gt, in, OR, AND
- Migrations — risk-scored, shadow-verified, with a recovery layer
- Studio — the bundled web UI for browse / edit / SQL / locks / RLS
- CLI reference — every command and flag