zanith

Models & Fields

Models are the foundation of your schema. Each model maps to a PostgreSQL table, and each field maps to a column with a specific type and constraints.

What is a model?

Think of a model as a structured description of a database table. Instead of writing raw CREATE TABLE SQL, you define your table in TypeScript with full type safety, autocomplete, and validation. Zanith reads this definition at runtime and uses it to build queries, enforce types, and manage relationships.

Anatomy of a model

Every model has these parts. Some are required, others are optional but recommended.

  1. 01

    name

    The model name in PascalCase (e.g. 'User', 'BlogPost'). Used in code and error messages.

  2. 02

    table

    The actual PostgreSQL table name (e.g. 'users', 'blog_posts'). This is what appears in your database.

  3. 03

    fields

    The columns. Each has a type (string, int, boolean...), optional modifiers (nullable, unique, default...), and maps to a database column.

  4. 04

    relations (optional)

    How this model connects to other models — belongsTo, hasMany, hasOne, manyToMany.

  5. 05

    indexes & constraints (optional)

    Database indexes for performance and unique constraints for data integrity.

  6. 06

    comment & meta (optional)

    Documentation and organizational metadata. Comments appear in the schema graph and can power admin UIs.

Complete example

Here's a real-world Product model with every feature demonstrated:

TSschema/product.ts
import { defineModel } from 'zanith';
 
const Product = defineModel((m) => ({
name: 'Product',
table: 'products',
 
fields: {
...m.id(), // uuid primary key
...m.timestamps(), // createdAt + updatedAt
name: m.string(), // required string (TEXT)
description: m.text().nullable(), // optional long text
price: m.float(), // decimal number
sku: m.string().unique(), // unique constraint
inStock: m.boolean().default(true), // boolean with default
metadata: m.json().nullable(), // JSONB column
categoryId: m.uuid().index(), // indexed foreign key
},
 
relations: {
category: m.belongsTo(Category, {
field: 'categoryId',
references: 'id',
}),
},
 
indexes: [
m.index((f) => [f.sku]), // explicit index
],
 
constraints: [
m.unique((f) => [f.name, f.categoryId]), // composite unique
],
 
comment: 'Product catalog',
meta: { module: 'inventory', owner: 'backend-team' },
}));

Scalar types

Every field starts with a type. Here's what's available and what each maps to in PostgreSQL and TypeScript:

BuilderPostgreSQL typeTypeScript typeUse case
m.string()TEXTstringNames, emails, short text
m.text()TEXTstringLong content, descriptions
m.uuid()UUIDstringIDs, references
m.int()INTEGERnumberCounts, quantities
m.float()DOUBLE PRECISIONnumberPrices, measurements
m.decimal()NUMERICnumberFinancial amounts
m.boolean()BOOLEANbooleanFlags, toggles
m.datetime()TIMESTAMPDateDates, timestamps
m.bigint()BIGINTbigintLarge numbers
m.json()JSONBunknownStructured metadata
m.bytes()BYTEABufferBinary data, files

Field modifiers

Modifiers change how a field behaves. Chain them after the type:

ModifierWhat it doesExample
nullable()Field can be NULL — adds | null to the TS typem.string().nullable()
unique()Adds a UNIQUE constraint in the databasem.string().unique()
default(value)Sets a default value when not providedm.boolean().default(false)
index()Creates a database index for faster lookupsm.uuid().index()
comment(text)Adds documentation to the schema graphm.string().comment('Login email')
map(col)Uses a custom column name in the databasem.string().map('first_name')
array()Makes it a PostgreSQL array typem.string().array()
autoUpdate()Auto-updates value on record changem.datetime().autoUpdate()

Naming conventions

Zanith automatically converts between JavaScript conventions (camelCase) and PostgreSQL conventions (snake_case):

In your codeIn the database
Model UserProfileTable user_profiles
Field createdAtColumn created_at
Field firstNameColumn first_name
Field organizationIdColumn organization_id

Override with table: "custom_table_name" on the model or .map("custom_column") on a field.