- Published on
Setup database migration in NestJS with Sequelize and MySQL
- Authors
- Name
- Gen9X
- X
- @gen9xdotcom
When building a system that uses a database, you will certainly encounter a situation where you need to change the structure of the database. It can be changing, adding, modifying, deleting columns of a table or creating new foreign key links to another table...
This is when we need to write commands to migrate the database. This article will introduce you to how to write migration using Sequelize and MySQL in NestJS. Let's get started now.
1. Create new NestJS project
Use Nest CLI to create a new project as follows:
# Install nest cli if you have not installed it before
$ npm i -g @nestjs/cli
# Create a new project named database-migration
$ nest new database-migration
2. Install modules
$ npm install @nestjs/sequelize sequelize sequelize-typescript mysql2 dotenv sequelize-cli
3. Create an env file
# Database
DATABASE_USERNAME=root
DATABASE_PASSWORD=Abcd@1234
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_NAME=databse-migration
DATABASE_DIALECT=mysql
4. Create database module
In the src
folder, create a new folder database
to configure the connection to our database. Inside the database folder will include the following files/folders:
├── src
│ ├── database
│ │ ├── constant
│ │ │ ├── index.ts
│ │ ├── models
│ │ │ ├── user.entity.ts
│ │ ├── database.config.ts
│ │ ├── database.module.ts
│ │ ├── database.provider.ts
│ │ ├── sequelize.config.js
index.ts
export const SEQUELIZE = 'SEQUELIZE'
user.entity.ts
import { Column, DataType, Model, Table } from 'sequelize-typescript'
@Table
export class User extends Model<User> {
@Column({
type: DataType.STRING,
allowNull: false,
})
name: string
@Column({
type: DataType.STRING,
unique: true,
allowNull: false,
})
phone: string
}
database.config.ts
const dotenv = require('dotenv')
dotenv.config()
import { SequelizeOptions } from 'sequelize-typescript'
import { Dialect } from 'sequelize'
export const sequelizeConfig: SequelizeOptions = {
dialect: 'mysql' as Dialect,
host: process.env.DATABASE_HOST || 'localhost',
port: Number(process.env.DATABASE_PORT) || 3306,
username: process.env.DATABASE_USERNAME || 'root',
password: process.env.DATABASE_PASSWORD || '',
database: process.env.DATABASE_NAME || 'your_database',
logging: false,
}
database.provider.ts
import { Logger } from '@nestjs/common'
import { Sequelize } from 'sequelize-typescript'
import { SEQUELIZE } from './constant'
import { sequelizeConfig } from './database.config'
import { User } from './models/user.entity'
export const databaseProviders = [
{
provide: SEQUELIZE,
useFactory: async () => {
const sequelize = new Sequelize({
...sequelizeConfig,
logging: Logger.log,
define: {
timestamps: true,
underscored: true,
paranoid: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: 'deleted_at',
charset: 'utf8mb4',
},
retry: {
max: 20,
match: [
/ConnectionError/,
/SequelizeConnectionError/,
/SequelizeConnectionRefusedError/,
/SequelizeHostNotFoundError/,
/SequelizeHostNotReachableError/,
/SequelizeInvalidConnectionError/,
/SequelizeConnectionTimedOutError/,
/SequelizeConnectionAcquireTimeoutError/,
/Connection terminated unexpectedly/,
],
},
})
sequelize.addModels([User])
await sequelize.sync()
return sequelize
},
},
]
database.module.ts
import { Module } from '@nestjs/common'
import { databaseProviders } from './database.provider'
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
sequelize.config.js
// src/config/sequelize-config.js
require('ts-node/register')
const { sequelizeConfig } = require('./database.config.ts')
module.exports = sequelizeConfig
Now you need to import DatabaseModule into AppModule
app.module.ts
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { DatabaseModule } from './database/database.module'
@Module({
imports: [DatabaseModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
5. Create migration file
First you need to create a .sequelizerc
file to declare the paths to the database configuration file.
.sequelizerc
const path = require('path')
module.exports = {
config: path.resolve('src/database/sequelize.config.js'),
'migrations-path': path.resolve('src/database/migrations'),
'seeders-path': path.resolve('src/database/seeders'),
}
For convenience, we will add the following 3 commands to the package.json
file
"db:migrate:up": "npx sequelize-cli db:migrate",
"db:migrate:down": "npx sequelize-cli db:migrate:undo",
"db:migrate:new": "npx sequelize-cli migration:generate"
Now try to create a new migration file with the following command:
npm run db:migrate:new --name user-add-email
After running the above command, a javascript file will be generated in the migrations folder. You need to change the file format from .js
to .ts
.
Suppose in this case we want to add the email
column to the users
table, we will write the code as follows:
import { QueryInterface, DataTypes } from 'sequelize'
module.exports = {
up: (queryInterface: QueryInterface): Promise<void> =>
queryInterface.sequelize.transaction(async (transaction) => {
await queryInterface.addColumn('users', 'email', {
type: DataTypes.STRING,
})
}),
down: (queryInterface: QueryInterface): Promise<void> =>
queryInterface.sequelize.transaction(async (transaction) => {
// here go all migration undo changes
await queryInterface.removeColumn('users', 'email')
}),
}
6. Run the migration file
Type the following command in the terminal:
npm run db:migrate:up
And the result after running the command will be:
> database-migration@0.0.1 db:migrate:up
> npx sequelize-cli db:migrate
Sequelize CLI [Node: 20.17.0, CLI: 6.6.2, ORM: 6.37.3]
Loaded configuration file "src\database\sequelize.config.js".
== 20240929140925-user-add-email: migrating =======
== 20240929140925-user-add-email: migrated (0.107s)
Finally, you just need to update the user model, add the email field and you're done
import { Column, DataType, Model, Table } from 'sequelize-typescript'
@Table
export class User extends Model<User> {
@Column({
type: DataType.STRING,
allowNull: false,
})
name: string
@Column({
type: DataType.STRING,
unique: true,
allowNull: false,
})
phone: string
@Column({
type: DataType.STRING,
})
email: string
}
In case you want to cancel the previously run migration file and return to the old state, you can run the following command:
npm run db:migrate:down
So I have guided you how to create a new migration file and execute it with just one command line. The entire sample code of the project is published on Github, you can refer to and use them. If you find it interesting, please share and support me.