A complete database migration system has been set up for your project with the following components:
-
src/server/utils/migration-runner.ts- Core migration engine- Executes migrations in order
- Tracks migration history
- Handles rollbacks
- Creates new migration files
-
scripts/migrate.ts- CLI tool for managing migrations- Command-line interface for all migration operations
-
src/server/migrations/- Migration files directory- Stores all migration scripts
- Includes initial auth tables migration
-
src/server/utils/neon-db.ts- Updated with migration functionsinitializeMigrationTable()- Creates migrations tracking tablerecordMigration()- Records executed migrationshasMigrationRun()- Checks if migration was executedgetExecutedMigrations()- Lists all executed migrations
npm run migrate:statusShows which migrations have been executed and which are pending.
npm run migrate:upExecutes all migrations that haven't been run yet.
npm run migrate create "add user profiles table"Creates a new timestamped migration file.
npm run migrate:downReverts the most recently executed migration (if it has a down function).
| Command | Description |
|---|---|
npm run migrate:status |
Show migration status |
npm run migrate:up |
Run all pending migrations |
npm run migrate:down |
Rollback last migration |
npm run migrate create <name> |
Create new migration file |
npm run migrate create "add user settings table"This creates: src/server/migrations/20260213_120530_add_user_settings_table.ts
import { getDbClient } from '../utils/neon-db.js';
export default {
async up() {
const sql = getDbClient();
await sql`
CREATE TABLE user_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
theme VARCHAR(20) DEFAULT 'light',
language VARCHAR(10) DEFAULT 'en',
notifications BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
)
`;
await sql`CREATE INDEX idx_user_settings_user_id ON user_settings(user_id)`;
console.log('✅ User settings table created');
},
async down() {
const sql = getDbClient();
await sql`DROP TABLE IF EXISTS user_settings CASCADE`;
console.log('✅ User settings table dropped');
}
};npm run migrate:upnpm run migrate:status📊 Migration Status:
Migration Files:
────────────────────────────────────────────────────────────────────────────────
✅ 20260213_000000_create_initial_auth_tables
Executed: 2/13/2026, 1:16:28 AM (1485ms)
✅ 20260213_120530_add_user_settings_table
Executed: 2/13/2026, 12:05:30 PM (234ms)
⏸️ 20260213_143000_add_user_profiles (pending)
────────────────────────────────────────────────────────────────────────────────
Total: 3 | Executed: 2 | Pending: 1
- All migrations are tracked in the
migrationstable - Prevents duplicate executions
- Records execution time and checksums
- Migrations run in timestamp order
- Ensures consistency across environments
- Validation before execution
- Detailed error messages
- Transaction support (when using SQL transactions)
- Each migration can include a
downfunction - Safely revert changes if needed
await sql`
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR(255) NOT NULL,
content TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
)
`;await sql`ALTER TABLE users ADD COLUMN phone VARCHAR(20)`;await sql`CREATE INDEX idx_posts_created_at ON posts(created_at)`;await sql`
INSERT INTO roles (name, permissions) VALUES
('admin', '["read", "write", "delete"]'),
('user', '["read", "write"]'),
('viewer', '["read"]')
`;-
Test Migrations Locally First
- Always test on development database
- Verify both
upanddownfunctions
-
One Change Per Migration
- Keep migrations focused and atomic
- Easier to debug and rollback
-
Never Edit Executed Migrations
- Create a new migration instead
- Editing changes the checksum
-
Write Reversible Migrations
- Always include
downfunction when possible - Some operations can't be reversed (data deletion)
- Always include
-
Use Descriptive Names
- ✅
add_email_verification_to_users - ❌
update_usersorfix1
- ✅
- Check error message in console
- Fix the migration file
- Delete failed record:
DELETE FROM migrations WHERE status = 'failed' - Run migration again
DROP TABLE migrations CASCADE;
DROP TABLE auth_audit_log CASCADE;
DROP TABLE sessions CASCADE;
DROP TABLE users CASCADE;Then run: npm run migrate:up
The system includes one migration that creates:
- users table - User accounts with authentication
- sessions table - Active user sessions with JWT tokens
- auth_audit_log table - Security audit trail
- All necessary indexes for performance
This migration is already executed in your database.
Your migration system is ready to use. Create new migrations as your database schema evolves!
For detailed documentation, see: src/server/migrations/README.md