Skip to main content
vertz/db is the database layer of the Vertz stack. You define schemas with the d builder, query with typed operations, and run migrations with a single command. Types are inferred from your schema — no code generation step, no separate type files.

How it works

1

Define your schema

Use the d builder to declare tables, columns, and relations. Column types, constraints, and annotations are all defined in TypeScript.
2

Query with types

The database client provides typed CRUD operations. Filters, selects, and includes are all type-checked at compile time.
3

Migrate automatically

The CLI diffs your schema against the database and generates migrations. No manual SQL, no migration files to maintain during development.

Quick example

import { createDb, d } from 'vertz/db';

// Define schema
const usersTable = d.table('users', {
  id: d.uuid().primary({ generate: 'cuid' }),
  email: d.email().unique(),
  name: d.text(),
  role: d.enum('user_role', ['admin', 'user']).default('user'),
  createdAt: d.timestamp().default('now').readOnly(),
});

const usersModel = d.model(usersTable);

// Create client — PostgreSQL
const db = createDb({
  url: process.env.DATABASE_URL!,
  models: { users: usersModel },
});

// Typed queries
const user = await db.users.create({
  data: { email: 'alice@example.com', name: 'Alice' },
});

const admins = await db.users.list({
  where: { role: 'admin' },
  orderBy: { createdAt: 'desc' },
});
Types flow from the schema definition. db.users.create() only accepts fields from the table definition, and the return type includes all non-hidden columns.

Database support

All databases use the same createDb() API — the only difference is the options you pass:
import { createDb } from 'vertz/db';

const db = createDb({
url: process.env.DATABASE_URL!,
models: { users: usersModel, tasks: tasksModel },
});

OptionPostgreSQLSQLite (local)Cloudflare D1
dialect'postgres' (default)'sqlite''sqlite'
Connectionurl: stringpath: stringd1: D1Database
MigrationsCLI (vertz db migrate)migrations: { autoApply: true }Not available (autoApply is disallowed for D1 — use wrangler migrations)
Transactionsdb.transaction()db.transaction()Not supported (use D1Database.batch())
SQLite with autoApply: true automatically creates tables on first query — no migration commands needed during development. Boolean values are stored as 0/1 and timestamps as ISO strings, but the query layer converts them to native JS types automatically.

What’s included

FeatureDescription
Schema builderDeclarative d API for tables, columns, relations
Typed queriesCRUD operations with compile-time type checking
Automatic migrationsSchema diffing with CLI commands
Multi-dialectPostgreSQL and SQLite with a unified API
RelationsOne-to-many, many-to-one, many-to-many through join tables
TransactionsAtomic multi-operation writes with db.transaction()
Error handlingResult-based errors — never throws
Multi-tenancyBuilt-in tenant scoping at the model level

Core principles

Schema is the source of truth

Column types, constraints, defaults, and annotations are all declared in the schema. Input/output types, validation schemas, and migration diffs are derived from it automatically. No duplication.

Result-based errors

Query operations return Result<T, Error> — they never throw. You handle errors explicitly with pattern matching, not try/catch:
const result = await db.users.create({ data: { email: 'alice@example.com', name: 'Alice' } });

if (result.ok) {
  console.log(result.data.name);
} else {
  console.error(result.error.code); // 'UNIQUE_VIOLATION', 'NOT_NULL_VIOLATION', etc.
}

Compile-time safety

If your schema says role is an enum of 'admin' | 'user', the query builder rejects any other value at compile time. If a column is .readOnly(), it won’t appear in create/update input types. The TypeScript compiler catches data issues before they reach the database.

Guides

Schema

Tables, columns, types, relations, and annotations.

Queries

CRUD operations, filters, pagination, and error handling.

Migrations

Auto-migration CLI and deployment workflow.

Seeding

Populate your database with dev, test, and production data.