PostgreSQL column types
Import from rado/postgres. Postgres has a famously rich type catalog and
rado covers a generous slice of it, including identity columns, arrays and
pgvector types.
import {pgTable, integer, text, timestamp} from 'rado/postgres'
Numbers
smallint() // number
integer() / int() // number
bigint({mode: 'number'}) // number
bigint({mode: 'bigint'}) // bigint
doublePrecision() // number
real() // number
numeric() // string (exact, arbitrary precision)
numeric({mode: 'number'}) // number
numeric({mode: 'bigint'}) // bigint
numeric({precision: 10, scale: 2})
oid() // number
numeric defaults to string so you never silently lose precision. Opt into
number or bigint when you know your values fit.
Serial and identity columns
smallserial() // number
serial() // number
bigserial({mode: 'number' | 'bigint'})
Postgres itself recommends identity columns over serials these days, and rado supports them as modifiers on any integer-ish column:
const User = pgTable('user', {
id: integer().primaryKey().generatedAlwaysAsIdentity()
// or .generatedByDefaultAsIdentity()
})
Text
text() // string
varchar({length: 255}) // string
char({length: 10}) // string
Booleans, binary, UUID
boolean() // boolean
bytea() // Uint8Array
uuid() // string
const Session = pgTable('session', {
id: uuid()
.primaryKey()
.default(sql`gen_random_uuid()`)
})
Dates and times
date({mode: 'date'}) // Date
date({mode: 'string'}) // string
time({precision: 6, withTimeZone: true}) // string
timestamp({mode: 'date'}) // Date
timestamp({mode: 'string', precision: 3, withTimeZone: true})
interval({fields: 'day to hour', precision: 6}) // string
const Post = pgTable('post', {
createdAt: timestamp('created_at', {mode: 'date'}).defaultNow()
})
JSON
json() // typed JSON
jsonb() // typed JSON, binary storage (you usually want this one)
const User = pgTable('user', {
settings: jsonb<{theme: 'light' | 'dark'}>()
})
await db.select().from(User).where(eq(User.settings.theme, 'dark'))
See JSON.
Arrays
Every Postgres column can become an array with .array(size?):
const Post = pgTable('post', {
tags: text().array(), // string[]
matrix: integer().array().array() // number[][], and yes, it nests
})
Filter with the array operators:
import {arrayContains, arrayOverlaps} from 'rado'
await db
.select()
.from(Post)
.where(arrayContains(Post.tags, ['sql']))
await db
.select()
.from(Post)
.where(arrayOverlaps(Post.tags, ['sql', 'ts']))
Network and bit types
inet() // string
cidr() // string
macaddr() // string
macaddr8() // string
bit({dimensions: 3}) // string
varbit({dimensions: 8}) // string
Geometry
point({mode: 'tuple'}) // [x, y]
point({mode: 'xy'}) // {x, y}
line({mode: 'tuple'}) // [a, b, c]
line({mode: 'abc'}) // {a, b, c}
geometry({type: 'point', mode: 'xy'}) // PostGIS geometry
pgvector
For similarity search with the pgvector extension:
vector({dimensions: 1536}) // number[]
halfvec({dimensions: 1536}) // number[]
sparsevec({dimensions: 1536}) // string
Enums
Postgres enums are declared with pgEnum and used as column types. They get
their own page: PostgreSQL schemas & enums.
import {pgEnum, pgTable} from 'rado/postgres'
const mood = pgEnum('mood', ['sad', 'ok', 'happy'])
const MoodEntry = pgTable('mood_entry', {
state: mood().notNull() // 'sad' | 'ok' | 'happy'
})
Postgres-specific column modifiers
On top of the shared modifiers, PgColumn adds:
.array(size?): see above.generatedAlwaysAsIdentity()/.generatedByDefaultAsIdentity().unique(name?, {nulls: 'not distinct'}): treat nulls as equal in the unique constraint