Defaults
Omitted, the column is required and not null.
Where this fits
A column with no default is required and not null. This page is how a missing value is filled, from nullable through a generated expression. Shape is next, when one column holds more than one value.
Insert requires the field unless the column is nullable, has a default, or is left out of input. Pass a value and that value is stored. Update does not fill the default again. A later .default(), .defaultSql(), or .generated() replaces the one before it.
Nullable
.nullable() allows SQL NULL. Omit the field, or pass null. The catalog stores no default.
import { table, t } from "okmodel/pg";
export const notes = table("notes", {
id: t.identity(),
publishedAt: t.timestamptz().nullable(),
});Literal
.default(literal) stores the encoded literal in the catalog. Omit the field on insert and Postgres stores that literal.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.identity(),
title: t.text().default(""),
});
const app = schema({ tables: [notes] });
export async function add(url: string) {
await using db = connect(url, { schema: app });
return db.notes.insert({});
}OKM1060
A default outside a picklist is refused. The message names the value and the list.
Generator
.default(generator) runs when insert omits the field. The catalog stores no default, so a writer that bypasses okmodel must supply the value. Pass a value and that value is stored instead.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
import { uuidv7 } from "okmodel/ids";
const notes = table("notes", {
id: t.uuid().default(uuidv7),
title: t.text(),
});
const app = schema({ tables: [notes] });
export async function add(url: string, title: string) {
await using db = connect(url, { schema: app });
return db.notes.insert({ title });
}Generators
A generator is uuidv4, uuidv7, or OKID (okid(...)), all from okmodel/ids, or any function. connect({ generators }) replaces uuidv4, uuidv7, and okid. A function you wrote is left alone. These run in the application. They are not the database functions uuidv7() and gen_random_uuid().
Already filled
t.id() and t.identity() already carry a default. Both are omitted from insert and update, so input cannot set them. The schema id default changes what a bare t.id() means. A column option wins.
| Call | What fills it | Insert |
|---|---|---|
t.id() | Database gen_random_uuid(), unless the schema default says otherwise. | Omitted. |
t.id({ default: "uuidv7" }) | Database uuidv7(). Needs Postgres 18. | Omitted. |
t.id({ default: "uuidv4" }) | Database gen_random_uuid(), the same as a bare call. | Omitted. |
t.id({ default: uuidv7 }) | The client generator. Nothing is stored in the catalog. | Omitted. |
t.id({ default: "none" }) | Nothing. | Required. Still omitted from update. |
t.identity() | generated always as identity. | Omitted. |
t.id({ default: "none" }) is the column option. It is not a schema default.
database
A bare t.id() is omitted from insert. Postgres fills gen_random_uuid(). A large write-heavy table can store uuidv7() instead: the ids are time-ordered and the index stays local. That function needs Postgres 18 and minPostgres: 18.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.id(),
title: t.text(),
});
const app = schema({ tables: [notes] });
export async function add(url: string, title: string) {
await using db = connect(url, { schema: app });
return db.notes.insert({ title });
}supplied
"none" is required on insert. Update still cannot set it.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
const notes = table("notes", {
id: t.id({ default: "none" }),
title: t.text(),
});
const app = schema({ tables: [notes] });
export async function add(url: string, id: string, title: string) {
await using db = connect(url, { schema: app });
return db.notes.insert({ id, title });
}Null
.default(null) is accepted only after .nullable(). The check runs when .default() is called, so the order is .nullable().default(null).
import { table, t } from "okmodel/pg";
export const notes = table("notes", {
id: t.identity(),
closedAt: t.timestamptz().nullable().default(null),
});OKM1060
A null default on a column that is not nullable is refused. Call .nullable() first.
SQL
.defaultSql(expression) stores the expression as written. Omit the field and Postgres evaluates it. An empty expression is refused.
import { table, t } from "okmodel/pg";
export const notes = table("notes", {
id: t.identity(),
createdAt: t.timestamptz().defaultSql("now()"),
});OKM1060
defaultSql must be a non-empty SQL expression.
Generated
.generated(expression) is generated always as (expression) stored. Insert and update omit the column. A read returns the value. An empty expression is refused. { stored: true } is the default, and the SQL is stored either way.
import { table, t } from "okmodel/pg";
export const notes = table("notes", {
id: t.identity(),
title: t.text(),
search: t.tsvector().generated("to_tsvector('simple', title)"),
});OKM1060
generated() must be a non-empty SQL expression.