Definers

The Postgres driver’s definers are the entry points you import from @schemic/postgres. defineTable is the core one — indexes and foreign keys are authored inside a table — and alongside it are standalone definers for enums, domains, sequences, extensions, views, functions, triggers, and policies. This page lists each one’s signature, plus defineTable’s methods and the escape-hatch factory.

defineTable

TypeScript
defineTable(name: string, fields: Shape, config?): PgTableDef

Creates a table definition. fields is an object of s.* columns. When no primary key is declared, Schemic adds an implicit "id" text PRIMARY KEY.

MethodDescription
.primaryKey(...cols)Composite / custom PRIMARY KEY, replacing the implicit id.
.check(expr)A table-level CHECK (expr). Emitted but excluded from drift detection.
.index(cols, opts?)A secondary index over cols. opts: { name?, unique?, method?, where? } (method = btree/gin/gist/brin/hash; where = partial predicate).
.foreignKey(opts)A composite or non-id foreign key. opts: { columns, refTable, refColumns?, onDelete?, onUpdate?, name? } (refColumns defaults to ["id"]).
.record(opts?)The s.references foreign-key field for this table, for use in another table’s shape. opts: { onDelete?, onUpdate? }.
TypeScript
import { defineTable, s } from "@schemic/postgres";

export const member = defineTable("member", {
  org: s.text(),
  person: s.text(),
})
  .primaryKey("org", "person")
  .index(["person"]);

Columns and per-field codecs

Each entry in fields is a PgField, which carries the codec for that column. Read and write through table.fields.<column>.encode(value) / .decode(value) — see encode & decode rows. The decoded and encoded row shapes are the App<typeof Table> and Wire<typeof Table> types.

Standalone definers

Beyond defineTable, the Postgres driver ships definers for the other top-level objects. Each is imported from @schemic/postgres and emits its CREATE … statement alongside your tables.

DefinerSignatureEmits
defineEnumdefineEnum(name, values).column() types a column as the enumCREATE TYPE <name> AS ENUM (...)
defineDomaindefineDomain(name, base, opts?)opts: { notNull?, default?, check? }; .column() types a columnCREATE DOMAIN <name> AS <base> [DEFAULT] [NOT NULL] [CHECK]
defineSequencedefineSequence(name, opts?)opts: { start?, increment?, min?, max?, cache?, cycle? }CREATE SEQUENCE <name> …
defineExtensiondefineExtension(name, opts?)opts: { schema?, version? }CREATE EXTENSION IF NOT EXISTS <name> [SCHEMA] [VERSION]
defineViewdefineView(name, sql)CREATE VIEW <name> AS <sql>
defineMaterializedViewdefineMaterializedView(name, sql)CREATE MATERIALIZED VIEW <name> AS <sql>
defineFunctiondefineFunction(name, opts)opts: { args?, returns, language?, body, volatility?, strict?, replace? }CREATE [OR REPLACE] FUNCTION <name>(args) RETURNS <ret> LANGUAGE <lang> AS $$ … $$
defineTriggerdefineTrigger(name, opts)opts: { table, timing, events, function, forEach?, when?, args? }CREATE TRIGGER <name> <timing> <events> ON <table> EXECUTE FUNCTION <fn>()
definePolicydefinePolicy(name, opts)opts: { table, command?, roles?, using?, withCheck?, permissive? }ALTER TABLE <table> ENABLE ROW LEVEL SECURITY; + CREATE POLICY <name> ON <table> …

defineEnum and defineDomain expose a .column() that returns a PgField typed as that enum or domain — drop it into a table’s fields like any other column. See the coverage map for the round-trip status of each.

The escape hatch

For a value with no built-in mapping, s.$postgres(pgType, codec) builds a field from scratch: the column emits as pgType, and the Zod codec maps between app and wire.

TypeScript
import { defineTable, s } from "@schemic/postgres";
import * as z from "zod";

export const blob = defineTable("blob", {
  raw: s.$postgres("text", z.string()),
});

The chainable form, .$postgres(wire, codec?), attaches a storage type and codec to an existing field — see field methods.

Where to go next