From backend
Write TypeORM migrations in the Packmind monorepo to manage and version database schema changes with consistent logging, reversible rollbacks, and shared helpers. Use this skill whenever creating or modifying database tables, columns, or foreign-key relationships — including any time schema changes need to be tracked, versioned, or rolled back. Invoke even if the user just says "add a column", "create a table", or "update the DB schema".
How this skill is triggered — by the user, by Claude, or both
Slash command
/backend:how-to-write-typeorm-migrations-in-packmindThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Write TypeORM migrations in the Packmind monorepo to manage database schema changes effectively while ensuring proper logging and rollback capabilities.
Write TypeORM migrations in the Packmind monorepo to manage database schema changes effectively while ensuring proper logging and rollback capabilities.
IMPORTANT: Always create migration files using the TypeORM CLI command. Never create migration files manually. The CLI automatically generates a timestamp prefix and sets up the basic structure.
# Create new migration file (TypeORM will automatically add timestamp)
npx typeorm migration:create packages/migrations/src/migrations/DescriptiveName
Implement the migration class with PackmindLogger for comprehensive logging. Include try-catch blocks and log start, progress, completion, and any errors.
import { MigrationInterface, QueryRunner } from 'typeorm';
import { PackmindLogger, LogLevel } from '@packmind/shared';
export class YourMigrationName1234567890 implements MigrationInterface {
private readonly logger = new PackmindLogger(
'YourMigrationName1234567890',
LogLevel.DEBUG,
);
public async up(queryRunner: QueryRunner): Promise<void> {
this.logger.info('Starting migration: YourMigrationName');
try {
// Your migration logic here
this.logger.info('Migration YourMigrationName completed successfully');
} catch (error) {
this.logger.error('Migration YourMigrationName failed', {
error: error.message,
});
throw error;
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
this.logger.info('Starting rollback: YourMigrationName');
try {
// Your rollback logic here
this.logger.info('Rollback YourMigrationName completed successfully');
} catch (error) {
this.logger.error('Rollback YourMigrationName failed', {
error: error.message,
});
throw error;
}
}
}
Use TypeORM Table class with shared helper columns (uuidMigrationColumn, timestampsMigrationColumns) from @packmind/shared to create new tables with consistent structure.
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
import {
timestampsMigrationColumns,
uuidMigrationColumn,
} from '@packmind/shared/src/database/migrationColumns';
export class CreateYourTable1234567890 implements MigrationInterface {
private readonly table = new Table({
name: 'your_table',
columns: [
uuidMigrationColumn, // Standard UUID primary key
{
name: 'name',
type: 'varchar',
},
{
name: 'description',
type: 'text',
isNullable: true,
},
...timestampsMigrationColumns, // created_at, updated_at
],
});
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(this.table);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable(this.table);
}
}
Use TableColumn class for adding individual columns or raw SQL for multiple columns. Always make new columns nullable initially for backward compatibility.
// Single column with TableColumn
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';
export class AddColumnsToTable1234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
'your_table',
new TableColumn({
name: 'new_column',
type: 'varchar',
isNullable: true,
}),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn('your_table', 'new_column');
}
}
// Multiple columns with raw SQL
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "your_table"
ADD COLUMN "column1" varchar NULL,
ADD COLUMN "column2" varchar NULL,
ADD COLUMN "column3" varchar NULL
`);
}
Create foreign key relationships using TableForeignKey class with proper cascade behavior. Always handle foreign keys before dropping related tables.
import { MigrationInterface, QueryRunner, TableForeignKey } from 'typeorm';
export class AddForeignKey1234567890 implements MigrationInterface {
private readonly foreignKey = new TableForeignKey({
columnNames: ['parent_id'],
referencedColumnNames: ['id'],
referencedTableName: 'parent_table',
onDelete: 'CASCADE',
});
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createForeignKey('child_table', this.foreignKey);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropForeignKey('child_table', this.foreignKey);
}
}
Execute migrations using npm scripts for both local development and Docker environments. Test both up and down migrations.
# Local development
npm run typeorm migration:run
npm run typeorm migration:revert
npm run typeorm migration:show
# Docker environment
npm run typeorm migration:run -- -d datasourceDocker.ts
Always include comprehensive logging with PackmindLogger, wrap operations in try-catch blocks, write reversible migrations with corresponding down operations, use descriptive names with verbs (Add, Remove, Update, Create, Drop), and handle foreign key dependencies properly when dropping tables.
npx claudepluginhub packmindhub/packmind-marketplace --plugin backendCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.