Constraints & defaults
The $-prefixed field methods attach SQL clauses to a column. This guide covers the everyday ones — defaults, checks, generated columns, identity, and comments. For the full method list see field methods.
Defaults
$default(value) emits a DEFAULT clause. A bare JavaScript value is rendered as a SQL literal; wrap a SQL expression in sqlExpr(...) so the driver knows it is an expression, not a string:
import { defineTable, s, sqlExpr } from "@schemic/postgres";
export const t = defineTable("t", {
status: s.text().$default("active"),
created: s.timestamptz().$default(sqlExpr("now()")),
});CREATE TABLE "t" (
"id" text PRIMARY KEY,
"created" timestamp with time zone NOT NULL DEFAULT now(),
"status" text NOT NULL DEFAULT 'active'
);The rule: $default("active") is the literal string 'active'; $default(sqlExpr("now()")) is the SQL function call now().
CHECK constraints
$check(expr) adds a column-level CHECK. Pass a bare SQL boolean expression (or wrap it in sqlExpr(...)):
export const t = defineTable("t", {
score: s.integer().$check("score >= 0"),
});CREATE TABLE "t" (
"id" text PRIMARY KEY,
"score" integer NOT NULL CHECK (score >= 0)
);For a constraint that spans columns, use the table-level .check(expr) instead — see define a table.
Generated columns
$generated(expr) emits a GENERATED ALWAYS AS (expr) STORED column computed from other columns. Reference other columns by their quoted name; a camelCase column needs the quotes:
export const line = defineTable("line", {
quantity: s.integer(),
unitPrice: s.numeric(10, 2),
total: s.numeric(12, 2).$generated('quantity * "unitPrice"'),
});CREATE TABLE "line" (
"id" text PRIMARY KEY,
"quantity" integer NOT NULL,
"total" numeric(12, 2) NOT NULL GENERATED ALWAYS AS (quantity * "unitPrice") STORED,
"unitPrice" numeric(10, 2) NOT NULL
);Identity columns
$identity() makes a column an auto-incrementing identity. The default mode is by-default (GENERATED BY DEFAULT AS IDENTITY); pass "always" for GENERATED ALWAYS AS IDENTITY:
export const t = defineTable("t", {
seq: s.integer().$identity("by-default"),
});CREATE TABLE "t" (
"id" text PRIMARY KEY,
"seq" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY
);s.serial() and s.bigserial() are shorthands for an integer / bigint identity column.
Comments
$comment(text) emits a COMMENT ON COLUMN statement:
export const t = defineTable("t", {
note: s.text().$comment("free text"),
});CREATE TABLE "t" (
"id" text PRIMARY KEY,
"note" text NOT NULL
);
COMMENT ON COLUMN "t"."note" IS 'free text';The comment emits but is not read back during introspection, so — like the clauses above — it is excluded from drift detection.
Where to go next
- Foreign keys — referential constraints with
s.references. - Indexes —
$uniqueand secondary indexes. - Field methods — every
$-prefixed clause.