Skip to content

Column Types

Column factories map a SQL type to a TypeScript type. The mapping is fixed, so InferTable always knows the row shape.

Factory SQL type Inferred TS type Notes
int() INTEGER number auto-increment when .pk()
text() TEXT string
real() REAL number floating point
bool() INTEGER boolean stored as 0/1, decoded on read
blob() BLOB Uint8Array binary data
json() JSON unknown cast as needed
uuid() TEXT string store UUIDs as text
timestamp() TIMESTAMP Date
enum(...) TEXT string constrained to listed values

enum() takes the allowed string values. The value is stored as text but type-checked against the list.

import { defineTable, enum_ } from "@mountsqli/core";
const tasks = defineTable("tasks", {
status: enum_("todo", "doing", "done")(),
});
// status: "todo" | "doing" | "done"

json() stores anything serializable. The inferred type is unknown, so narrow it at the call site:

const docs = defineTable("docs", { data: json() });
type Doc = InferTable<typeof docs>; // { data: unknown }
const row = await db.from(docs).findOne();
const name = (row!.data as { name: string }).name;

Every column is nullable by default. Chain .notNull() to make it required. Nullability flows into the row type:

const t = defineTable("t", {
a: int(), // number | null
b: int().notNull(), // number
});

timestamp() maps to Date. Use .defaultNow() for a SQL-level CURRENT_TIMESTAMP default:

createdAt: timestamp().notNull().defaultNow(),
  • Prefer int().pk() over a string id unless you need UUIDs.
  • Use bool() for flags; the 0/1 storage is handled for you.
  • Narrow json() results at the edge of your app, not in queries.
  • Assuming bool() returns 0/1 — it returns true/false.
  • Leaving columns nullable when they should be required.