Sequelize is a popular ORM for Node.js, but using it with TypeScript—especially for migrations—can be challenging. This guide will walk you through setting up Sequelize properly with TypeScript, focusing on database migrations.
Sequelize officially supports TypeScript, but its Sequelize-cli tool generates JavaScript files for migrations. This creates several issues:
- TypeScript errors in your project
- ESLint parsing errors
- Inconsistent codebase with mixed JS/TS files
Solution Overview
We'll implement a proper TypeScript setup for Sequelize migrations by:
- Creating a custom .sequelizerc configuration
- Setting up a workflow for TypeScript migrations
- Configuring project files correctly
- Creating a script to automate migration generation
Step 1: Project Setup
First, ensure your tsconfig.json is properly configured:
{
"compilerOptions": {
"target": "es2016",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"rootDir": "./src",
"baseUrl": "./",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"exclude": ["node_modules", "__tests__"],
"include": ["src/**/*.ts"]
}
Step 2: Create a Custom .sequelizerc File
Create a .sequelizerc file in your project root:
const { resolve } = require('path');
module.exports = {
config: resolve('build/db/config.js'),
'seeders-path': resolve('build/db/seeders'),
'migrations-path': resolve('build/db/migrations'),
'models-path': resolve('db/models')
};
Step 3: TypeScript Migration Workflow
To create and run migrations with TypeScript:
- Generate migration files using the CLI:
npx sequelize-cli migration:create --name add-some-table
The migration file will be generated in "build/db/migrations". Move the generated file to your actual application TypeScript migrations folder and rename it with a .ts extension.
Replace the content with this TypeScript template:
import { QueryInterface, DataTypes, QueryTypes } from 'sequelize';
/** @type {import("sequelize-cli").Migration} */
module.exports = {
up: (queryInterface: QueryInterface): Promise<void> => queryInterface.sequelize.transaction(
async (transaction) => {
// here go all migration changes
}
),
down: (queryInterface: QueryInterface): Promise<void> => queryInterface.sequelize.transaction(
async (transaction) => {
// here go all migration undo changes
}
)
};
- add necessary changes to the migration file
OR ANOTHER OPTION (RUNNING SCRIPT)
TO AUTOMATE THIS STEP YOU CAN CREATE A SCRIPT WHICH WILL AUTOMATICALLY CREATE THIS FILE
- Generate migration files using the CLI:
npx sequelize-cli migration:create --name add-some-table
import fs from 'fs';
import path from 'path';
// Get migration name from command-line arguments
const migrationName = process.argv[2];
if (!migrationName) {
console.error('❌ Please provide a migration name.');
process.exit(1);
}
// Define paths
const migrationsDir = path.resolve(__dirname, 'db/migrations');
const timestamp = new Date().toISOString().replace(/\D/g, '').slice(0, 14); // YYYYMMDDHHMMSS
const fileName = `${timestamp}-${migrationName}.ts`;
const filePath = path.join(migrationsDir, fileName);
// Migration template
const migrationTemplate = `import { QueryInterface, DataTypes } from "sequelize";
/** @type {import("sequelize-cli").Migration} */
export default {
up: async (queryInterface: QueryInterface): Promise<void> => {
await queryInterface.sequelize.transaction(async (transaction) => {
//your code here
});
},
down: async (queryInterface: QueryInterface): Promise<void> => {
await queryInterface.sequelize.transaction(async (transaction) => {
//your code here
});
},
};
`;
// Ensure migrations directory exists
if (!fs.existsSync(migrationsDir)) {
fs.mkdirSync(migrationsDir, { recursive: true });
}
// Write migration file
fs.writeFileSync(filePath, migrationTemplate);
console.log(`✅ Migration created: ${filePath}`);
To run this script:
npx ts-node path/to/generateMigration.ts add-some-table
Step 4: Running Migrations
To run migrations, you need to:
Compile your TypeScript files
Run the migrations on the compiled JavaScript files
Add these scripts to your package.json:
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
"build": "tsc -p .",
"start": "node build/dist/index.js",
"prestart": "npm install --production",
"migrate:generate":"npx ts-node src/generateMigration.ts",
"migrate": "tsc -p . & npx sequelize-cli db:migrate ",
"migrate:undo": "npx sequelize-cli db:migrate:undo ",
"migrate:undo:all": "npx sequelize-cli db:migrate:undo:all "
},
Conclusion
By following these steps, you've successfully set up Sequelize with TypeScript for migrations. This approach gives you:
- Type safety in your migrations
- Consistent codebase with TypeScript throughout
- Properly working ESLint
- Streamlined migration workflow
Your development process is now more robust and you can enjoy the benefits of TypeScript while using Sequelize migrations.
Top comments (0)