我正在使用NestJS开发一个多租户应用程序,并通过他们的GraphQL模块提供我的API。我想知道如何告诉NestJS根据Web请求实例化我的提供程序。根据他们的文档提供者默认是单身,但我找不到注册瞬态或每个请求提供者的方法。
让我解释一下这个具体的用例。在我的多租户实现中,每个客户都有一个数据库,每当我在后端收到请求时,我需要找出它是哪个客户,所以我需要通过连接到正确的数据库来实例化服务。
这甚至可以使用NestJS吗?
随着nest.js 6.0的发布,添加了injection scopes。有了这个,您可以为您的提供商选择以下三个范围之一:
将它添加到@Injectable()
装饰器:
@Injectable({ scope: Scope.REQUEST })
export class UsersService {}
或者在模块定义中为自定义提供程序设置它:
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
正如您在此issue中所看到的,nestjs尚未为请求范围的提供程序提供内置解决方案。但它可能会在不久的将来这样做:
一旦async-hooks功能(它在节点10中仍然是实验性的)是稳定的,我们将考虑为请求范围的实例提供内置解决方案。
我正在努力解决类似问题,实现这一目标的一种方法是使用node-request-context
模块作为全局请求寄存器,它将为您提供请求上下文。所以你不会有单独的服务实例,但你可以要求这个静态寄存器为你提供特定的实例/连接请求。
https://github.com/guyguyon/node-request-context
创建简单的上下文帮助器:
import { createNamespace, getNamespace } from 'node-request-context';
import * as uuid from 'uuid';
export class RequestContext {
public static readonly NAMESPACE = 'some-namespace';
public readonly id = uuid.v4();
constructor(public readonly conn: Connection) { }
static create(conn: Connection, next: Function) {
const context = new RequestContext(conn);
const namespace = getNamespace(RequestContext.NAMESPACE) || createNamespace(RequestContext.NAMESPACE);
namespace.run(() => {
namespace.set(RequestContext.name, context);
next();
});
}
static currentRequestContext(): RequestContext {
const namespace = getNamespace(RequestContext.NAMESPACE);
return namespace ? namespace.get(RequestContext.name) : null;
}
static getConnection(): Connection {
const context = RequestContext.currentRequestContext();
return context ? context.conn : null;
}
}
conn
实例参数是您的连接,随意放置其他请求特定的依赖项。另外id
只是用于调试,没有真正需要像我一样使用uuid
模块。
创建中间件包装器(这允许您在此处使用DI):
@Injectable()
export class ContextMiddleware implements NestMiddleware {
constructor(private readonly connectionManager: ...) { }
resolve(...args: any[]): MiddlewareFunction {
return (req, res, next) => {
// create the request specific connection here, probably based on some auth header...
RequestContext.create(this.connectionManager.createConnection(), next);
};
}
}
然后在嵌套应用程序中注册新的中间件:
const app = await NestFactory.create(AppModule, {});
app.use(app.get(RequestLoggerMiddleware).resolve());
最后是利润部分 - 在您的应用程序中的任何位置获取请求特定连接:
const conn = RequestContext.getConnection();