Core Concepts
Column Types & Modifiers
All available column helpers and the chainable modifiers used to configure them.
This page serves as a reference guide for all available column helpers and the chainable modifiers used to configure them.
Column Types
These functions define the data type of your column.
| Function | SQLite Type | Default TS Type | Modes & Special Usage |
|---|---|---|---|
text(name, config?) | TEXT | string | mode: 'json': The column is serialized/deserialized as JSON, and the TS type becomes any. enum: [...]: Constrains values to a TypeScript union type (e.g., `'active' |
integer(name, config?) | INTEGER | number | mode: 'timestamp': Treats the value as a UNIX epoch second and maps to a Date object. mode: 'timestamp_ms': Treats the value as a UNIX epoch millisecond and maps to a Date object. mode: 'boolean': Maps 0 and 1 to false and true. |
real(name) | REAL | number | Used for floating-point numbers. |
blob(name, config?) | BLOB | Uint8Array | mode: 'json': Stores JSON as a binary blob, maps to any. mode: 'bigint': Maps the value to a bigint. |
boolean(name) | BOOLEAN | boolean | An alias for integer(name, { mode: 'boolean' }). |
numeric(name, config?) | NUMERIC | number | A flexible type that can store various numeric formats. mode: 'bigint': Maps the value to a bigint. |
enumType(name, vals) | TEXT | vals[number] | A convenient shortcut for text(name, { enum: vals }). |
Column Modifiers
These chainable methods add constraints and behaviors to your column definitions.
| Modifier | Description | Example |
|---|---|---|
.primaryKey() | Marks the column as the primary key. Automatically implies .notNull(). | integer('id').primaryKey() |
.autoincrement() | When used on an integer primary key, makes it auto-incrementing. | integer('id').primaryKey().autoincrement() |
.notNull() | Adds a NOT NULL constraint. Inserts must provide a value for this column unless a default is set. | text('email').notNull() |
.unique() | Adds a UNIQUE constraint, ensuring all values in this column are unique across the table. | text('session_token').unique() |
.default(value) | Provides a static default value for the column if one is not provided on insert. | integer('status').default(0) |
.$defaultFn(() => value) | Provides a dynamic default value, evaluated at the time of insertion. Perfect for timestamps or UUIDs. | text('id').$defaultFn(() => crypto.randomUUID()) |
.$onUpdateFn(() => value) | Sets a dynamic value that is evaluated every time a row is updated. | integer('updated_at').$onUpdateFn(() => new Date()) |
.references(() => table._.columns.column, options?) | Creates a foreign key via a lazy getter (Drizzle-style). Enables self-references and forward refs. Use table._.columns.id to avoid name clashes. | integer('author_id').references(() => users._.columns.id, { onDelete: 'cascade' }) |
.$type<T>() | Advanced: Overrides TypeScript's inferred type for the column. Useful for custom data transformations that the ORM doesn't handle natively. | text('metadata').$type<MyCustomMetadataType>() |
Important Notes
.default() vs .$defaultFn()
Understanding the difference between these two is crucial:
.default(value)→ Creates a SQLDEFAULT valueclause in the table schema. The value must be a simple literal (number, string, boolean). This is evaluated by SQLite at INSERT time..$defaultFn(fn)→ Calls the function in your application code when you INSERT a row. Use this for complex values likeDateobjects orcrypto.randomUUID().
// ✅ CORRECT
export const users = sqliteTable('users', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
status: integer('status').default(0), // Simple value - OK for .default()
createdAt: integer('created_at', { mode: 'timestamp' })
.$defaultFn(() => new Date()), // Date object - must use $defaultFn()
});
// ❌ WRONG - Will cause SQL syntax error!
export const users = sqliteTable('users', {
id: text('id').primaryKey()
.default(crypto.randomUUID()) // ❌ Function call in .default()
.$defaultFn(() => crypto.randomUUID()),
createdAt: integer('created_at', { mode: 'timestamp' })
.default(new Date()) // ❌ Date object in .default()
.$defaultFn(() => new Date()),
});
Rule of thumb:
- Simple values (
0,'active',false) → use.default() - Complex values (
new Date(),crypto.randomUUID(), computed values) → use.$defaultFn() - Never use both on the same column
Accessing Columns in Code
When referencing columns in queries, always use the table._.columns.columnName syntax:
// ✅ CORRECT
eq(users._.columns.id, 1)
eq(users._.columns.email, 'john@example.com')
users._.columns.createdAt
// ❌ WRONG - This doesn't work
eq(users.id, 1)
eq(users.email, 'john@example.com')
users.createdAt
The ._ property contains the table's metadata, including all column definitions.