glimr/db/schema
Schema DSL
A fluent builder for defining database table schemas in Gleam. Schemas serve as the source of truth for code generation, migration generation, and type inference.
Types
Represents a column definition within a table. Each column has a name, type, nullability flag, and optional default value.
pub type Column {
Column(
name: String,
column_type: ColumnType,
nullable: Bool,
default: option.Option(Default),
renamed_from: option.Option(String),
)
}
Constructors
-
Column( name: String, column_type: ColumnType, nullable: Bool, default: option.Option(Default), renamed_from: option.Option(String), )
Defines the available column types that map to both PostgreSQL and SQLite data types. The codegen tool uses these to generate appropriate Gleam types and SQL DDL statements.
pub type ColumnType {
Id
String(max: option.Option(Int))
Text
Int
SmallInt
BigInt
Float
Boolean
Timestamp
UnixTimestamp
Date
Json
Uuid
Foreign(
table: String,
on_delete: option.Option(ForeignAction),
on_update: option.Option(ForeignAction),
)
Array(ColumnType)
Enum(name: String, variants: List(String))
Decimal(precision: Int, scale: Int)
Blob
Time
}
Constructors
-
IdAuto-incrementing integer primary key
-
String(max: option.Option(Int))Variable-length string with optional max length (VARCHAR)
-
TextUnlimited text (TEXT)
-
IntStandard integer (INT/INTEGER)
-
SmallIntSmall integer (SMALLINT)
-
BigIntLarge integer (BIGINT)
-
FloatFloating point number (REAL/DOUBLE PRECISION)
-
BooleanBoolean value (BOOLEAN/INTEGER for SQLite)
-
TimestampTimestamp with timezone (TIMESTAMP/TEXT for SQLite)
-
UnixTimestampUnix timestamp as integer seconds (INTEGER)
-
DateDate without time (DATE/TEXT for SQLite)
-
JsonJSON data (JSONB for Postgres, TEXT for SQLite)
-
UuidUUID (UUID for Postgres, TEXT for SQLite)
-
Foreign( table: String, on_delete: option.Option(ForeignAction), on_update: option.Option(ForeignAction), )Foreign key reference to another table with optional actions
-
Array(ColumnType)Array of another column type (PostgreSQL native arrays, JSON in SQLite)
-
Enum(name: String, variants: List(String))Enum with named variants (CREATE TYPE in Postgres, CHECK in SQLite). Generates a Gleam custom type with to_string/from_string.
-
Decimal(precision: Int, scale: Int)Precise numeric value (NUMERIC in Postgres, TEXT in SQLite)
-
BlobBinary data (BYTEA in Postgres, BLOB in SQLite)
-
TimeTime without date (TIME in Postgres, TEXT in SQLite)
Defines default values that can be assigned to columns. Used in migration generation to produce appropriate SQL DEFAULT clauses.
pub type Default {
DefaultString(String)
DefaultInt(Int)
DefaultFloat(Float)
DefaultBool(Bool)
DefaultNow
DefaultUnixNow
DefaultAutoUuid
DefaultNull
DefaultEmptyArray
}
Constructors
-
DefaultString(String) -
DefaultInt(Int) -
DefaultFloat(Float) -
DefaultBool(Bool) -
DefaultNow -
DefaultUnixNow -
DefaultAutoUuid -
DefaultNull -
DefaultEmptyArray
Defines the referential actions for foreign key constraints.
Used with on_delete and on_update modifiers to control
what happens when the referenced row is deleted or updated.
pub type ForeignAction {
Cascade
Restrict
SetNull
SetDefault
NoAction
}
Constructors
-
Cascade -
Restrict -
SetNull -
SetDefault -
NoAction
Database queries that filter or sort by a column benefit
hugely from an index — without one, the database scans every
row. This type captures what the developer wants indexed,
whether it should enforce uniqueness, and an optional custom
name (otherwise the migration generates
idx_{table}_{col1}_{col2} automatically).
pub type IndexDef {
IndexDef(
columns: List(String),
unique: Bool,
name: option.Option(String),
)
}
Constructors
-
IndexDef( columns: List(String), unique: Bool, name: option.Option(String), )
Values
pub fn array(def: ColumnDef) -> ColumnDef
Sometimes you need a column that holds multiple values —
tags, scores, email addresses. This wraps any column type in
an Array so string("tags") |> array() becomes
List(String) in Gleam, TEXT[] in Postgres, and JSON text
in SQLite. Chaining |> array() |> array() gives you nested
arrays for matrix-style data.
pub fn auto_uuid(def: ColumnDef) -> ColumnDef
Sets the default value to an auto-generated UUID. Use with
uuid columns for automatic unique identifier generation.
Maps to:
- PostgreSQL:
gen_random_uuid() - SQLite: Custom expression generating UUID v4 format
Example:
table("users", [
uuid("external_id")
|> auto_uuid(),
])
pub fn bigint(name: String) -> ColumnDef
Creates a large integer column for values exceeding standard integer range.
Maps to:
- PostgreSQL:
name BIGINT - SQLite:
name INTEGER
pub fn blob(name: String) -> ColumnDef
Creates a binary data column.
Maps to:
- PostgreSQL:
BYTEA - SQLite:
BLOB
pub fn boolean(name: String) -> ColumnDef
Creates a boolean column.
Maps to:
- PostgreSQL:
name BOOLEAN - SQLite:
name INTEGER(0 = false, 1 = true)
pub fn columns(t: Table) -> List(Column)
Returns the table’s columns in definition order. Useful for iterating over columns when generating code or performing schema introspection.
pub fn date(name: String) -> ColumnDef
Creates a date column (without time component).
Maps to:
- PostgreSQL:
name DATE - SQLite:
name TEXT(YYYY-MM-DD format)
pub fn decimal(
name: String,
precision: Int,
scale: Int,
) -> ColumnDef
Creates a precise numeric column with the given precision and scale.
Maps to:
- PostgreSQL:
NUMERIC(precision, scale) - SQLite:
TEXT
pub fn default_bool(def: ColumnDef, value: Bool) -> ColumnDef
Sets a boolean default value for the column.
Example:
table("users", [
boolean("is_active")
|> default_bool(True),
])
pub fn default_empty_array(def: ColumnDef) -> ColumnDef
Without a default, inserting a row without specifying the
array column would violate NOT NULL. This sets the default
to an empty array ('{}' in Postgres, '[]' in SQLite) so
new rows get a valid empty list instead of failing.
pub fn default_float(def: ColumnDef, value: Float) -> ColumnDef
Sets a float default value for the column.
Example:
table("products", [
float("price")
|> default_float(0.0),
])
pub fn default_int(def: ColumnDef, value: Int) -> ColumnDef
Sets an integer default value for the column.
Example:
table("posts", [
int("view_count")
|> default_int(0),
])
pub fn default_now(def: ColumnDef) -> ColumnDef
Sets the default value to the current timestamp.
Example:
table("posts", [
timestamp("published_at")
|> default_now(),
])
pub fn default_null(def: ColumnDef) -> ColumnDef
Sets the default value to NULL. Use with nullable columns to explicitly set NULL as the default rather than having no default value specified.
pub fn default_string(def: ColumnDef, value: String) -> ColumnDef
Sets a string default value for the column.
Example:
table("users", [
string("role")
|> default_string("user"),
])
pub fn default_unix_now(def: ColumnDef) -> ColumnDef
Sets the default value to the current Unix timestamp
(seconds since epoch). Use with unix_timestamp columns.
Example:
table("events", [
unix_timestamp("created_at")
|> default_unix_now(),
])
pub fn enum(name: String, variants: List(String)) -> ColumnDef
Creates an enum column that generates a Gleam custom type
with to_string and from_string converters. The enum name
is auto-generated from the column name (PascalCase) but can
be overridden with |> enum_name("custom").
Maps to:
- PostgreSQL: Custom
CREATE TYPEwith enum values - SQLite:
TEXTwithCHECKconstraint
Example:
table("users", [
schema.enum("status", ["active", "inactive", "banned"]),
])
pub fn enum_name(def: ColumnDef, name: String) -> ColumnDef
Overrides the auto-generated enum type name. By default, the enum type name is derived from the column name in PascalCase. Use this to set a custom name.
Example:
schema.enum("status", ["active", "inactive"])
|> enum_name("user_status")
pub fn float(name: String) -> ColumnDef
Creates a floating-point number column.
Maps to:
- PostgreSQL:
name DOUBLE PRECISION - SQLite:
name REAL
pub fn foreign(name: String, references: String) -> ColumnDef
Creates an integer column that references another table’s
id. The column name should follow the convention
{table}_id.
Maps to:
- PostgreSQL:
name INTEGER REFERENCES table(id) - SQLite:
name INTEGER REFERENCES table(id)
Example:
table("posts", [
id(),
foreign("user_id", "users"),
string("title"),
])
pub fn id() -> ColumnDef
Creates an auto-incrementing integer primary key column named “id”. This is typically the first column in a table.
Maps to:
- PostgreSQL:
id SERIAL PRIMARY KEY - SQLite:
id INTEGER PRIMARY KEY AUTOINCREMENT
pub fn index(columns: List(String)) -> IndexDef
Creates a regular (non-unique) index on the given columns.
Use this when you frequently query by a column but don’t
need uniqueness enforced — for example, indexing a status
column that your list queries filter on.
pub fn indexes(table: Table, defs: List(IndexDef)) -> Table
Pipes onto table() to declare indexes alongside columns.
Keeping indexes in the schema definition means the migration
generator can detect when you add or remove an index and
produce the right CREATE INDEX / DROP INDEX SQL
automatically — no hand-written migrations needed.
Example:
table(name, [id(), string("email")])
|> indexes([unique(["email"])])
pub fn int(name: String) -> ColumnDef
Creates a standard integer column.
Maps to:
- PostgreSQL:
name INTEGER - SQLite:
name INTEGER
pub fn json(name: String) -> ColumnDef
Creates a JSON column for structured data.
Maps to:
- PostgreSQL:
name JSONB - SQLite:
name TEXT
pub fn named(def: IndexDef, name: String) -> IndexDef
Overrides the auto-generated index name. The default
idx_{table}_{col1}_{col2} is fine most of the time, but if
you need a specific name for constraint error handling or
migration compatibility with an existing database, pipe
through this.
pub fn nullable(def: ColumnDef) -> ColumnDef
Marks the column as nullable (allows NULL). By default, columns are NOT NULL.
Example:
table("users", [
id(),
string("name"),
string("bio")
|> nullable(),
])
pub fn on_delete(
def: ColumnDef,
action: ForeignAction,
) -> ColumnDef
Sets the ON DELETE action for a foreign key column. Only
applies to columns with Foreign type.
Example:
foreign("user_id", "users")
|> on_delete(Cascade)
pub fn on_update(
def: ColumnDef,
action: ForeignAction,
) -> ColumnDef
Sets the ON UPDATE action for a foreign key column. Only
applies to columns with Foreign type.
Example:
foreign("user_id", "users")
|> on_update(Restrict)
pub fn rename_from(def: ColumnDef, old_name: String) -> ColumnDef
Indicates that this column was renamed from a previous name.
The migration generator will use RENAME COLUMN instead of
drop/add, preserving data. This modifier is automatically
removed from the schema after the migration is generated.
Example:
table("users", [
string("email_address")
|> rename_from("email"),
])
pub fn smallint(name: String) -> ColumnDef
Creates a small integer column for compact storage of small values like status codes or counters.
Maps to:
- PostgreSQL:
name SMALLINT - SQLite:
name INTEGER
pub fn soft_deletes() -> ColumnDef
Adds a nullable deleted_at timestamp column for soft
delete support. Records are marked as deleted by setting
this column instead of being removed from the database.
Maps to:
- PostgreSQL:
deleted_at TIMESTAMP WITH TIME ZONE - SQLite:
deleted_at TEXT
pub fn string(name: String) -> ColumnDef
Creates a VARCHAR(255) column with the given name.
Maps to:
- PostgreSQL:
name VARCHAR(255) - SQLite:
name TEXT
pub fn string_sized(name: String, max: Int) -> ColumnDef
Creates a VARCHAR column with a specific maximum length.
Maps to:
- PostgreSQL:
name VARCHAR(max) - SQLite:
name TEXT
pub fn table(name: String, column_defs: List(ColumnDef)) -> Table
Creates a new table definition with the given name and column definitions.
Example:
import glimr/db/schema
pub const table_name = "users"
pub fn definition() {
schema.table(table_name, [
schema.id(),
schema.string("name"),
schema.string("email"),
schema.timestamps(),
])
}
pub fn table_indexes(t: Table) -> List(IndexDef)
Returns the table’s index definitions. The migration generator and snapshot system use this to detect when indexes have been added, removed, or changed between schema versions.
pub fn text(name: String) -> ColumnDef
Creates an unlimited text column. Use for large text content like blog posts, descriptions, etc.
Maps to:
- PostgreSQL:
name TEXT - SQLite:
name TEXT
pub fn time(name: String) -> ColumnDef
Creates a time column (without date component).
Maps to:
- PostgreSQL:
TIME - SQLite:
TEXT
pub fn timestamp(name: String) -> ColumnDef
Creates a timestamp column for date/time values.
Maps to:
- PostgreSQL:
name TIMESTAMP WITH TIME ZONE - SQLite:
name TEXT(ISO 8601 format)
pub fn timestamps() -> ColumnDef
Creates both created_at and updated_at timestamp
columns. This is a convenience function for the common
pattern of tracking record creation and modification times.
pub fn unique(columns: List(String)) -> IndexDef
Creates a unique index — the database will reject any INSERT or UPDATE that would create a duplicate. This is how you enforce “one account per email” or “one vote per user per post” at the database level rather than relying on application-level checks that can race.
pub fn unix_timestamp(name: String) -> ColumnDef
Creates an integer column for storing Unix timestamps (seconds since epoch).
Maps to:
- PostgreSQL:
name BIGINT - SQLite:
name INTEGER
pub fn unix_timestamps() -> ColumnDef
Creates both created_at and updated_at as unix timestamp
columns (integer seconds since epoch). This is a convenience
function for the common pattern of tracking record creation
and modification times using integer timestamps.