Migrations with TypeORM in NestJs
Migrations are a crucial part of database management and versioning in NestJS applications using TypeORM. TypeORM provides migration support to help you manage database schema changes over time.
Why We Need Migrations in NestJs ?
As we all know that NestJs provide auto-sync for our entity changes to the database, then the first question that was raised in our head that why we need migration?
So, the answer is that we can use auto-sync functionality, but in some cases when our application is Live, and we have data in our database, then some direct modifications in our database might generate issues. So it is Unsafe to use synchronize on production.
Advantages
- We can run migration whenever we need.
- Reduce error generation on production.
- We can generate migration files from entity changes directly. We don’t need to write migration files.
- This is particularly useful when working in teams or deploying updates to production systems without disrupting existing data.
- Each migration file represents a specific set of changes, making it easier to track and manage schema modifications over time.
Disadvantage
- We need to generate migration files for every changes in our entity. After that we need to run it to alter database.
- If you’re new to migrations or TypeORM, there may be a learning curve involved in understanding the concepts and best practices associated with migrations. This can slow down initial development and require additional effort to grasp the migration workflow.
- When performing database schema changes, there is always a risk of data loss if not handled carefully. Modifying or deleting columns, tables, or relationships in migrations without proper backup strategies or safeguards can lead to unintentional data loss
Example
Suppose you have an entity named Student something like this:
import { BaseEntity, Column,Entity, PrimaryGeneratedColumn} from 'typeorm';
@Entity('students')
export class Students extends BaseEntity {
@PrimaryGeneratedColumn()
id: string;
@Column()
name: string;
@Column()
marks: number;
}
Now you need to alter “name” column with “student_name” then you need to create a migration file with the below SQL query
ALTER TABLE "students" ALTER COLUMN "name" RENAME TO "student_name";
TypeORM provides such functionality that you can store such queries and execute them to alter your database. This functionality is migrations.
How to Create New Migrations?
Before creating migrations you need to set up your TypeORM config file something like:
{
"type": "postgres",
"host": "postgres",
"port": 5432,
"username": "postgres",
"password": "postgres",
"database": "elite_opinio",
"entities": [
"/usr/src/eliteopinio/backend/dist/@config/../**/entities/*.entity.{js,ts}"
],
"synchronize": false,
"migrations": [
"src/migrations/*.ts"
],
"migrationsTableName": "migrations_TypeORM",
"cli": {
"migrationsDir": "src/migrations"
},
"seeds": [
"/usr/src/eliteopinio/backend/dist/**/entities/*.seeder.{js,ts}"
],
"factories": [
"/usr/src/eliteopinio/backend/dist/**/entities/*.factory.{js,ts}"
]
}
First of all, you need to close auto sync by setting “synchronize”: false
Then you need to specify migration files path in “cli”: {“migrationsDir”: “src/migrations”}, where all your migration files will generate.
While running migrations it will take files from “migrations” : [“src/migrations/*.ts”] path.
Also, we need to add a script for TypeORM in package.json file like:
"TypeORM": "ts-node ./node_modules/TypeORM/cli.js -f ./ormconfig.json"
After setting the config file you need to create a new migration file with: TypeORM migration:create -n PostRefactoring
This command will generate a new file in src folder like:
import {MigrationInterface, QueryRunner} from "TypeORM";
export class Student12345 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {}
public async down(queryRunner: QueryRunner): Promise<void> {}
}
This file has contain two methods “up” and “down”. “up” is where you need to write your new SQL changes and “down” is used to revert those changes.
Now to alter our column we need to write query something like:
import {MigrationInterface, QueryRunner} from "TypeORM";
export class Student12345 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "students" ADD "status" boolean NOT NULL DEFAULT false`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "students" DROP COLUMN "status"`);
}
}
Run Migration
To run all migrations we need to execute the command:
npm run TypeORM migration:run
It will execute the file and alter the column of the student’s table.
Now, if we follow this process then we need to write queries for every single change. And execute migrations every time. But TypeORM provides functionality to generate migration automatically from entity changes.
For doing that you just need to run the below command:
npm run TypeORM migration:generate -- -n MigrationName
This command will generate all new changes in entities that are not in the database. So you can generate files and make changes in the database by running migrations.
Conclusion
Migration is mostly helpful for live websites. Like if we create any new relationship and our sync is on. But there is some data mismatching on a server then it will generate an error during build creation. So, as we have seen migration provides a clear way of database schema generation. We are experts in the development of ReactJS. Do you have a project? Let’s talk about it!