An Easy Guide to Managing Database Migrations with NestJS and PostgreSQL

Joseph ChimezieJoseph Chimezie
3 min read

Ever felt overwhelmed by the thought of database changes as your app evolves? Fear not! Migrations are here to rescue you from the complexity, turning daunting into the doable. Think of them as your app's organizational magic โ€“ like tidying up your app's room as it grows.

In this enchanting guide, we'll unveil the secrets of creating and managing migrations in your NestJS application, and we're bringing along PostgreSQL as your trusty database sidekick. With our step-by-step journey, you'll effortlessly navigate the realm of migrations, ensuring your database evolves gracefully alongside your app's progress.

Migrations might appear intricate at first glance, but with the power of NestJS, your loyal app framework, and the reliability of PostgreSQL, a fortress of a database system, you'll embark on a breeze-like journey of transforming complexities into simplicity.

Let's dive into this captivating adventure and unveil the art of migrations while enjoying the ride. Your app's organizational wizard awaits โ€“ are you ready to wield its magic? ๐Ÿช„๐ŸŒŸ

Before We Begin: What You Need

Before diving in, make sure you've got Node.js and npm ready to roll on your computer. Oh, and if you're new to PostgreSQL, don't worry โ€“ it's a friendly database that we'll use to store your app's data.

Step 1: Setting Up Your NestJS App

Let's get started:

  1. Open your terminal.

  2. If you haven't already, install NestJS globally:

    npm install -g @nestjs/cli

  3. Create your NestJS app:

    nest new my-nest-app

Step 2: Connecting to Your Database

  1. Install packages related to databases:

    npm install @nestjs/typeorm typeorm pg

  2. Open up app.module.ts and set up your database connection:

     import { Module } from '@nestjs/common';
     import { AppController } from './app.controller';
     import { AppService } from './app.service';
     import { TypeOrmModule } from '@nestjs/typeorm';
    
     @Module({
       imports: [
         TypeOrmModule.forRoot({
           type: 'postgres',
           host: 'localhost',
           port: 5432,
           password: 'postgres',
           username: 'postgres',
           entities: [],
           database: 'migration-tutorial',
           autoLoadEntities: true,
         }),
       ],
       controllers: [AppController],
       providers: [AppService],
     })
     export class AppModule {}
    

Step 3: Setting Up the Data Source

Create a data-source.ts file in the root of your project:

Then configure the data-source:

import { DataSource } from 'typeorm';

export default new DataSource({
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  password: 'postgres',
  username: 'postgres',
  database: 'migration-tutorial',
  migrations: ['src/migrations/**/*.ts'],
});

Step 4: Managing Migrations

4.1. Creating Migration Scripts

  1. Open your package.json file and add the migration scripts:

     "scripts": {
       "migrate": "typeorm-ts-node-esm -d data-source.ts migration:run",
       "migrate:revert": "typeorm-ts-node-esm -d data-source.ts migration:revert",
       "migrate:test": "typeorm-ts-node-esm -d data-source_test.ts migration:run",
       "migrate:test:revert": "typeorm-ts-node-esm -d data-source_test.ts migration:revert",
       "new:migration": "cd src/migrations && typeorm-ts-node-esm migration:create"
     }
    

4.2. Writing Migration Logic

  1. Create a folder named migrations inside the src directory.

  1. Run the following command to generate a new migration script:

     npm run new:migration
    
  2. Open the generated migration file in the src/migrations directory and modify the generated migration file

     // before modification 
     import { MigrationInterface, QueryRunner } from "typeorm"
    
     export class User1692166487661 implements MigrationInterface {
    
         public async up(queryRunner: QueryRunner): Promise<void> {
         }
    
         public async down(queryRunner: QueryRunner): Promise<void> {
         }
    
     }
    
     // after modification 
     import { MigrationInterface, QueryRunner, Table } from 'typeorm';
    
     export class User1692111030774 implements MigrationInterface {
       public async up(queryRunner: QueryRunner): Promise<void> {
         await queryRunner.createTable(
           new Table({
             name: 'user',
             columns: [
               {
                 name: 'id',
                 isGenerated: true,
                 type: 'int',
                 isPrimary: true,
               },
               {
                 name: 'full_name',
                 type: 'varchar',
                 isNullable: true,
                 default: null,
               },
             ],
           }),
           true,
         );
       }
    
       public async down(queryRunner: QueryRunner): Promise<void> {
         await queryRunner.dropTable('user');
       }
     }
    

4.3. Running Migration

Run migrations to apply changes to the database:

npm run migrate

You should get the following output in your terminal

4.3. Reverting Migration

If needed, you can revert a migration:

npm run migrate:revert

Wrapping Up: Your Database, Organized

You're now a migration maestro! These simple steps ensure your database stays neat and orderly as your app evolves. With NestJS and PostgreSQL as your companions, you're fully equipped to create and manage migrations like a pro. Happy coding and building your incredible app! ๐Ÿš€

3
Subscribe to my newsletter

Read articles from Joseph Chimezie directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Joseph Chimezie
Joseph Chimezie

Hi, I'm Joseph, a backend engineer with expertise in JavaScript, TypeScript, Node.js, Express.js, and Nest.js. I specialize in developing scalable backend solutions using databases like MongoDB, PostgreSQL, and MySQL. I have extensive experience in designing efficient database schemas, optimizing query performance, and ensuring data integrity. I excel in building RESTful APIs with a focus on authentication, authorization, and data validation. I'm skilled in Docker for containerization, enabling seamless deployment and scalability. I've worked on various projects, from startups to enterprise-level applications, collaborating with cross-functional teams to deliver high-quality software solutions. I stay updated with the latest backend technologies and enjoy tackling complex challenges. If you're looking for a dedicated backend engineer who can contribute to your team's success, feel free to reach out. Let's connect and discuss how we can create exceptional backend solutions together. Personal interests: In my free time, I enjoy listening to music and am always looking for ways to apply my technical skills to personal projects. I am also an active member of the Google Developers Group (GDG) Uyo Community.