building-a-multi-tenant-application-with-nestjs-and-typeorm.html

Building a Multi-Tenant Application with NestJS and TypeORM

In today's software landscape, multi-tenant applications are becoming increasingly popular. They allow multiple users (tenants) to share the same application infrastructure while keeping their data isolated. This model is particularly useful for Software as a Service (SaaS) applications, where resources can be efficiently managed and scaled. In this article, we will explore how to build a multi-tenant application using NestJS and TypeORM, two powerful tools in the Node.js ecosystem.

What is Multi-Tenancy?

Multi-tenancy is an architecture where a single instance of a software application serves multiple tenants. Each tenant's data is securely stored and isolated, ensuring privacy and security. This model provides several benefits:

  • Resource Efficiency: Reducing infrastructure costs by sharing resources.
  • Simplified Maintenance: Updates and maintenance can be done centrally.
  • Scalability: Easily scalable to accommodate more tenants.

Why Choose NestJS and TypeORM?

NestJS

NestJS is a progressive Node.js framework that leverages TypeScript, making it an excellent choice for building scalable and maintainable server-side applications. Its modular architecture allows for easy organization and separation of concerns.

TypeORM

TypeORM is an Object Relational Mapping (ORM) tool for TypeScript and JavaScript. It provides a simple way to interact with databases through entities, making data handling easier and more intuitive.

Setting Up Your NestJS Project

To get started, you need to set up a new NestJS project. Follow these steps:

  1. Install NestJS CLI: bash npm i -g @nestjs/cli

  2. Create a New Project: bash nest new multi-tenant-app cd multi-tenant-app

  3. Install TypeORM and a Database Driver: For this example, we will use PostgreSQL. Install the required packages: bash npm install @nestjs/typeorm typeorm pg

Configuring TypeORM for Multi-Tenancy

In a multi-tenant application, you can choose either the database-per-tenant or shared-database approach. For this guide, we will focus on using a shared database with a tenantId field in each table.

Database Configuration

Open the app.module.ts file and configure TypeORM:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'your_username',
      password: 'your_password',
      database: 'your_database',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
    UserModule,
  ],
})
export class AppModule {}

Creating an Entity

Next, create a User entity to represent tenants. Use the NestJS CLI to generate a module and service:

nest generate module user
nest generate service user
nest generate controller user

Now, create a user.entity.ts file in the user folder:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  tenantId: string; // This field helps in identifying the tenant
}

Implementing Multi-Tenancy Logic

Middleware for Tenant Identification

To handle tenant identification, create a middleware that extracts the tenant information from the request. For simplicity, we will assume the tenant ID is sent in the request headers.

Create a new middleware file:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class TenantMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const tenantId = req.headers['tenant-id'];
    if (!tenantId) {
      return res.status(400).send('Tenant ID is required');
    }
    req['tenantId'] = tenantId; // Store tenant ID for later use
    next();
  }
}

Don’t forget to register this middleware in your app.module.ts:

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { TenantMiddleware } from './tenant.middleware';

@Module({
  imports: [UserModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(TenantMiddleware).forRoutes('*'); // Apply to all routes
  }
}

Using Tenant ID in Services

In the User service, you can now use the tenantId to filter users by tenant:

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>,
  ) {}

  async createUser(name: string, tenantId: string): Promise<User> {
    const user = this.userRepository.create({ name, tenantId });
    return this.userRepository.save(user);
  }

  async findAll(tenantId: string): Promise<User[]> {
    return this.userRepository.find({ where: { tenantId } });
  }
}

Conclusion

Building a multi-tenant application with NestJS and TypeORM provides a robust and scalable architecture. With a clear separation of tenant data and a user-friendly development experience, you can create secure and efficient applications tailored to various clients.

By following the steps outlined in this article, you should now have a foundational understanding of how to set up a multi-tenant system. As you continue to develop your application, consider implementing additional features such as tenant-specific configurations or advanced security measures.

Embrace the power of NestJS and TypeORM to deliver high-quality multi-tenant applications that stand out in the competitive SaaS landscape!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.