NestJS: Connecting to Different Databases Based on Routes

Updated: February 6, 2024 By: Guest Contributor Post a comment

Overview

In modern web development, the need to interact with multiple databases within a single application is increasingly common. This requirement arises from various business needs, including separating data storage for security reasons, optimizing performance, or simply due to the nature of the data being dealt with. NestJS, a progressive Node.js framework for building efficient and scalable server-side applications, provides a robust system for managing different database connections. In this tutorial, we will explore how to dynamically connect to different databases based on the routes accessed in a NestJS application.

Prerequisites

  1. Basic understanding of TypeScript
  2. Familiarity with NestJS framework
  3. Node.js and npm installed

Setting Up Your NestJS Project

First, let’s create a new NestJS project if you haven’t got one already:

npm i -g @nestjs/cli
nestjs new my-database-project
cd my-database-project

Once your project is set up, ensure that you have the necessary database drivers and ORM libraries installed. For this tutorial, we’ll assume you’re working with TypeORM, a popular choice within NestJS applications:

npm install --save @nestjs/typeorm typeorm

Configuring Multiple Database Connections

Next, let’s configure TypeORM to connect to multiple databases. You’ll need to modify the app.module.ts and set up different connections. Here’s how a basic configuration might look:

// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      name: 'default', // Default connection
      ... // Other default connection settings
    }),
    TypeOrmModule.forRoot({
      name: 'secondary', // Secondary connection
      ... // Other secondary connection settings
    })
  ],
})
export class AppModule {}

We specify each connection with a unique name to distinguish between them. This setup is critical for accessing and managing these connections dynamically.

Dynamic Database Connection Based on Routes

With the multiple connections configured, we can now focus on dynamically selecting the correct database connection based on the routes accessed. For this purpose, we can leverage NestJS’s middleware capabilities or create custom decorators.

Using Middleware

One straightforward approach is to use middleware to alter the database connection before the request reaches the route handler. Create a middleware that checks the route and switches the TypeORM connection accordingly:

// switch-db.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
import { getConnectionManager } from 'typeorm';

@Injectable()
export class SwitchDbMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    const connectionManager = getConnectionManager();
    const route = req.path;
    let connectionName = 'default';
    if(route.startsWith('/api/secondary')) connectionName = 'secondary';
    // Ensure the connection is established with the right DB
    connectionManager.get(connectionName).connect().then(() => next());
  }
}

Using Custom Decorators

An alternative and more elegant way to handle this is through custom decorators. You can create a decorator that specifies which database connection a service method should use:

// use-connection.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const UseConnection = createParamDecorator(
  (connectionName: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    request.connectionName = connectionName;
    return connectionName;
  }
);

Then, in your service methods, you can use this decorator to dynamically switch connections:

// my.service.ts
import { Injectable } from '@nestjs/common';
import { UseConnection } from './use-connection.decorator';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class MyService {
  constructor(
    @InjectRepository(MyEntity, 'default') private defaultRepo: Repository<MyEntity>,
    @InjectRepository(MyEntity, 'secondary') private secondaryRepo: Repository<MyEntity>
  ) {}

  @UseConnection('secondary')
  async findInSecondaryDB() {
    return this.secondaryRepo.find();
  }
}

This approach is cleaner and offers more flexibility. It also encapsulates the connection logic neatly within decorators, keeping your services concise and focused on business logic.

Conclusion

Throughout this tutorial, we’ve explored how to set up and dynamically switch between multiple database connections in a NestJS application based on routes. While we focused on a couple of strategies – middleware and custom decorators – the concepts can adapt to fit a wide variety of use cases. NestJS’s modular and flexible ecosystem makes it an excellent choice for complex applications that require interactions with multiple databases.

Remember, managing multiple connections can introduce complexity to your application, so it’s essential to thoroughly test your connection-switching logic to ensure data integrity and application stability. Happy coding!