NestJS 请求多个数据库的范围多租户
NestJS Request Scoped Multitenancy for Multiple Databases
希望使用 NestJS 6 的新请求注入范围功能实现多租户 NestJS 解决方案。
对于任何给定的服务,我假设我可以做这样的事情:
@Injectable({scope: Scope.REQUEST})
export class ReportService implements OnModuleInit { ... }
然后,在构造函数中,根据请求确定租户,连接到适当的数据库,并为新连接实例化存储库。
我想知道这是否是最直接的方法?
是否可以覆盖请求的连接提供程序和范围 that,而不是更新每个服务?
这是我们最终做的...
- 创建一个简单的全局
TenancyModule
绑定到请求范围:
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 {}
- 将 request-specific
'CONNECTION'
注入模块服务,从中检索存储库:
user.service.ts
...
@Injectable({scope: Scope.REQUEST})
export class UserService {
private readonly userRepository: Repository<User>;
constructor(@Inject('CONNECTION') connection) {
this.userRepository = connection.getRepository(User);
}
我建议将@nurikabe 的方法与请求范围的工厂提供程序和请求范围的服务一起使用。 Nestjs 本身就有一个类似的工厂例子in the docs。
但为了完整起见,还有另一种方法:您也可以使用中间件并将连接附加到请求对象,如 this answer 中描述的类似问题。然而,通过中间件将连接之类的东西附加到请求上是在绕过 DI 机制,并通过使其表现得像一个提供连接的服务容器来疏远请求对象——因此工厂方法应该是首选。
最好将连接作为提供者(来自工厂)注入,而不是将其附加到请求。
请注意,这两种方法都将不可避免地导致创建的连接数增加。这可能会导致性能问题,即使使用连接池也是如此。因此,这种方法(每个租户一个连接)只有在租户数量相对较少时才真正有效。
使用 multi-schema 方法实现的一种方法在 this article 中有完整记录。
希望使用 NestJS 6 的新请求注入范围功能实现多租户 NestJS 解决方案。
对于任何给定的服务,我假设我可以做这样的事情:
@Injectable({scope: Scope.REQUEST})
export class ReportService implements OnModuleInit { ... }
然后,在构造函数中,根据请求确定租户,连接到适当的数据库,并为新连接实例化存储库。
我想知道这是否是最直接的方法?
是否可以覆盖请求的连接提供程序和范围 that,而不是更新每个服务?
这是我们最终做的...
- 创建一个简单的全局
TenancyModule
绑定到请求范围:
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 {}
- 将 request-specific
'CONNECTION'
注入模块服务,从中检索存储库:
user.service.ts
...
@Injectable({scope: Scope.REQUEST})
export class UserService {
private readonly userRepository: Repository<User>;
constructor(@Inject('CONNECTION') connection) {
this.userRepository = connection.getRepository(User);
}
我建议将@nurikabe 的方法与请求范围的工厂提供程序和请求范围的服务一起使用。 Nestjs 本身就有一个类似的工厂例子in the docs。
但为了完整起见,还有另一种方法:您也可以使用中间件并将连接附加到请求对象,如 this answer 中描述的类似问题。然而,通过中间件将连接之类的东西附加到请求上是在绕过 DI 机制,并通过使其表现得像一个提供连接的服务容器来疏远请求对象——因此工厂方法应该是首选。
最好将连接作为提供者(来自工厂)注入,而不是将其附加到请求。
请注意,这两种方法都将不可避免地导致创建的连接数增加。这可能会导致性能问题,即使使用连接池也是如此。因此,这种方法(每个租户一个连接)只有在租户数量相对较少时才真正有效。
使用 multi-schema 方法实现的一种方法在 this article 中有完整记录。