skills/dexploarer/hyper-forge/database-migration-helper

database-migration-helper

SKILL.md

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 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
Weekly Installs
3
GitHub Stars
6
First Seen
9 days ago
Installed on
opencode3
claude-code3
github-copilot3
codex3
amp3
cline3