Easy Guide to Running Sequelize Migrations with Typescript

Posted February 12, 2024
Easy Guide to Running Sequelize Migrations with Typescript

Have you tried to create Sequelize migrations within a Typescript app? I’m sure it challenging. Sequelize doesn’t provide a direct way to integrate migrations to a Typescript project. In this guide, you will a simple way to get your Typescript apps ready to run Sequelize migrations.

Related: Sequelize Models with Typescript using PostgreSQL and MySQL

The Challenge of Using Sequelize Migrations with Typescript

Sequelize generates it file in .js. However, you don’t have a way to initialize the Typescript app with Sequelize and get Sequelize .ts files. This means, that for you to inherit Typescript within Migrations, you need to rename .js files to .ts while rewriting some codes to use Typescript

Let’s dive into this guide and get a simple way to get Typescript ready with Sequelize migrations files.

Setting Up a Typescript Sequelize Node.js App

You need to ensure you have a ready and working Typescript Node.js project. if not follow these steps:

  • Initialize Node.js:
npm init -y
  • Install packages Typescript Sequelize Migrations packages. Now, you need Typescript Node.js Packages, database Drivers (I will use Postgres), and Sequelize Typescript dependencies. Use the following command to get them ready:
npm -install -g typescript
npm install sequelize sequelize-cli typescript ts-node @types/express @types/node @types/sequelize pg express node
  • Initialize Typescript
npx tsc --init    

Your application is now ready and you can start using Typescript and Sequelize to create migrations.

Initializing Typescript Sequelize Migration Files

Instead of using the Sequelize CLI directly, you will create a .sequelizerc file. This file will contain the path configuration that Sequelize will use to generate Migration files.

Update your .sequelizerc file as follows:

// Import the path module to work with file paths
const path = require("path");
// Export an object with configuration options for Sequelize CLI
module.exports = {
  // Configuration file path for Sequelize
  config: path.resolve("src/db", "sequelize.config.js"),
  // Path to the directory where models are stored
  "models-path": path.resolve("src/db", "models"),
  // seeders
  "seeders-path": path.resolve("src/db", "seeders"),
  // Set the path to the directory to store migrations
  "migrations-path": path.resolve("src/db", "migrations"),
};

Keep in mind you are still in your Typescript project. Then run the following command to initialize Sequelize Migrations. This means Sequelize will create a src/db directory and initialize the Sequelize Migration-related files Use the following command:

npx sequelize init

You will have:

  • src/db/models/index.js for models
  • src/db/migrations directory stores migration files. These changes are in a structured way.
  • src/db/seeders contains seeder files. But you will have to create them if you need to add seeding.
  • sequelize.config.js that you will use to add database connections to Postgres or your database choice.

Everything here will be generated using JavaScript. We only need Sequelize to use Typescript. Let’s add that is the next step.

Generating Typescript Sequelize Migration

I will use a Product example. Therefore, you need to use the npx sequelize-cli db:create --name Product command. However, I don’t want to use this command. The easiest way is to create a Model while Creating the migrations files. Use the following command (You can change I to match your case):

npx sequelize-cli model:generate --name Product --attributes name:string,price:decimal,quantity:integer

Get more info here to create your first migration.

Adding Sequelize Migrations Database Connection

You have to add database connections for Postgres to Sequelize (sequelize.config.js). Most likely it has Typescript errors if you have intellicence in your Editor. You will need to update it as follows:

require('ts-node/register');

module.exports = {
  username: 'postgres',
  password: 'pass',
  database: 'shop',
  host: 'localhost',
  dialect: 'postgres',

};

But remember to add your database credentials and have the database shop already created.

At the same time, Typescript can’t import and use a sequelize.config.js file within a .ts file. This means you need to create an additional file with a Database connection using Typescript code. In the same directory of the sequelize.config.js file (src/db), create a sequelize.ts file and add the following code:

import { Sequelize } from 'sequelize';

let connection: Sequelize = new Sequelize("shop", 'postgres', 'pass', {
  host: "localhost",
  dialect: 'postgres', 
});

export default connection;

Creating Sequelize Migration Model with Typescript Files

Now, the model’s folder contains an index.js file which creates the database connection. Likewise, the Sequelize Migration will use the product.js model to start Migration and generate Migrations files. This is where the whole app will start to change.

First, rename the src/model/product.js file to src/model/product.ts. Make sure it has the .ts extension. This is still your Model. To allow Typescript while letting Sequelize use this model file when running migrations, you will change the code a bit.

Delete the src\db\models/index.ts file. You will not need it.

In this case, you will point the database connection from the sequelize.ts file the add your Model using Typescript. The whole src/model/product.ts should look as follows:

import { Model, DataTypes } from 'sequelize';
import connection from '../sequelize'; 

interface ProductAttributes {
  id?: number;
  name: string;
  price: number;
  quantity: number;
  updatedAt?: Date;
  deletedAt?: Date;
  createdAt?: Date;
}


class Product extends Model<ProductAttributes> implements ProductAttributes {
  public id!: number;
  public name!: string;
  public price!: number;
  public quantity!: number;
  public readonly updatedAt!: Date;
  public readonly createdAt!: Date;
}

Product.init(
  {
    id: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.NUMBER,
    },
    name: {
      allowNull: false,
      type: DataTypes.STRING,
    },
    price: {
      allowNull: false,
      type: DataTypes.DECIMAL(10, 2),
    },
    quantity: {
      allowNull: false,
      type: DataTypes.INTEGER,
      defaultValue: 0,
    },
    createdAt: {
      allowNull: false,
      type: DataTypes.DATE,
    },
    updatedAt: {
      allowNull: false,
      type: DataTypes.DATE,
    },
  },
  {
    sequelize: connection,
    modelName: 'Product',
  }
);

export default Product;

This will be enough to let you use the same model in your app and still let Sequelize run Typescript and its migrations because:

  • You import the configuration for the database from sequelize.ts.
  • You have a Sequelize instance (connection).
  • You have passed this Sequelize instance to the Sequelize option when initializing the model.

This approach will use the configuration defined in sequelize.ts to establish the database connection while giving you flexibility and centralization to manage Sequelize Migrations within Typescript.

Updating Sequelize Migration file to Typescript

Go to src\db\migrations\20240212150331-create-product.js and change it to src\db\migrations\20240212150331-create-product.ts. You only need to add .ts. However, this file will have a few errors such as using implicitly has an ‘any’ type

Just update this file with Typescript. It’s only a few lines that need to change. But here is the complete code for deeper reference:

import { QueryInterface, DataTypes } from 'sequelize';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface: QueryInterface, Sequelize:typeof DataTypes) { 
    await queryInterface.createTable('Products', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      name: {
        type: Sequelize.STRING
      },
      price: {
        type: Sequelize.DECIMAL
      },
      quantity: {
        type: Sequelize.INTEGER
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  async down(queryInterface:QueryInterface, Sequelize:any) {
    await queryInterface.dropTable('Products');
  }
};

Running your First Sequelize Migration with Typescript

Sequelize uses npx sequelize-cli db:migrate Migrations. Still with Sequelize and Typescript, you will use the same command to run the Migrations as follows:

npx sequelize-cli db:migrate

Go to your database and confirm you have the database table created from the above Sequelize Typescript migration.

Testing Sequelize Migrations with a Typescript API

Now, the migrations are working. This app should be able to use the src\db\models\product.ts file and access the database and the created Product table with a Server.

Go ahead and create an index.ts file inside the src folder. To make this simple, I will only use the Product model and try to send a get request. If I get an array (although empty), this means the whole setup works file. Add the following code to the index.ts file as such:

import Express from "express";
import Product from "../src/db/models/product";
const App = Express();

App.use(Express.json());

// Define route to handle GET request for all products
App.get('', async (req, res) => {
  try{
    res.send(await Product.findAll())
  }catch(err){
    console.error(err);
    res.status(500).send("Unexpected error occurred on server!");
  }
})

const port = 3000;
App.listen(port, () => {
  console.log(`Server is running on port ${port}`)
})

Run your app:

ts-node src/index.ts

Open http://localhost:3000/ and you get any items you have in your table, an empty array if not.

Related: Create|Generate|Run Sequelize CLI db Migrations With Node.js

Conclusion

I hope this guide helped to at least get started using Typescript while running Sequelize migrations. Check the code used here on GitHub repo.

Easy Guide to Running Sequelize Migrations with Typescript

Written By:

Joseph Chege