DEV Community

Ian Sam Mungai
Ian Sam Mungai

Posted on

2 2

Create Custom Database Module in NestJS with TypeORM MySQL/PostgreSQL

Intro

First, Check out these articles for
Custom Database Setup for Sequelize: https://www.freecodecamp.org/news/build-web-apis-with-nestjs-beginners-guide/
Custom Database Setup for MongoDB: https://dev.to/10xtarun/create-custom-database-module-in-nestjs-4kim
I got a lot of inspiration from these articles especially from the first by Victor

Setup Project

We Create new nest project as,

$ nest new custom-db-project
Enter fullscreen mode Exit fullscreen mode

We will also install all we need,

$ npm install --save @nestjs/typeorm @nestjs/config typeorm mysql2 dotenv

Enter fullscreen mode Exit fullscreen mode

Now generate the custom database module as,

$ nest generate module core/database
Enter fullscreen mode Exit fullscreen mode

...
Let's Code

Database Interface

Inside the database folder, create an interfaces folder, then create a dbConfig.interface.ts file inside it. This is for the database configuration interface.

Each of the database environments should optionally have the following properties.

export interface IDatabaseConfigAttributes {
    username?: string;
    password?: string;
    database?: string;
    host?: string;
    port?: number;
    type?: string;
    urlDatabase?: string;
    entities?: string[];
    migrationsTableName?: string;
    migrations?: string[];
    cli?: {
        migrationsDir?: string;
    }
    synchronize?: boolean;
}

export interface IDatabaseConfig {
    development: IDatabaseConfigAttributes;
    test: IDatabaseConfigAttributes;
    production: IDatabaseConfigAttributes;
}
Enter fullscreen mode Exit fullscreen mode

Database Configuration

Now, let’s create a database environment configuration. Inside the database folder, create a database.config.ts file.

import * as dotenv from 'dotenv';
import { IDatabaseConfig } from './interfaces/dbConfig.interface';

dotenv.config();

let entities = [__dirname + '/**/*.entity{.ts,.js}']

let migrationsDir = 'src/migration';
let migrations = [migrationsDir + '/*.ts']

export const databaseConfig: IDatabaseConfig = {
    development: {
        username: process.env.DB_USER,
        password: process.env.DB_PASS,
        database: process.env.DB_NAME_DEVELOPMENT,
        host: process.env.DB_HOST,
        port: +process.env.DB_PORT,
        type: process.env.DB_DIALECT,
        entities: entities,
        migrationsTableName: process.env.DB_MIGRATION_TABLE_NAME,
        migrations: migrations,
        cli: {
            migrationsDir: migrationsDir
        },
        synchronize: true
    },
    test: {
        username: process.env.DB_USER,
        password: process.env.DB_PASS,
        database: process.env.DB_NAME_TEST,
        host: process.env.DB_HOST,
        port: +process.env.DB_PORT,
        type: process.env.DB_DIALECT,
        entities: entities,
        migrationsTableName: process.env.DB_MIGRATION_TABLE_NAME,
        migrations: migrations,
        cli: {
            migrationsDir: migrationsDir
        },
        synchronize: true
    },
    production: {
        username: process.env.DB_USER,
        password: process.env.DB_PASS,
        database: process.env.DB_NAME_PRODUCTION,
        host: process.env.DB_HOST,
        port: +process.env.DB_PORT,
        type: process.env.DB_DIALECT,
        entities: entities,
        migrationsTableName: process.env.DB_MIGRATION_TABLE_NAME,
        migrations: migrations,
        cli: {
            migrationsDir: migrationsDir
        },
        synchronize: true
    },
};
Enter fullscreen mode Exit fullscreen mode

The environment will determine which configuration should be used.

.env file

On our project root folder, create a .env file

DB_HOST=localhost
DB_PORT=3306
DB_USER=database_user_name
DB_PASS=database_password
DB_DIALECT=postgres
DB_NAME_TEST=test_database_name
DB_NAME_DEVELOPMENT=development_database_name
DB_NAME_PRODUCTION=production_database_name
DB_MIGRATION_TABLE_NAME=migration

Enter fullscreen mode Exit fullscreen mode

Don't forget to add .env to your .gitignore file.

Import the @nestjs/config

Import the @nestjs/config into your app root module:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DatabaseModule } from './core/database/database.module';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    DatabaseModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Victor Pointed out that Setting the ConfigModule.forRoot({ isGlobal: true }) to isGlobal: true will make the .env properties available throughout the application.

Database Provider

Let’s create a database provider. Inside the database folder, create a file called database.providers.ts.

The core directory will contain all our core setups, configuration, shared modules, pipes, guards, and middlewares.

In the database.providers.ts let's add

import { TypeOrmModule } from '@nestjs/typeorm';
import { TYPEORM ,DEVELOPMENT, TEST, PRODUCTION } from '../constants';
import { databaseConfig } from './database.config';

export const databaseProviders = [{
    provide: TYPEORM,
    useFactory: async () => {
      let config;
      switch (process.env.NODE_ENV) {
      case DEVELOPMENT:
         config = databaseConfig.development;
         break;
      case TEST:
         config = databaseConfig.test;
         break;
      case PRODUCTION:
         config = databaseConfig.production;
         break;
      default:
         config = databaseConfig.development;
      }

      const typeOrm = TypeOrmModule.forRoot(config);

      return typeOrm
    },
}];

Enter fullscreen mode Exit fullscreen mode

Here, the application decides what environment we are currently running on and then chooses the environment configuration.

Best practice: It is a good idea to keep all string values in a constant file and export it to avoid misspelling those values. You'll also have a single place to change things.

Inside the core folder, create a constants folder and inside it create an index.ts file. Paste the following code:

export const TYPEORM = 'TYPEORM';
export const DEVELOPMENT = 'development';
export const TEST = 'test';
export const PRODUCTION = 'production';
Enter fullscreen mode Exit fullscreen mode

I hope this article helps someone. Check out the article by Victor to get started with Nestjs

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay