Claude-skill-registry database-migration-helper
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.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/database-migration-helper" ~/.claude/skills/majiayu000-claude-skill-registry-database-migration-helper && rm -rf "$T"
manifest:
skills/data/database-migration-helper/SKILL.mdsource content
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 }
# Create migration npx prisma migrate dev --name add_user_role # Apply migrations npx prisma migrate deploy # Reset database (development only) npx prisma migrate reset # Create migration without applying npx prisma migrate dev --create-only
TypeORM Migrations
// migrations/1234567890-AddUserRole.ts import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'; export class AddUserRole1234567890 implements MigrationInterface { name = 'AddUserRole1234567890'; public async up(queryRunner: QueryRunner): Promise<void> { // Add role column await queryRunner.addColumn( 'users', new TableColumn({ name: 'role', type: 'enum', enum: ['user', 'admin', 'moderator'], default: "'user'", }) ); // Create index await queryRunner.createIndex( 'users', new Index({ name: 'IDX_USERS_ROLE', columnNames: ['role'], }) ); // Data migration - set existing users to 'user' role await queryRunner.query( `UPDATE users SET role = 'user' WHERE role IS NULL` ); } public async down(queryRunner: QueryRunner): Promise<void> { // Remove index await queryRunner.dropIndex('users', 'IDX_USERS_ROLE'); // Remove column await queryRunner.dropColumn('users', 'role'); } }
# Generate migration from entity changes npm run typeorm migration:generate -- -n AddUserRole # Create empty migration npm run typeorm migration:create -- -n DataMigration # Run migrations npm run typeorm migration:run # Revert last migration npm run typeorm migration:revert
Alembic (Python) Migrations
# alembic/versions/001_add_user_role.py """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 identifiers revision = '001' down_revision = None branch_labels = None depends_on = None def upgrade(): # Create enum type role_enum = postgresql.ENUM('user', 'admin', 'moderator', name='role') role_enum.create(op.get_bind()) # Add column op.add_column( 'users', sa.Column('role', role_enum, nullable=False, server_default='user') ) # Create index op.create_index('ix_users_role', 'users', ['role']) # Data migration op.execute(""" UPDATE users SET role = 'admin' WHERE email IN (SELECT email FROM admin_emails) """) def downgrade(): # Remove index op.drop_index('ix_users_role', table_name='users') # Remove column op.drop_column('users', 'role') # Drop enum op.execute('DROP TYPE role')
# Create migration alembic revision -m "add user role" # Auto-generate migration from models alembic revision --autogenerate -m "add user role" # Run migrations alembic upgrade head # Rollback one migration alembic downgrade -1 # Show current version alembic current
Sequelize Migrations (Node.js)
// migrations/20240101120000-add-user-role.js 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { // Add column await queryInterface.addColumn('users', 'role', { type: Sequelize.ENUM('user', 'admin', 'moderator'), allowNull: false, defaultValue: 'user', }); // Add index await queryInterface.addIndex('users', ['role'], { name: 'users_role_idx', }); // Data migration using raw SQL await queryInterface.sequelize.query(` UPDATE users SET role = 'admin' WHERE is_admin = true `); // Remove old column await queryInterface.removeColumn('users', 'is_admin'); }, down: async (queryInterface, Sequelize) => { // Re-add old column await queryInterface.addColumn('users', 'is_admin', { type: Sequelize.BOOLEAN, defaultValue: false, }); // Reverse data migration await queryInterface.sequelize.query(` UPDATE users SET is_admin = true WHERE role = 'admin' `); // Remove index await queryInterface.removeIndex('users', 'users_role_idx'); // Remove column and enum await queryInterface.removeColumn('users', 'role'); await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_users_role"'); }, };
Raw SQL Migration Template
-- Up Migration -- migrations/001_add_user_role_up.sql -- Add enum type (PostgreSQL) CREATE TYPE user_role AS ENUM ('user', 'admin', 'moderator'); -- Add column ALTER TABLE users ADD COLUMN role user_role NOT NULL DEFAULT 'user'; -- Create index CREATE INDEX idx_users_role ON users(role); -- Data migration UPDATE users SET role = 'admin' WHERE id IN (1, 2, 3); -- Down Migration -- migrations/001_add_user_role_down.sql -- Remove index DROP INDEX IF EXISTS idx_users_role; -- Remove column ALTER TABLE users DROP COLUMN IF EXISTS role; -- Drop type DROP TYPE IF EXISTS user_role;
Complex Data Migration Example
// Data transformation migration export class MigrateUserData1234567890 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { // 1. Create new table structure 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() ) `); // 2. Migrate data with transformation 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 `); // 3. Drop old table await queryRunner.query(`DROP TABLE users_old`); // 4. Rename new table await queryRunner.query(`ALTER TABLE users_new RENAME TO users`); } public async down(queryRunner: QueryRunner): Promise<void> { // Reverse migration // ... implementation } }
Best Practices
- Always include both
andup
migrationsdown - 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