-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: How to use Factory Proxy Providers and How to Register? #199
Comments
Hi, thanks for reaching out. I think that you just bumped into a situation, where Proxy providers do not work well (yet) - and that is injecting proxy providers into other proxy providers, there's an open issue that I still haven't got around to implement: #169. (EDIT: It has been implemented) The root of the issue is that there is no guaranteed order in which Proxy providers resolve. We can leverage Nest's dependency injection to gather the needed dependencies, but we can't use it's dependency resolution algorithm to instantiate them in the correct order (because from Nest's perspective, they are singletons and have already been constructed). That means that the Until a proper solution is implemented in the library, this can be "worked around" in two ways: 1) Manually resolve proxy providers in the correct order.
ClsModule.forRoot({
global: true,
middleware: {
mount: true
+ resolveProxyProviders: false
},
}), This will disable automatic resolution of Proxy providers in the middleware
@Injectable()
export class ManualProxyResolvingMiddleware implements NestMiddleware {
constructor(private readonly cls: ClsService) {}
async use(_req: any, _res: any, next: () => any) {
// resolve the providers that are depended upon by others
await this.cls.resolveProxyProviders([TENANT_CONNECTION_PROVIDER])
// resolve the rest of unresolved proxy providers
await this.cls.resolveProxyProviders()
}
} Then your existing Proxy providers should work as expected. 2) Do not use Proxy provider for the DataSource.If we can put the DataSource instance in the CLS before any Proxy provider needs it, we can retrieve it in the Proxy's factory. This can be done in the ClsModule.forRoot({
global: true,
middleware: {
mount: true
setup: (cls, req) => {
const tenantId = req.headers['x-tenant-id'];
if (!tenantId) {
throw new BadRequestException('Tenant ID is required');
}
const connection = getTenantConnection(tenantId);
// attach the TENANT_CONNECTION to the CLS
cls.set(TENANT_CONNECTION, connection);
}
},
}), Then, you refactor your existing Proxy providers into these: ClsModule.forFeatureAsync({
provide: TENANT_CONNECTION_PROVIDER,
inject: [ClsService],
// this essentially just makes a proxy provider out of the existing CLS value
useFactory: (cls: ClsService) => cls.get(TENANT_CONNECTION)
global: true,
}), And export const registerTenantRepository = (
repoClass: Type<any>,
entityClass: Type<any>,
) => {
return ClsModule.forFeatureAsync({
provide: repoClass,
inject: [ClsService],
useFactory: (cls: ClsService) => {
const datasource = cls.get(TENANT_CONNECTION)
return datasource.getRepository(entityClass);
},
global: true,
});
}; I hope this helps. By the way, the way you register your repository providers is dangerous: registerTenantRepository(Repository<User>, User) Since TypeScript generic don't exist at runtime, if you register multiple repositories this way, they will all be registered under a token called That's the reason I suggested to define a "dummy" |
Thanks @Papooch for the detailed explanation and reply. and also thank you for pointing out the way I was registering my providers using generics. I used the second workaround of not using the proxy provider at all and storing the connection in Cls. This is working as expected and I'm planning to test it out more. Closing this issue for now. Thank you so much again. |
@rupakkarki27 A proper algorithm for resolving Proxy Providers' dependencies in the correct order has been implemented and published in |
Hi @Papooch, thanks for the library.
I am currently trying to migrate from request scoped providers to
nestjs-cls
to support tenant specific database connections. I was also looking at this issue: #89 to get a context on how to improve the dev experience.The problem is, the
datasource
in the factory is an empty object and I getdatasource.getRepository is not a function
. But, in theClsModule.forFeatureAsync
, the connection is defined on each request.app.module.ts
I also created a higher order function to help with the injection.
and using it in modules like:
I tried debugging a lot but cannot find out what went wrong. Any help would be appriciated.
The text was updated successfully, but these errors were encountered: