NestJS Request Scoped Multitenancy for Multiple Databases
Here's what we ended up doing...
- Create a simple, global
TenancyModule
bound to the request scope:
tenancy.module.ts
import { Global, Module, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { getConnection } from 'typeorm';
const connectionFactory = {
provide: 'CONNECTION',
scope: Scope.REQUEST,
useFactory: (req) => {
const tenant = someMethodToDetermineTenantFromHost(req.headers.host);
return getConnection(tenant);
},
inject: [REQUEST],
};
@Global()
@Module({
providers: [connectionFactory],
exports: ['CONNECTION'],
})
export class TenancyModule {}
- Inject request-specific
'CONNECTION'
into module services from which to retrieve repositories:
user.service.ts
...
@Injectable({scope: Scope.REQUEST})
export class UserService {
private readonly userRepository: Repository<User>;
constructor(@Inject('CONNECTION') connection) {
this.userRepository = connection.getRepository(User);
}
I would recommend to use the approach by @nurikabe with a request scoped factory provider and request scoped services. Nestjs itself has a similar factory example in the docs.
But for the sake of completenes, there is also another approach: You could also use a middleware and attach the connection to the request object as described in this answer to a similar question. However, attaching things like a connection to the request via a middleware is circumventing the DI mechanism and alienates the request object by making it behave like a service container that delivers the connection – therefore the factory approach should be preferred.