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:
-
Install NestJS CLI:
bash npm i -g @nestjs/cli
-
Create a New Project:
bash nest new multi-tenant-app cd multi-tenant-app
-
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!