How to seed database using TypeORM in a NestJS project

How to seed database using TypeORM in a NestJS project

Complete database seeding configuration from scratch in a NestJS project using TypeORM

Motivation

It is often required to seed a database with initial data during development. In a NestJS + TypeORM project, we can do that with the help of typeorm-extension.

Initial project setup

Let's initialize our project using Nest CLI.

nest new nest-typeorm

Navigate to the project folder and install the necessary dependencies. In this example, I am using PostgreSQL, therefore I have added pg as a dependency.

cd nest-typeorm
npm install --save @nestjs/typeorm typeorm pg

Database connection

Let's create a separate Nest module for handling database-related tasks.

nest g module db

Create the data-source.ts file inside the src/db directory. This file will contain database connection configurations.

// src/db/data-source.ts
import { config } from 'dotenv';
import { DataSourceOptions } from 'typeorm';

config();

export const dataSourceOptions: DataSourceOptions = {
  type: 'postgres',
  host: process.env.DB_HOST,
  port: +process.env.DB_PORT,
  username: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  entities: ['dist/resources/**/*.entity.js'],
  synchronize: true, // do not set it true in production application
};

Setting synchronize: true shouldn't be used in production - otherwise you can lose production data. You should use migration instead. I have an article on How to setup TypeORM migrations in a NestJS project.

Use this dataSourceOptions for setting up TypeOrmModule inside the DbModule that we generated using Nest CLI.

// src/db/db.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { dataSourceOptions } from './data-source';

@Module({
  imports: [TypeOrmModule.forRoot(dataSourceOptions)],
})
export class DbModule {}

Now if you run the project using npm run start:dev command, it should be able to connect to the database. However, you have to make sure the database server is up and running and the database with the name that we specified in the data-source file is already created.

Generate an entity

Generate users resource using the CLI.

nest g resource resource/users

It will generate the users module, controller, service, DTOs and entity file inside src/resource/users directory.

Let's update the user.entity.ts file and add some columns there.

// src/resources/users/entities/user.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ name: 'first_name' })
  firstName: string;

  @Column({ name: 'last_name' })
  lastName: string;
}

Since we set synchronize: true in our datasource options, if we run this project using npm start command, it should create the user table in our database.

The database should already be running in the specified host and port before running the app.

If the user table is created successfully then we can start working on the seeding part.

Seeding

We are going to use typeorm-extension for seeding our database. Let's install it first.

npm install typeorm-extension --save

Now we need a seeder class for seeding the user entity. Seeder interface is exported from typeorm-extension, we just need to implement it. Let's create the class in src/db/seeds/user.seeder.ts file.

// src/db/seeds/user.seeder.ts
import { Seeder, SeederFactoryManager } from 'typeorm-extension';
import { DataSource } from 'typeorm';
import { User } from '../../resources/users/entities/user.entity';

export default class UserSeeder implements Seeder {
  public async run(
    dataSource: DataSource,
    factoryManager: SeederFactoryManager,
  ): Promise<void> {
    await dataSource.query('TRUNCATE "user" RESTART IDENTITY;');

    const repository = dataSource.getRepository(User);
    await repository.insert({
      firstName: 'Mazedul',
      lastName: 'Islam',
    });
  }
}

We have implemented the run method from the Seeder interface. When we execute the seed command using the typeorm-extension CLI, this run function will be executed. The run method has access to the DataSource and SeederFactoryManager instances, as they are passed as arguments.

At first, we truncate the user table and restart the identity sequence to make sure the first record will get ID 1. Then we get the user repository from the dataSource and insert our first user object into the table using the repository.

Now we need to tell the dataSource that we have this seeder class. To do that, let's update our src/db/data-source.ts file.

// src/db/data-source.ts
import { SeederOptions } from 'typeorm-extension';
...
export const dataSourceOptions: DataSourceOptions & SeederOptions = {
  ...
  seeds: ['dist/db/seeds/**/*.js'],
};

We have added seeds array into our dataSourceOptions object. Notice, we are using the glob pattern for pointing to the seeds directory inside the dist/db/ directory. The glob pattern will not work if you point to the typescript files inside src/db/seeds/ directory. Since our build command will generate the dist directory anyway, there is no harm using the .js files from there.

Now, add a script for seeding in the package.json.

// package.json
{
  ...
  "scripts": {
    ...
    "seed": "npm run build && ts-node ./node_modules/typeorm-extension/dist/cli/index.js seed -d ./src/db/data-source.ts"
  }
}

In this command, we build the project first, then run the seed command from typeorm-extension with the help of ts-node.

Note: using the -d ./src/db/data-source.ts we are passing the path to the datasource file to the seed command.

Let's invoke the script.

npm run seed

Once we execute the command the user table in the database should be populated with one user.

Factories to generate bulk data

To generate random data for an entity, create a factory for the desired entity.

The factory callback provides an instance of the faker library as a function argument, to populate the entity with random data.

Create a factory function in src/db/factories/user.factory.ts file.

// src/db/factories/user.factory.ts
import { setSeederFactory } from 'typeorm-extension';
import { User } from '../../resources/users/entities/user.entity';

export default setSeederFactory(User, (faker) => {
  const user = new User();

  const sexFlag = faker.number.int(1);
  const sex: 'male' | 'female' = sexFlag ? 'male' : 'female';

  user.firstName = faker.person.firstName(sex);
  user.lastName = faker.person.lastName(sex);

  return user;
});

Now update the user.seeder.ts file and utilize this factory function for generating random users.

// src/db/seeds/user.seeder.ts
...
export default class UserSeeder implements Seeder {
  public async run(
    dataSource: DataSource,
    factoryManager: SeederFactoryManager,
  ): Promise<void> {
    ...
    const userFactory = factoryManager.get(User);
    // save 1 factory generated entity, to the database
    await userFactory.save();

    // save 5 factory generated entities, to the database
    await userFactory.saveMany(5);
  }
}

Since we are using SeederFactoryManager instance to get the user factory, we need to update our dataSourceOptions and make it aware of the factory files.

// src/db/data-source.ts
...
export const dataSourceOptions: DataSourceOptions & SeederOptions = {
  ...
  factories: ['dist/db/factories/**/*.js'],
};

Our factory setup is done. Run the seed command again.

npm run seed

Check the user table, it should have 1 hardcoded user and 6 more random users.

Seed programmatically

It is also possible to seed the database from the codebase, without using the CLI commands. To do that, we need to get access to the DataSource instance. There is a function called runSeeders exported from typeorm-extension which can be used to seed data programmatically. This is especially useful in e2e tests. You can seed your in-memory test database on-the-fly before each test. Probably I'll cover this in another article for testing a NestJS application.

Conclusion

We have successfully configured SQL database seeding functionality using TypeORM and typeorm-extension package. We also get some insights on how to seed programmatically using runSeeders function provided by typeorm-extension.

You can find the full project here: https://github.com/mazid1/nestjs-typeorm/tree/feature/seed

In the next article, I'll write about how to test a NestJS application.


I'm open for a remote full-time/contract/freelance job for the position of Full-stack Software Engineer. If you are hiring, ping me at