Introduction
When talking about connecting databases in NestJS, we have TypeORMModule for MySQL and MongooseModule for MongoDB available. There might be a scenario where we are required to connect to a database with our own/custom database module. This could be an example when we want to connect to MongoDB native driver without using mongoose ORM.
Prerequisite
This is for someone who is beginner in NestJS and has the understanding of TypeScript. Optionally, has used TypeORM or Mongoose module in NestJS projects.
Custom Providers in NestJS
If you have been working in NestJS for a while (obviously if you are reading this blog), you may have used providers which is created using dependency injection. we can create our own custom providers concerning the requirements of the project. One such custom provider and its module we are going to create here. Read more about custom providers here
Modules in NestJS
Modules are nothing but segregation of an application into smaller parts that are individually responsible for certain roles or features of the application. In NestJS, we have at least one module, which is at the root level. We are going to create a custom database module for MongoDB.
Implementation and Explanation
Create new nest project as,
$ nest new custom-db-project
Now generate the custom database module as,
$ nest generate module database
Now create provider for database module as,
$ touch src/database/database.provider.ts
We will also install MongoDB driver,
$ npm install --save mongodb
...
Let's Code,
- First we will create the provider for mongodb driver as,
import * as mongodb from 'mongodb';
export const databaseProviders = [
{
provide: 'DATABASE_CONNECTION',
useFactory: async (): Promise<mongodb.Db> => {
try {
const client = await mongodb.MongoClient.connect(
'mongodb://localhost',
{
useUnifiedTopology: true,
},
)
const db = client.db('test');
return db;
} catch (error) {
throw error;
}
}
}
]
- We will define the provider as variable in which provider name is 'DATABASE_CONNECTION', this will be used when we are injecting database provider in other modules so that we can use it.
- In useFactory, we are initialising the actual mongodb driver and return the 'db' that is the exact variable to used in other modules.
- We are first establishing the connection using 'connect' function and then linking to 'test' database and finally returning 'db'. ...
- Now we will come back to database module which we generated and has the basic implementation of the Module annotation of NestJS.
import { Module } from '@nestjs/common';
@Module({})
export class DatabaseModule {}
- We will modify this structure as,
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.provider';
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders]
})
export class DatabaseModule { }
- Here we specified that module has to consider the objects in 'databaseProviders' array as the provider, so hence it can be recognised the project structure.
- Also, we need to export these providers so that they can be used in other modules. ...
Now we will create another module just to demonstrate the use of Database Module.
Generate todo Module as,
$ nest generate module todo
Generate todo service so that we can interact with DatabaseModule,this also generate 'todo.service.spec.ts' file which is a test file and we can ignore it as we are not discussing it here.
$ nest generate service todo
Now we will add the DatabaseModule in TodoModule,
import { Module } from '@nestjs/common';
import { DatabaseModule } from 'src/database/database.module';
import { TodoService } from './todo.service';
@Module({
imports: [DatabaseModule],
providers: [TodoService]
})
export class TodoModule { }
- In imports we have defined that DatabaseModule this will allow the todo module structure to make the use of database providers. ...
- Now we will inject Database provider in todo service provider, hence the service will have the access to database functions.
import { Inject, Injectable } from '@nestjs/common';
import * as mongodb from 'mongodb';
@Injectable()
export class TodoService {
constructor(@Inject('DATABASE_CONNECTION') private db: mongodb.Db) { }
async getAllTodos(): Promise<any[]> {
return await this.db.collection('todos').find({}).toArray();
}
}
- Remember that we had named the database provider name as 'DATABASE_CONNECTION', you can see it is being used here in constructor to import/inject the 'db'.
- And in 'getAllTodos' function we can see how 'db' is used to find data in the given collection.
Finally we implemented the custom database module for MongoDB in NestJS, also this implementation does not force us to make the use of schemas unlike mongoose, which can be a requirement in some projects.
Top comments (2)
Great article yo...
How do I dynamically pass the database name to the provider, 'DATABASE_PROVIDER'?
It's really helpful, thank you