Database Migration#
Migration Files#
| Type | Directory | Description |
|---|---|---|
| Core | api/src/migrations/core/ | System tables (users, billing, etc.) |
| Business | api/src/migrations/user/ | Your business tables |
Create Migration#
Create a SQL file in api/src/migrations/user/, named by date:
sql-- api/src/migrations/user/20260605.sql
CREATE TABLE IF NOT EXISTS todos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
title VARCHAR(255) NOT NULL,
completed BOOLEAN DEFAULT false,
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT,
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT
);
Run Migration#
bashnpm run db:migrate
How It Works#
The migration script (api/src/scripts/migrate.ts):
- Creates a
migrationstable (tracks executed migrations) - Reads all
.sqlfiles sorted by filename - Skips already-executed ones, runs new ones
- Core migrations (
core/) run first, then business migrations (user/)
Conventions#
- Use
CREATE TABLE IF NOT EXISTSfor idempotency - Use
ALTER TABLE ... ADD COLUMN IF NOT EXISTSfor new columns - Timestamps:
BIGINT(Unix epoch seconds) - IDs:
UUID - Business tables must have a
user_idcolumn (data isolation) - Indexes:
CREATE INDEX IF NOT EXISTS - Data seeding:
ON CONFLICT DO NOTHING - Amounts:
INTEGERin cents