Schema

Row types

The TypeScript value of a column, and how row types are produced.

Codecs choose the TypeScript value of a column, and the SQL type stays. Row types are the interfaces schema() emits for each table, or the types the checker keeps. One schema can set both.

Codecs

codecs chooses the TypeScript value of t.bigint(), t.identity(), and t.numeric(). The SQL type stays bigint or numeric. A time column is already Temporal. Omit a key and that codec stays at its default.

The column as is what types the value. While the schema codec is "string", a column may pass another as. When the schema codec is "number" or "bigint", every column of that type must pass the same as. A bare column was built as "string".

CodecValuesDefault
bigint"string", "number", "bigint""string"
numeric"string", "number""string"
timestamps"temporal""temporal"

bigint

t.bigint() and t.identity() share this codec. "number" is a safe integer. A value outside that range is OKM1210. See bigint.

string

Omitted and "string" are a decimal string. The SQL value stays exact past a safe integer.

bigint-string.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  cents: t.bigint(), 
});

export default schema({ tables: [payments] });

number

"number" is a safe integer. The column passes the same as.

bigint-number.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity({ as: "number" }), 
  cents: t.bigint({ as: "number" }), 
});

export default schema({
  tables: [payments],
  codecs: { bigint: "number" }, 
});

native

"bigint" is a JavaScript bigint.

bigint-native.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity({ as: "bigint" }),
  cents: t.bigint({ as: "bigint" }), 
});

export default schema({
  tables: [payments],
  codecs: { bigint: "bigint" }, 
});

OKM1060

A bare column under "number" or "bigint" is Schema codecs.bigint is number, and payments.cents was built as string. Set as: "number" on that column. Any other schema value is schema() codecs.bigint date must be string, number, or bigint.

numeric

t.numeric() is exact. { as: "number" } can lose digits past a JavaScript number. See numeric.

string

Omitted and "string" keep every digit.

numeric-string.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  amount: t.numeric(12, 2), 
});

export default schema({ tables: [payments] });

number

"number" needs { as: "number" } on the column. Precision and scale stay the SQL type.

numeric-number.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  amount: t.numeric(12, 2, { as: "number" }), 
});

export default schema({
  tables: [payments],
  codecs: { numeric: "number" }, 
});

OKM1060

A bare column under "number" is Schema codecs.numeric is number, and payments.amount was built as string. Set as: "number" on that column. Any other schema value is schema() codecs.numeric bigint must be string or number.

timestamps

"temporal" is the only value. Omitting it is the same. A time column has no as. t.timestamptz() is Temporal.Instant. See time.

timestamps.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  paidAt: t.timestamptz(), 
});

export default schema({
  tables: [payments],
  codecs: { timestamps: "temporal" }, 
});

OKM1060

Any other value is schema() codecs.timestamps date must be temporal.

together

One schema can set every codec. Each bigint column matches "bigint". The numeric column matches "number".

schema.ts
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity({ as: "bigint" }),
  cents: t.bigint({ as: "bigint" }), 
  amount: t.numeric(12, 2, { as: "number" }), 
  paidAt: t.timestamptz(),
});

export default schema({
  tables: [payments],
  codecs: {
    bigint: "bigint", 
    numeric: "number", 
    timestamps: "temporal", 
  },
});

Row types

types is stored on the schema. Queries still infer a row from schema(). okm build writes .okm/types.d.ts for either value, and okm generate writes that same file. The header is Mode: emitted or Mode: inferred. The interfaces do not change with the mode.

A table named notes becomes Notes, NotesInsert, and NotesUpdate. The file then maps those names on Rows, Inserts, and Updates, in the order of the tables list. Two names that emit one interface are refused there.

t.jsonb<{ published?: boolean }>() keeps that argument in the file. The type parameter is erased at runtime, so the text is read from the schema source. A bare t.jsonb() is jsonb<unknown>. When either form appears, the file starts with export type jsonb<T> = T. t.json<T>() is the same, with json.

Not the catalog

This flag is not part of the catalog hash. Swapping "emitted" and "inferred" does not change it.

emitted

Omitted means "emitted". This is the mode a file imports when it should not re-check the schema.

schema.ts
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

export const app = schema({
  tables: [notes],
});

export const mode = app.types;

"emitted" stores the same value.

schema.ts
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

export const app = schema({
  tables: [notes],
  types: "emitted", 
});

export const mode = app.types;

inferred

"inferred" stores that mode. The client and Row, Insert, and Update still read the schema. Use this when the checker should see the schema, not the written interfaces.

schema.ts
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

export const app = schema({
  tables: [notes],
  types: "inferred", 
});

export const mode = app.types;

Types together

Two tables, a typed jsonb, and the default mode. okm build writes Notes and Comments, each with an insert and an update, then Rows, Inserts, and Updates.

schema.ts
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
  meta: t.jsonb<{ published?: boolean }>(), 
});

const comments = table("comments", {
  id: t.identity(),
  noteId: t.bigint().references("notes"),
  body: t.text(),
});

export default schema({
  tables: [notes, comments],
});

OKM1060

Any other value is schema() types local must be emitted or inferred. The type only allows "emitted" and "inferred", so this is also a type error.

NextYou use it for
Required rowsRows inserted when the key is missing.