HomeApply for Multi-Tenancy at Nest.js

Apply for Multi-Tenancy at Nest.js

Recap

In the first part, Nest.js – Create a Multi-Tenancy Application in Part 1, we configured the Nest.js framework and configured and tested the Micro Services Architecture application using Nest.js. In the second part, we used Sequelize and Mongoose to access the database and tested for both MySQL database and MongoDB.

Async connection

In this section We will see how to allow an application to connect to multiple databases on an application basis. Because it is a multi-tenancy application, each tenant has its own database that contains their data to access the same application, so the application needs to be connected to different databases. Is required. We will change and use the repository option. forRootAsync() instead of forRoot(), We need to use custom class for configuration.

For both Sequelize and Mongoose, add the following:

MongooseModule.forRootAsync({
    useClass:MongooseConfigService
  }),
SequelizeModule.forRootAsync({
      useClass:SequelizeConfigService
})

We will create a config file and two classes. MongooseConfigService And SequelizeConfigService

The plan here is to add an injection for each incoming request and use a domain to switch between connections.

So we need to use. @Injectable({scope:Scope.REQUEST}), And on the class constructor, we use. @Inject(REQUEST) private read-only request So that we can get the host information from the application data.

For example, they say the domain is example.com, and we have a tenant. company1, Then the domain will be company1.example.com. Same thing for company2 Tenant, the domain will be company2.example.com and so on.

config / MongooseConfigService.ts

import { Inject, Injectable, Scope } from "@nestjs/common";
import { REQUEST } from "@nestjs/core";
import { MongooseModuleOptions, MongooseOptionsFactory } from "@nestjs/mongoose";

@Injectable({scope:Scope.REQUEST})
export class MongooseConfigService implements MongooseOptionsFactory {
    constructor(@Inject(REQUEST) private readonly request,){}

  createMongooseOptions(): MongooseModuleOptions {
    let domain:string[]
    let database="database_development"
    if(this.request.data ){
      domain=this.request.data['host'].split('.')
      console.log(this.request)
    }
    else{
      domain=this.request['headers']['host'].split('.')
    }

    console.log(domain)
    if(domain[0]!='127' && domain[0]!='www' && domain.length >2){
      database="tenant_"+domain[0]
      console.log('current DB',database)
    }
    return {
      uri: 'mongodb://localhost:27017/'+database,
    };
  }
}

config / SequelizeConfigService.ts

import { Inject, Injectable, Scope } from "@nestjs/common";
import { REQUEST } from "@nestjs/core";
import { CONTEXT, RedisContext, RequestContext } from "@nestjs/microservices";
import { SequelizeModuleOptions, SequelizeOptionsFactory} from "@nestjs/sequelize";

@Injectable({scope:Scope.REQUEST})
export class SequelizeConfigService implements SequelizeOptionsFactory {
    constructor(@Inject(REQUEST) private readonly request:RequestContext){}
    
    createSequelizeOptions(): SequelizeModuleOptions {

      let domain:string[]
      let database="database_development"
      if(this.request.data ){
        domain=this.request.data['host'].split('.')
        console.log(this.request)
      }
      else{
        domain=this.request['headers']['host'].split('.')
      }

      console.log(domain)
      if(domain[0]!='127' && domain[0]!='www' && domain.length >2){
        database="tenant_"+domain[0]
        console.log('current DB',database)
      }

    return {
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'ismaeil',
      password: 'root',
      database: database,
      autoLoadModels: true,
      synchronize: true,
    };
  }
}

Testing

After completing the configuration, we need to do some work to test it as we have to map our local host and IP in the domain. I will try to use two methods to test the application locally but for production, it will be a configuration in your domain provider.

1- Edit the host file in your local machine and edit this file whenever you add tenants.

In Linux go to the following file: /etc/hosts And in Windows: c:windowssystem32driversetchosts And add

## lines
127.0.0.1    example.com
127.0.0.1    company1.example.com
127.0.0.1    company2.example.com

2- Use local DNS.

In Linux, you can install Dnsmasq and follow these steps:

1- Install dnsmasq in Network Manager.

2- Add configuration file. sudo nano /etc/NetworkManager/dnsmasq.d/dnsmasq-localhost.conf.

Add this line to the file:

address=/.example.com/127.0.0.1

3- Restart the service. sudo systemctl reload NetworkManager.

Source code is available in Gut Branch Multi Database.

Bus! In Part 4, we are going to add the level of security and user roles.

.

With over 500+ clients 711 web services  is a frontrunner in providing end to online business solutions to medium and small enterprises in their budget

Contact

© 2023 711web All Rights Reserved.