| name | database-migration-helper |
| description | Creates database migrations with proper schema changes, data migrations, and rollback support for various ORMs (Prisma, TypeORM, Alembic, etc.). Use when managing database schema changes. |
Database Migration Helper Skill
Expert at creating safe, reversible database migrations across different frameworks and tools.
When to Activate
- "create database migration for [change]"
- "generate migration to add [table/column]"
- "write data migration for [transformation]"
Prisma Migrations
// prisma/schema.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@map("users")
}
model Post {
id Int @id @default(autoincrement())
title String
slug String @unique
content String @db.Text
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([slug])
@@index([authorId, publishedAt])
@@map("posts")
}
enum Role {
USER
ADMIN
MODERATOR
}
npx prisma migrate dev --name add_user_role
npx prisma migrate deploy
npx prisma migrate reset
npx prisma migrate dev --create-only
TypeORM Migrations
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';
export class AddUserRole1234567890 implements MigrationInterface {
name = 'AddUserRole1234567890';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
'users',
new TableColumn({
name: 'role',
type: 'enum',
enum: ['user', 'admin', 'moderator'],
default: "'user'",
})
);
await queryRunner.createIndex(
'users',
new Index({
name: 'IDX_USERS_ROLE',
columnNames: ['role'],
})
);
await queryRunner.query(
`UPDATE users SET role = 'user' WHERE role IS NULL`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropIndex('users', 'IDX_USERS_ROLE');
await queryRunner.dropColumn('users', 'role');
}
}
npm run typeorm migration:generate -- -n AddUserRole
npm run typeorm migration:create -- -n DataMigration
npm run typeorm migration:run
npm run typeorm migration:revert
Alembic (Python) Migrations
"""add user role
Revision ID: 001
Revises:
Create Date: 2024-01-01 12:00:00
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = '001'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
role_enum = postgresql.ENUM('user', 'admin', 'moderator', name='role')
role_enum.create(op.get_bind())
op.add_column(
'users',
sa.Column('role', role_enum, nullable=False, server_default='user')
)
op.create_index('ix_users_role', 'users', ['role'])
op.execute("""
UPDATE users
SET role = 'admin'
WHERE email IN (SELECT email FROM admin_emails)
""")
def downgrade():
op.drop_index('ix_users_role', table_name='users')
op.drop_column('users', 'role')
op.execute('DROP TYPE role')
alembic revision -m "add user role"
alembic revision --autogenerate -m "add user role"
alembic upgrade head
alembic downgrade -1
alembic current
Sequelize Migrations (Node.js)
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'role', {
type: Sequelize.ENUM('user', 'admin', 'moderator'),
allowNull: false,
defaultValue: 'user',
});
await queryInterface.addIndex('users', ['role'], {
name: 'users_role_idx',
});
await queryInterface.sequelize.query(`
UPDATE users
SET role = 'admin'
WHERE is_admin = true
`);
await queryInterface.removeColumn('users', 'is_admin');
},
down: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('users', 'is_admin', {
type: Sequelize.BOOLEAN,
defaultValue: false,
});
await queryInterface.sequelize.query(`
UPDATE users
SET is_admin = true
WHERE role = 'admin'
`);
await queryInterface.removeIndex('users', 'users_role_idx');
await queryInterface.removeColumn('users', 'role');
await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_users_role"');
},
};
Raw SQL Migration Template
CREATE TYPE user_role AS ENUM ('user', 'admin', 'moderator');
ALTER TABLE users ADD COLUMN role user_role NOT NULL DEFAULT 'user';
CREATE INDEX idx_users_role ON users(role);
UPDATE users SET role = 'admin' WHERE id IN (1, 2, 3);
DROP INDEX IF EXISTS idx_users_role;
ALTER TABLE users DROP COLUMN IF EXISTS role;
DROP TYPE IF EXISTS user_role;
Complex Data Migration Example
export class MigrateUserData1234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE users_new (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
profile JSONB NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
)
`);
await queryRunner.query(`
INSERT INTO users_new (id, email, profile, created_at)
SELECT
id,
email,
jsonb_build_object(
'firstName', first_name,
'lastName', last_name,
'phone', phone,
'address', jsonb_build_object(
'street', address_street,
'city', address_city,
'zip', address_zip
)
) as profile,
created_at
FROM users_old
`);
await queryRunner.query(`DROP TABLE users_old`);
await queryRunner.query(`ALTER TABLE users_new RENAME TO users`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
}
}
Best Practices
- Always include both
up and down migrations
- Test migrations on copy of production data
- Use transactions for data migrations
- Add indexes after data insertion for large tables
- Version control all migrations
- Never modify existing migrations after deployment
- Use descriptive migration names
- Add comments explaining complex migrations
- Test rollback procedures
- Back up database before major migrations
- Use batching for large data migrations
- Monitor migration execution time
- Handle NULL values properly
- Validate data after migration
Output Checklist
- ✅ Migration file created
- ✅ Up migration implemented
- ✅ Down migration implemented
- ✅ Indexes added
- ✅ Data migration (if needed)
- ✅ Constraints added
- ✅ Tested on sample data
- 📝 Migration notes documented