The schema
schema() compiles the tables into one catalog. Some options are sections of this page. Others are their own pages.
schema() compiles the tables into one catalog. This page holds the declaration, the tables list, and the oldest PostgreSQL major the catalog allows. Each other option is linked from the declaration to the page that now holds it.
tables: [notes],required …,sectionsTables
The list, in the order you write it. A reference names a table in this list, earlier or later. table() only stores the columns. Names and the catalog hash are fixed here. Calling schema() twice on the same tables returns equal catalog bytes.
Pages
Tables and minimum Postgres are sections on this page. Each other field is a separate page.
The declaration
Options are sections of this page, or pages that still exist: tables, columns, and defaults. Tables come first, because every other option refers to them.
| Order | Page | You use it for |
|---|---|---|
| 1 | Tables | The list schema() compiles. |
| 2 | Casing | The SQL name of a camelCase field. |
| 3 | Codecs | The TypeScript value. The SQL type stays. |
| 4 | Row types | Emitted types, or types kept in the checker. |
| 5 | Id default | What a bare t.id() means. |
| 6 | Minimum Postgres | The oldest major. Omitted means 15. |
| 7 | Traits | Columns added to every table. |
| 8 | Tenancy | Tenant keys on every table. |
| 9 | Validation | Whether rules run. |
| 10 | Extensions | Extensions the catalog names. |
| 11 | Functions | Routines the catalog stores. |
| 12 | Triggers | Triggers that call those functions. |
| 13 | Views | Views and materialized views. |
| 14 | Catalogs | external(), a snapshot, and one history per namespace. |
Tables
tables is every table() this schema compiles. The client name is the name you passed to table().
The list order is the order of the emitted interfaces. The catalog is sorted by identity, so swapping two tables does not change the catalog hash. Calling schema() twice on the same tables returns equal catalog bytes.
| Field | Required | Default | Role |
|---|---|---|---|
tables | yes | — | The list. A reference names one entry, earlier or later. |
one
One table is a schema. The module default-exports it. Config points schema at this file.
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.identity(),
title: t.text(),
});
export default schema({ tables: [notes] }); several
.references("notes") looks up notes in this list. comments is first here. notes is second. Either order resolves.
import { schema, table, t } from "okmodel/pg";
const comments = table("comments", {
id: t.identity(),
noteId: t.bigint().references("notes"),
body: t.text(),
});
const notes = table("notes", {
id: t.identity(),
title: t.text(),
});
export default schema({ tables: [comments, notes] }); OKM1020
A name that is not in the list is Table posts is not in the schema. The message lists the accepted names.
OKM1023
The same name twice is Table notes is declared more than once. note-items and note_items both emit NoteItems, which is Table note_items and note-items both emit NoteItems. Two tables that store the same SQL name are Table posts uses SQL name notes, which another table already uses. A table named capabilities is Table capabilities uses the client name capabilities. That name is db.capabilities.
OKM1060
A value that is not a list is schema() needs a tables array.
Empty
An empty list is a schema with no tables.
Minimum Postgres
minPostgres is a major. Connect checks the server against it. Omitted means 15, the oldest major okmodel runs. A feature below its major is refused when schema() runs. A bare t.id() stores gen_random_uuid() and does not need 18.
Not the catalog
This field is not part of the catalog hash. Changing the floor does not change it.
omitted
Omitted names 15. A bare t.id() stores gen_random_uuid().
Connect accepts PostgreSQL 15 and newer. okm migrate apply still looks for an explicit default uuidv7() before the first statement. A server below 18 that has no uuidv7() function is refused, and nothing is changed. A server that already defines uuidv7() is accepted. A migration with no such expression does not ask the server.
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.id(),
title: t.text(),
});
export default schema({
tables: [notes],
});OKM1803
A server below 15, when no floor is named, is The server is PostgreSQL 14. This schema needs PostgreSQL 15 or higher (minPostgres: 15).
OKM1812
A server below 18 with no uuidv7() function is Column notes.id has a uuidv7() default (migration 0001_init, step 1), and the server is PostgreSQL 17. uuidv7() needs PostgreSQL 18 or higher. minPostgres: 18. Nothing was changed. The message names the column, the migration, the step, and the server. The step counts from 1, the same number apply prints in failed at step N.
floor
minPostgres: 18 asks for 18 or newer. A server below that major is refused. The majors are 15, 16, 17, and 18.
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.id(),
title: t.text(),
});
export default schema({
tables: [notes],
minPostgres: 18,
});features
Each row is a feature that refuses a lower floor. The number is the major that feature needs.
| Feature | Needs |
|---|---|
uuidv7() database default | Needs PostgreSQL 18 or higher. |
below 18
minPostgres of 15, 16, or 17 refuses a column whose default is uuidv7() when schema() runs. A bare t.id() is gen_random_uuid() and is accepted. "uuidv4" stores the same function. A client generator is the other way through. Both are on id default. An omitted floor does not refuse an explicit uuidv7() here. Apply checks the server.
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.id({ default: "uuidv4" }),
title: t.text(),
});
export default schema({
tables: [notes],
});OKM1812
uuidv7() needs PostgreSQL 18 or higher. minPostgres: 18.
OKM1802
A server below the floor is The server is PostgreSQL 16. This schema needs PostgreSQL 17 or higher (minPostgres: 17).
together
A schema written for 17. Both bare t.id() columns already store gen_random_uuid(). This example sets that same function on the schema. A large write-heavy table can set uuidv7 instead, and that choice needs Postgres 18 and minPostgres: 18.
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.id(),
title: t.text(),
});
const sessions = table("sessions", {
id: t.id(),
name: t.text(),
});
export default schema({
tables: [notes, sessions],
minPostgres: 17,
defaults: { id: "uuidv4" },
});string
requires: { postgres: ">=17" } is the same floor as minPostgres: 17. The string form is deprecated. Passing both is an error that names both keys. ">= 17", "17", ">=17.2", and a major outside 15 through 18 are errors. The error names minPostgres and those majors.
OKM1060
A major outside the list is minPostgres: 15-18. Passing both keys is minPostgres or requires.postgres, not both.
| Next | You use it for |
|---|---|
| Keys | The primary key, the id default, and a generator. |