vertz db pull connects to an existing database, reads its tables, columns, indexes, and foreign keys, and generates a TypeScript schema file using the d builder. This is the fastest way to bring an existing database into Vertz.
Getting started
Configure your database connection
Add a
db export to your vertz.config.ts with the connection URL and dialect:vertz.config.ts
Review and customize
The generated schema is a starting point. Review it, add annotations like
.readOnly(), .autoUpdate(), and .hidden(), then use it like any hand-written schema.CLI options
| Option | Description |
|---|---|
-o, --output <path> | Output file or directory. If the path ends with /, generates one file per table. |
--dry-run | Preview the generated code without writing files. |
-f, --force | Overwrite existing files. |
--url <url> | Database URL (overrides vertz.config.ts). |
--dialect <dialect> | Database dialect: postgres or sqlite. |
Output modes
Single file (default)
Generates oneschema.ts file with all tables:
src/schema.ts
Per-table mode
When the output path ends with/, each table gets its own file plus a barrel index.ts:
src/schema/posts.ts
Zero-config mode
You can skipvertz.config.ts entirely by providing --url and --dialect directly:
Type mapping
The code generator maps database types tod builder calls:
Postgres
| SQL type | Generated code | Notes |
|---|---|---|
uuid | d.uuid() | |
text | d.text() | |
character varying(n) | d.varchar(n) | Falls back to d.text() without length |
boolean | d.boolean() | |
integer | d.integer() | |
integer + nextval() | d.serial() | Auto-increment detection |
bigint | d.bigint() | |
numeric(p,s) | d.decimal(p, s) | |
real | d.real() | |
double precision | d.doublePrecision() | |
timestamptz | d.timestamp() | |
timestamp | d.timestamp() | Annotated with // Source: timestamp without time zone |
date | d.date() | |
time | d.time() | |
jsonb | d.jsonb() | |
json | d.jsonb() | Annotated with // Source: json |
text[] | d.textArray() | |
integer[] | d.integerArray() | |
USER-DEFINED | d.enum(name, values) | Reads enum values from pg_enum |
smallint | d.integer() | Annotated with // Source: smallint |
citext | d.text() | Case-insensitive text extension |
bytea | d.text() | Annotated with // TODO: binary type |
SQLite
| SQL type | Generated code | Notes |
|---|---|---|
integer | d.integer() | |
text | d.text() | |
real / float | d.real() | |
blob | d.text() | Annotated with // TODO: binary type |
d.text() with a // TODO: unmapped type comment so you can fix them manually.
What gets generated
Columns
- Names: Snake-case column names are converted to camelCase (
created_atbecomescreatedAt) - Constraints:
.primary(),.unique(),.nullable()are applied based on the database schema - Defaults: Simple defaults (
now(),true,false, numeric values, string literals) are preserved. Complex expressions (function calls, casts) are skipped.
Indexes
Indexes are generated with their original name, uniqueness, type, and WHERE clause:Relations
Foreign keys are detected and generated.model() with d.ref.one() relations:
- The relation name is derived from the FK column by stripping
IdorFksuffixes (authorIdbecomesauthor) - Self-referential FKs work (
managerIdonemployeesreferencesemployees) - Multiple FKs to the same table are disambiguated (
sender,usersByReceiverId)
Only
d.ref.one() (many-to-one) relations are generated. Inverse d.ref.many() relations are not
inferred — add them manually if needed.Table ordering
Tables are topologically sorted so FK targets are defined before the tables that reference them. Circular references are detected, placed at the end, and annotated:Composite primary keys
Tables with multiple primary key columns use theprimaryKey option instead of .primary() on individual columns:
What is NOT generated
The code generator produces a database-level schema — the structural representation of what exists in the database. App-level annotations that carry semantic meaning are left for you to add:| Annotation | Why it’s not generated |
|---|---|
.readOnly() | Database can’t tell which columns are read-only in your app |
.autoUpdate() | Database triggers exist but don’t map cleanly to Vertz’s auto-update |
.hidden() | Field visibility is an app concern |
.tenant() | Tenant scoping is a framework convention, not a DB concept |
.email(), .url() | Format validation is application logic |
d.ref.many() | Inverse relations require knowing which side is “primary” |
Preview before writing
Use--dry-run to see what would be generated without writing any files:
Workflow: adopting Vertz on an existing database
Review and annotate
Add
.readOnly(), .autoUpdate(), .hidden(), .tenant(), and other annotations. Fix any // TODO comments for unmapped types.Baseline the migration history
bash vertz db baseline This marks the current database state as the starting point — no
SQL is applied.Start developing
From here, use
vertz dev for automatic migrations or vertz db migrate for explicit migration
files. See the migrations guide for details.Schema
Full reference for the
d builder — all column types, modifiers, and annotations.Migrations
How migrations work in development and production.