Database Migration by @wshobson · Skillet
Database Migration Execute database migrations across ORMs and platforms with zero-downtime strategies, data transformation, and rollback procedures. Use when migrating databases, changing schemas, performing data transformations, or implementing zero-downtime deployment strategies.
Delete or overwrite files Install
It’s free, and every skill you add syncs into every AI tool on your computer, instantly.
true
email
type
Sequelize
STRING
unique
true
allowNull
false
createdAt
Sequelize
DATE
updatedAt
Sequelize
DATE
down
async
Sequelize
await
dropTable
"users"
TypeORM Migrations
import { MigrationInterface , QueryRunner , Table } from "typeorm" ;
export class CreateUsers1701234567 implements MigrationInterface {
public async up (queryRunner : QueryRunner ): Promise <void > {
await queryRunner.createTable (
new Table ({
name : "users" ,
columns : [
{
name : "id" ,
type : "int" ,
isPrimary : true ,
isGenerated : true ,
generationStrategy : "increment" ,
},
{
name : "email" ,
type : "varchar" ,
isUnique : true ,
},
{
name : "created_at" ,
type : "timestamp" ,
default : "CURRENT_TIMESTAMP" ,
},
],
}),
);
}
public async down (queryRunner : QueryRunner ): Promise <void > {
await queryRunner.dropTable ("users" );
}
}
Prisma Migrations // schema.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
createdAt DateTime @default(now())
}
// Generate migration: npx prisma migrate dev --name create_users
// Apply: npx prisma migrate deploy
Schema Transformations
Adding Columns with Defaults
module .exports = {
up : async (queryInterface, Sequelize ) => {
await queryInterface.addColumn ("users" , "status" , {
type : Sequelize .STRING ,
defaultValue : "active" ,
allowNull : false ,
});
},
down : async (queryInterface) => {
await queryInterface.removeColumn ("users" , "status" );
},
};
Renaming Columns (Zero Downtime)
module .exports = {
up : async (queryInterface, Sequelize ) => {
await queryInterface.addColumn ("users" , "full_name" , {
type : Sequelize .STRING ,
});
await queryInterface.sequelize .query ("UPDATE users SET full_name = name" );
},
down : async (queryInterface) => {
await queryInterface.removeColumn ("users" , "full_name" );
},
};
module .exports = {
up : async (queryInterface) => {
await queryInterface.removeColumn ("users" , "name" );
},
down : async (queryInterface, Sequelize ) => {
await queryInterface.addColumn ("users" , "name" , {
type : Sequelize .STRING ,
});
},
};
Changing Column Types module .exports = {
up : async (queryInterface, Sequelize ) => {
await queryInterface.addColumn ("users" , "age_new" , {
type : Sequelize .INTEGER ,
});
await queryInterface.sequelize .query (`
UPDATE users
SET age_new = CAST(age AS INTEGER)
WHERE age IS NOT NULL
` );
await queryInterface.removeColumn ("users" , "age" );
await queryInterface.renameColumn ("users" , "age_new" , "age" );
},
down : async (queryInterface, Sequelize ) => {
await queryInterface.changeColumn ("users" , "age" , {
type : Sequelize .STRING ,
});
},
};
Data Transformations
Complex Data Migration module .exports = {
up : async (queryInterface, Sequelize ) => {
const [users] = await queryInterface.sequelize .query (
"SELECT id, address_string FROM users" ,
);
for (const user of users) {
const addressParts = user.address_string .split ("," );
await queryInterface.sequelize .query (
`UPDATE users
SET street = :street,
city = :city,
state = :state
WHERE id = :id` ,
{
replacements : {
id : user.id ,
street : addressParts[0 ]?.trim (),
city : addressParts[1 ]?.trim (),
state : addressParts[2 ]?.trim (),
},
},
);
}
await queryInterface.removeColumn ("users" , "address_string" );
},
down : async (queryInterface, Sequelize ) => {
await queryInterface.addColumn ("users" , "address_string" , {
type : Sequelize .STRING ,
});
await queryInterface.sequelize .query (`
UPDATE users
SET address_string = CONCAT(street, ', ', city, ', ', state)
` );
await queryInterface.removeColumn ("users" , "street" );
await queryInterface.removeColumn ("users" , "city" );
await queryInterface.removeColumn ("users" , "state" );
},
};
Rollback Strategies
Transaction-Based Migrations module .exports = {
up : async (queryInterface, Sequelize ) => {
const transaction = await queryInterface.sequelize .transaction ();
try {
await queryInterface.addColumn (
"users" ,
"verified" ,
{ type : Sequelize .BOOLEAN , defaultValue : false },
{ transaction },
);
await queryInterface.sequelize .query (
"UPDATE users SET verified = true WHERE email_verified_at IS NOT NULL" ,
{ transaction },
);
await transaction.commit ();
} catch (error) {
await transaction.rollback ();
throw error;
}
},
down : async (queryInterface) => {
await queryInterface.removeColumn ("users" , "verified" );
},
};
Checkpoint-Based Rollback module .exports = {
up : async (queryInterface, Sequelize ) => {
await queryInterface.sequelize .query (
"CREATE TABLE users_backup AS SELECT * FROM users" ,
);
try {
await queryInterface.addColumn ("users" , "new_field" , {
type : Sequelize .STRING ,
});
const [result] = await queryInterface.sequelize .query (
"SELECT COUNT(*) as count FROM users WHERE new_field IS NULL" ,
);
if (result[0 ].count > 0 ) {
throw new Error ("Migration verification failed" );
}
await queryInterface.dropTable ("users_backup" );
} catch (error) {
await queryInterface.sequelize .query ("DROP TABLE users" );
await queryInterface.sequelize .query (
"CREATE TABLE users AS SELECT * FROM users_backup" ,
);
await queryInterface.dropTable ("users_backup" );
throw error;
}
},
};
Additional patterns and templates More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.
~1.8K tokens