DEV Community

Ian Sam Mungai
Ian Sam Mungai

Posted on

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)