Database Migrations
Strav uses a schema-driven migration system that automatically generates database migrations from your schema changes. Migrations are organized by domains and executed in batches with full rollback support.
How Migrations Work
Strav migrations follow a unique approach:
- Schema-first: Define your data models using schemas, not raw SQL
- Auto-generation: Migrations are automatically generated by comparing schemas with the current database
- Domain-scoped: Migrations can be scoped to specific domains (public, tenant, etc.)
- Batched execution: Migrations run in numbered batches for precise rollback control
- Transactional: Each migration runs in a transaction for atomic operations
CLI Commands
Generate Migration
Generate migrations from schema changes:
# Generate migration for default (public) domain
strav generate:migration --message "Add user authentication"
# Generate migration for specific domain
strav generate:migration --message "Add tenant settings" --scope tenant
# Available aliases
strav migration:generate -m "Migration message"
strav g:migration -m "Migration message"
Options:
-m, --message: Descriptive message for the migration-s, --scope: Domain scope (public, tenant, factory, marketing, etc.)
Run Migrations
Execute all pending migrations:
# Run migrations for default domain
strav migrate
# Run migrations for specific domain
strav migrate --scope tenant
# Alternative command
strav migration:run -s public
Example output:
Running pending migrations...
Applied 3 migration(s) in batch 5:
- 1704067200000
- 1704153600000
- 1704240000000
Rollback Migrations
Roll back migrations by batch:
# Rollback last batch
strav rollback
# Rollback specific batch
strav rollback --batch 5
# Rollback specific domain
strav rollback --scope tenant
# Alternative command
strav migration:rollback --batch 3
Example output:
Rolling back batch 5...
Rolled back 3 migration(s) from batch 5:
- 1704240000000
- 1704153600000
- 1704067200000
Fresh Migration
Drop all tables and re-run migrations from scratch:
# Fresh migration for default domain
strav migration:fresh
# Fresh migration for specific domain
strav migration:fresh --scope tenant
Migration File Structure
Each migration creates a versioned directory with organized SQL files:
database/
└── migrations/
└── public/ // Domain scope
└── 1704067200000/ // Timestamp version
├── manifest.json // Migration metadata
├── enums/
│ ├── up.sql // Create/modify enums
│ └── down.sql // Rollback enums
├── tables/
│ ├── users/
│ │ ├── up.sql // Create/modify users table
│ │ └── down.sql // Rollback users table
│ └── profiles/
│ ├── up.sql
│ └── down.sql
├── constraints/
│ ├── up.sql // Add foreign keys, unique constraints
│ └── down.sql // Remove constraints
└── indexes/
├── up.sql // Create indexes
└── down.sql // Drop indexes
Migration Manifest
Each migration includes a manifest.json file with metadata and execution order:
{
"version": "1704067200000",
"message": "Add user authentication",
"generatedAt": "2024-01-01T12:00:00.000Z",
"summary": {
"tablesToCreate": 2,
"tablesToDrop": 0,
"tablesToModify": 1,
"enumsToCreate": 1,
"enumsToModify": 0,
"enumsToDrop": 0
},
"executionOrder": {
"up": [
"enums/up.sql",
"tables/users/up.sql",
"tables/profiles/up.sql",
"constraints/up.sql",
"indexes/up.sql"
],
"down": [
"indexes/down.sql",
"constraints/down.sql",
"tables/profiles/down.sql",
"tables/users/down.sql",
"enums/down.sql"
]
}
}
Multi-Domain Migrations
Strav supports multiple database domains for complex applications:
| Domain | Purpose | Migration Path | Tracking Table |
|---|---|---|---|
public |
Main application tables | database/migrations/public/ |
_strav_migrations |
tenant |
Multi-tenant specific tables | database/migrations/tenant/ |
_strav_tenant_migrations |
factory |
Data factory/seeding tables | database/migrations/factory/ |
_strav_factory_migrations |
marketing |
Marketing and analytics | database/migrations/marketing/ |
_strav_marketing_migrations |
Each domain has its own migration tracking table and can be managed independently.
Workflow Examples
Basic Migration Workflow
// 1. Update your schema
// database/schemas/user.ts
export default defineSchema('user', {
archetype: Archetype.Entity,
fields: {
id: t.ulid().primaryKey(),
email: t.string().email().unique(),
password: t.string(),
role: t.enum(['user', 'admin']).default('user'),
// Added new field
emailVerifiedAt: t.timestamptz().nullable(),
},
})
# 2. Generate migration
strav generate:migration -m "Add email verification"
# ✓ Migration generated: 1704067200000
# 3. Review generated SQL files
cat database/migrations/public/1704067200000/tables/user/up.sql
# ALTER TABLE "user" ADD COLUMN "email_verified_at" TIMESTAMPTZ;
# 4. Run the migration
strav migrate
# ✓ Applied 1 migration(s) in batch 6
Multi-Domain Workflow
# 1. Check available domains
strav generate:migration --scope invalid_domain
# Error: Invalid domain: invalid_domain. Available domains: public, tenant, factory
# 2. Generate tenant-specific migration
strav generate:migration -m "Add tenant settings" -s tenant
# 3. Run tenant migrations
strav migrate -s tenant
# 4. Check migration status
strav migration:status -s tenant
Rollback Scenario
# 1. Something went wrong, rollback last batch
strav rollback
# ✓ Rolled back 2 migration(s) from batch 6
# 2. Fix the issue in your schemas
# 3. Generate new migration
strav generate:migration -m "Fix user table issues"
# 4. Run again
strav migrate
# ✓ Applied 1 migration(s) in batch 7
Migration Tracking
Strav tracks applied migrations in dedicated tracking tables:
-- Example tracking table structure
CREATE TABLE "_strav_migrations" (
"id" SERIAL PRIMARY KEY,
"version" VARCHAR(255) NOT NULL,
"batch" INTEGER NOT NULL,
"executed_at" TIMESTAMPTZ DEFAULT NOW()
);
The tracking system enables:
- Batch rollbacks: Roll back entire batches atomically
- Migration history: Track when and in what order migrations were applied
- Domain isolation: Separate tracking per domain
- Conflict prevention: Prevent running the same migration twice
Error Handling
Migration errors are handled gracefully with full transaction rollback:
# If a migration fails mid-batch
strav migrate
# Error: Migration 1704067200000 failed: column "invalid_column" does not exist
# ✓ Transaction rolled back - database unchanged
Common error scenarios:
- SQL errors: Invalid SQL syntax or constraint violations
- Schema conflicts: Schema changes conflict with existing data
- Domain errors: Invalid domain scope specified
- File errors: Missing migration files or corrupted manifest
Best Practices
Schema Changes
- Make incremental schema changes rather than large rewrites
- Test schema changes in development before production
- Use descriptive migration messages
- Review generated SQL before running migrations
Production Migrations
- Always backup your database before running migrations
- Test rollback procedures in staging environments
- Consider maintenance windows for destructive changes
- Monitor migration performance for large tables
Multi-Domain Management
- Keep domain schemas focused and separate
- Use consistent naming conventions across domains
- Document domain purposes and dependencies
- Coordinate cross-domain changes carefully