NestJS 请求多个数据库的范围多租户

NestJS Request Scoped Multitenancy for Multiple Databases

希望使用 NestJS 6 的新请求注入范围功能实现多租户 NestJS 解决方案。

对于任何给定的服务,我假设我可以做这样的事情:

@Injectable({scope: Scope.REQUEST})
export class ReportService implements OnModuleInit { ... }

然后,在构造函数中,根据请求确定租户,连接到适当的数据库,并为新连接实例化存储库。

我想知道这是否是最直接的方法?

是否可以覆盖请求的连接提供程序和范围 that,而不是更新每个服务?

这是我们最终做的...

  1. 创建一个简单的全局 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 {}
  1. 将 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 中有完整记录。