Schema Migrations
Layeron Database uses declarative SQL migrations to safely build and evolve your database schema.
Migrations are part of the db(...) declaration. Layeron stores the migration
metadata with the Database Product instance, applies pending migrations during
dev and deploy flows, and records applied migration state for each environment.
Schema-Generated Migration
Section titled “Schema-Generated Migration”When you pass schema, Layeron generates the first migration named
layeron_001_schema:
import { db, table, text, integer, rawSql } from "@layeron/modules"
const database = db({ name: "main", schema: { posts: table({ id: text().primaryKey(), title: text().notNull(), createdAt: integer().notNull().default(rawSql("(unixepoch())")), }), },})Use schema-generated migrations when you want typed Table API metadata and a concise source of truth for the initial table layout.
SQL String
Section titled “SQL String”For quick prototypes or small stores, you can pass one SQL string. Layeron
creates an internal migration name such as layeron_001_auto:
const database = db({ name: "main", sql: ` create table if not exists posts ( id text primary key, title text not null ); `,})Versioned Migrations
Section titled “Versioned Migrations”For production applications, define named migration objects:
const database = db({ name: "main", sql: [ { name: "001_create_posts", sql: ` create table if not exists posts ( id text primary key, title text not null ); `, }, { name: "002_add_status", sql: "alter table posts add column status text not null default 'draft';", }, ],})Names should be stable and ordered. A common convention is
001_create_posts, 002_add_status, and 003_create_comments.
Combine Schema and SQL
Section titled “Combine Schema and SQL”You can declare a typed schema and append custom SQL migrations. Layeron places the schema-generated migration first, then the SQL migrations:
const database = db({ name: "main", schema: { posts: table({ id: text().primaryKey(), title: text().notNull(), createdAt: integer().notNull(), }), }, sql: [ { name: "002_add_posts_status", sql: "alter table posts add column status text not null default 'draft';", }, ],})This is useful when most tables live in the typed schema and one change needs hand-written SQL.
Migration Rules
Section titled “Migration Rules”Layeron applies migrations with these rules:
- Immutable history: Applied migration SQL and metadata are stable.
- Append-only changes: Add a new migration for each schema change.
- Sequential execution: Pending migrations run in order.
- Forward fixes: Correct mistakes with a later forward migration.
- Per-environment state: Dev, preview, and production environments track their own applied migrations.
Multi-Shard Migrations
Section titled “Multi-Shard Migrations”For multi-shard databases, Layeron applies migrations to every physical D1 database in the active D1 Plus shard set. The logical Database instance owns the migration history; individual D1 shards are implementation resources.
Safety Checks
Section titled “Safety Checks”Layeron flags potentially destructive SQL during migration planning:
drop tabletruncatealter table ... drop columnalter table ... renamedelete from ...(without awhereclause)update ...(without awhereclause)
[!WARNING] Destructive migrations require explicit metadata and deployment approval.
const database = db({ name: "main", sql: [ { name: "003_drop_deprecated_table", sql: "drop table if exists temp_logs;", destructive: true, }, ],})Practical Guidance
Section titled “Practical Guidance”- Create tables with
if not exists. - Add nullable columns or columns with defaults before writing application code that depends on them.
- Backfill data with explicit
whereranges or separate operational scripts. - Keep migration names descriptive enough for deployment review.
- Use raw SQL migrations for indexes, backfills, and SQLite expressions that are clearer in SQL.
Next Steps
Section titled “Next Steps”- Schema: Review typed table declarations that generate migration SQL.
- Raw SQL: Write direct SQLite/D1-compatible statements with parameters and result helpers.
- Batch and transactions: Group migration-adjacent data changes inside runtime operations.
- Capacity and sharding: Plan migration behavior for fixed, manual, and auto capacity.
- API reference: Review migration input, capacity options, sharding options, and runtime helpers.