Table of Contents
Overview
Working with multiple databases in NestJS applications requires a clear understanding of database connections and module decorators. This article will guide you through the process with practical examples. Learn how to enhance your NestJS applications by integrating them with numerous database systems smoothly and effectively.
Introduction to NestJS and Databases
NestJS, a Node.js framework for building efficient and scalable server-side applications, is known for its extensibility. Supporting multiple database systems is just one of its many features. Before diving into the configuration and implementation, make sure you are familiar with TypeScript and NestJS’s core fundamentals, including modules, services, and controllers.
Throughout this tutorial, we’ll tackle the integration of multiple data sources step by step, starting with basic configurations and proceeding with how to create different database connections within a single NestJS application.
Setting Up Your NestJS Project
npm i -g @nestjs/cli
nestjs new project-name
Firstly, create your new NestJS project using the Nest CLI. This command sets up a new project with a default database connection. You’ll modify this to accommodate additional databases.
Adding Database Modules
For each database you want to use, you’ll need to add the corresponding module to your project. For the sake of this guide, we’ll assume you’re using PostgreSQL and MongoDB.
npm i --save @nestjs/typeorm typeorm pg
npm i --save @nestjs/mongoose mongoose
This will install the necessary packages for PostgreSQL and MongoDB, respectively.
Configuring Multiple Database Connections
PostgreSQL Configuration
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'test',
password: 'test',
database: 'test',
entities: [],
synchronize: true,
}),
// ... other imports
],
// ...
})
export class AppModule {}
Here, you are setting up the PostgreSQL database connection using the TypeOrmModule’s forRoot method. Ensure you configure the connection options like host, port, username, and password as per your environment.
MongoDB Configuration
import { MongooseModule } from '@nestjs/mongoose';
//... other imports
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
// ... other imports
],
// ...
})
export class AppModule {}
You establish MongoDB’s connection using the MongooseModule’s forRoot method and pass in the URI for the local MongoDB server. In this example, replace ‘localhost’ and ‘nest’ with your own parameters.
Separate Modules for Database Connections
To keep your application organized, it’s recommended to create separate modules for each database connection. Create a PostgresModule and MongoDBModule, and use forRootAsync to establish asynchronous connections.
Creating PostgresModule
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: 'postgres',
// ... other options
}),
}),
],
})
export class PostgresModule {}
In this PostgresModule, you define an asynchronous configuration factory that will be resolved at runtime.
Creating MongoDBModule
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRootAsync({
useFactory: () => ({
uri: 'mongodb://localhost/nest',
// ... other options
}),
}),
],
})
export class MongoDBModule {}
The MongoDBModule is set up similar to the PostgresModule but using MongooseModule to connect to the MongoDB database.
Working with Entities and Schemas
For both PostgreSQL and MongoDB, you need to define entities and schemas to interact with the databases.
Defining PostgreSQL Entities
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
// ... other columns
}
Defining entities in PostgreSQL is straightforward with TypeORM, where you use decorators to mark classes and fields as database elements.
Defining MongoDB Schemas
import * as mongoose from 'mongoose';
export const CatSchema = new mongoose.Schema({
name: String,
// ... other properties
});
In MongoDB, you define schemas using the mongoose library which offers a schema-based solution to model your application data.
Querying Data Across Databases
Once you have set up your database connections and defined your entities and schemas, the next step is querying data. Here’s how you can perform data operations using the repository pattern for PostgreSQL and direct schema methods for MongoDB.
Fetching Data from PostgreSQL
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
findAll(): Promise<User[]> {
return this.userRepository.find();
}
// ... other methods
}
This is an example service in NestJS where you inject the repository and use it to fetch all users from your PostgreSQL database.
Fetching Data from MongoDB
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Cat, CatDocument } from './cat.schema';
@Injectable()
export class CatService {
constructor(
@InjectModel(Cat.name) private catModel: Model<CatDocument>,
) {}
findAll(): Promise<Cat[]> {
return this.catModel.find().exec();
}
// ... other methods
}
Similarly, in the CatService, you inject the Cat model and use it to fetch all cats from the MongoDB database.
Advanced Configuration and Use Cases
Further configuration can include establishing named connections, using transactions across databases, and optimizing performance through connection pooling. Be aware that transactions across different database systems can be complex and might require specific handling.
Conclusion
Deploying multiple databases in a NestJS application allows for a versatile data management strategy. By following structured and separated module patterns, your applications can utilize various databases seamlessly. Remember to test configurations, entities, and services thoroughly to ensure that your connections work as expected under real application scenarios.