Complete guide to database migrations using Drizzle ORM in the GitHub Actions CI/CD pipeline.
The SaaS boilerplate uses Drizzle ORM for database migrations with automatic execution in the CI/CD pipeline:
Migration Strategy:
Migration Flow:
pnpm db:generatepnpm db:migrateDatabase setup and migration configuration:
File: drizzle.config.ts
import { defineConfig } from 'drizzle-kit'
import initDotEnv from './src/db/scripts/env'
initDotEnv()
export default defineConfig({
schema: './src/db/models/*', // Schema location
out: './drizzle/migrations', // Migration output directory
dialect: 'postgresql', // Database type
dbCredentials: {
url: process.env.DATABASE_URL as string,
},
verbose: true, // Detailed migration logs
strict: true, // Strict type checking
introspect: {
casing: 'camel', // Convert snake_case to camelCase
},
})Package.json migration commands:
{
"scripts": {
"db:generate": "drizzle-kit generate", // Generate migration files
"db:migrate": "drizzle-kit migrate", // Apply migrations
"db:push": "drizzle-kit push --force", // Push schema directly (dev only)
"db:pull": "drizzle-kit pull", // Pull schema from database
"db:drop": "drizzle-kit drop", // Drop all tables (careful!)
"db:studio": "drizzle-kit studio", // Open Drizzle Studio
"db:check": "tsx src/db/scripts/check.ts", // Check database connection
"db:seed": "tsx src/db/scripts/seed.ts", // Seed database with data
"db:clear": "tsx src/db/scripts/clear.ts", // Clear all data
"db:reset-seed": "pnpm db:clear && pnpm db:push && pnpm db:seed"
}
}The boilerplate includes a complete database schema managed by Drizzle ORM with PostgreSQL, including:
All schema changes are managed through Drizzle migrations and automatically applied during deployment.
How migrations run in GitHub Actions:
File: .github/workflows/production.yml
- name: Install Drizzle Kit
run: npm install -g drizzle-kit
- name: Run database migrations
run: pnpm db:migrate
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
Migration Process:
pnpm db:migrate with production DATABASE_URLFile: src/db/scripts/migrate.ts
#!/usr/bin/env node
import { sql } from 'drizzle-orm'
import { drizzle } from 'drizzle-orm/node-postgres'
import { migrate } from 'drizzle-orm/node-postgres/migrator'
import pg from 'pg'
import initDotEnv from './env'
initDotEnv()
const runMigrate = async () => {
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not defined')
}
const client = new pg.Client({
connectionString: process.env.DATABASE_URL,
})
await client.connect()
const db = drizzle(client)
console.log('⏳ Running migrations...')
const start = Date.now()
// Critical: Create UUID extension first
await db.execute(sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`)
// Run all pending migrations
await migrate(db, { migrationsFolder: './drizzle/migrations' })
const end = Date.now()
console.log('✅ Migrations completed in', end - start, 'ms')
process.exit(0)
}
try {
await runMigrate()
} catch (error) {
console.error('❌ Migration failed')
console.error(error)
process.exit(1)
}Key Features:
uuid-ossp extension required for UUID primary keysStandard workflow for database changes:
// Update TypeScript schema files in src/db/models/
export const users = pgTable('user', {
// ... existing fields
newField: text('new_field'), // Add new field
})pnpm db:generate
# Creates migration file in drizzle/migrations/pnpm db:migrate
# Applies migration to local databasegit add . && git commit -m "Add new field"
git push origin main
# GitHub Actions will run migration automaticallyThe CI/CD pipeline ensures safe migrations:
Migration best practices:
Your database migrations are automatically handled by the CI/CD pipeline. When you push schema changes, Drizzle will generate and execute the necessary delta migrations during deployment.