Schema

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.

schema()Anatomy
export default schema({
tables: [notes],required
…,sections
});

Tables

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.

OrderPageYou use it for
1TablesThe list schema() compiles.
2CasingThe SQL name of a camelCase field.
3CodecsThe TypeScript value. The SQL type stays.
4Row typesEmitted types, or types kept in the checker.
5Id defaultWhat a bare t.id() means.
6Minimum PostgresThe oldest major. Omitted means 15.
7TraitsColumns added to every table.
8TenancyTenant keys on every table.
9ValidationWhether rules run.
10ExtensionsExtensions the catalog names.
11FunctionsRoutines the catalog stores.
12TriggersTriggers that call those functions.
13ViewsViews and materialized views.
14Catalogsexternal(), 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.

FieldRequiredDefaultRole
tablesyes—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.

schema.ts
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.

several.ts
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.

schema.ts
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.

schema.ts
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.

FeatureNeeds
uuidv7() database defaultNeeds 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.

schema.ts
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.

schema.ts
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.

NextYou use it for
KeysThe primary key, the id default, and a generator.