#[nestjs-cls] Is it possible to inject a ProxyProvider into a Factory Proxy Provider?

11 messages · Page 1 of 1 (latest)

charred saddle
#

Hi,

I'm tagging @vague crest , maybe you could help as the author of nestjs-cls library?

Is it possible to set up Proxy Provider in an interceptor and then inject it into Proxy Provider Factory?

When I'm trying to do that, I don't get data from an interceptor.

If I add resolveProxyProviders: false to ClsModule.forRoot, then it works. But then I cannot use the result of Proxy Provider Factory in another module.

In one module I have the following:

@Module({
  imports: [
    ClsModule.forRoot({
      global: true,
      middleware: { mount: true }, // should I put resolveProxyProviders: false here? 
    }),
    ClsModule.forFeature(RequestContext)
  ]
  exports: [ClsModule]
})
export class Module1 {}

In another module I have:

imports: [
    Module1,
    ClsModule.forFeatureAsync({
      imports: [Module1],
      provide: DATABASE_CONNECTION,
      inject: [RequestContext],
      useFactory: async (requestContext: RequestContext) => {
        const tenantId = requestContext.tenantId;

        console.log({tenantId}) // { tenantId: undefined } when resolveProxyProviders is true
        
        return createDbConnection(tenantId);
      },
    }),
  ],
  providers: [
    {
      provide: EXAMPLE_REPOSITORY_TOKEN,
      useClass: ExampleRepository,
    },
  ],
  exports: [EXAMPLE_REPOSITORY_TOKEN],
})
export class Module2 {}

and I have this provider:

@Injectable()
export class ExampleRepository implements ExampleRepositoryInterface {
  constructor(
    private readonly logger: ApplicationLogger,
    @Inject(DATABASE_CONNECTION) private readonly dbConnection: MockDatabaseConnectionInterface,
  ) {
    this.dbConnection.getAll // undefined when resolveProxyProviders is false
  }
}
#

The interceptor looks like this:

@Injectable()
export class RequestContextInterceptor<T, R> implements NestInterceptor<T, R> {
  constructor(
    private readonly requestContextProxy: RequestContext,
  ) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<R> {
    const request = context.switchToHttp().getRequest<Request>();
    const { apiKey, tenantId } = getRequestContext(request);
    this.requestContextProxy.apiKey = apiKey;
    this.requestContextProxy.tenantId = tenantId;

    // making this function async and doing await this.clsService.resolveProxyProviders(); here doesn't help

    return next.handle();
  }
}

This interceptor is added via @UseInterceptors(RequestContextInterceptor) on Controller.

vague crest
#

Hi, there's currently very little support for injecting proxy providers into other proxy providers, but it can be done.

#

You'll need a two step process

#

The method for manually triggering resolution of proxy providers on the cls serivice - cls.resolveProxyProviders() optionally accepts a list of proxy provider tokens to resolve.

#

So, in order to inject one proxy provider into another, you need to set resolveProxyProviders: false and then in the factory, call cls.resolveProxyProviders(TheOtherProvider)

#

And when you need all of them, call it without the parameter and it will resolve all that haven't beem resolved yet

charred saddle
#

Thanks for the answer. I tried calling cls.resolveProxyProviders(), without arguments but it didn't work. I guess I'll try to add context info in middleware, or use ClsService, or create a static helper that extracts info from CLS_REQ. Not sure which method will be the cleanest one. I like the Proxy Provider idea but it's my second approach and a second failure. Maybe it will be easier to use in the future. I will take a look at transactional plugins also, but I need this context in external api clients, so it might not be the best solution for that.

Btw, since there are so many ways to provide the context, I think it would be useful to have some hints which ways are recommended.

Thanks again!

vague crest
# charred saddle Thanks for the answer. I tried calling `cls.resolveProxyProviders()`, without ar...

Yeah, I plan to add native support for injecting proxy providers into other proxy providers (by detecting proxy providers in the dependency array of others and iteratively resolving them - like you have to do now, but manually). However, it's not a trivial task (and Nest's internals do not help here, because to Nest, all proxy providers are singletons that exists from the get go) and I don't have that much free time on my hands lately.

#

I agree that there's not many examples in the documentation on how to use nestjs-cls the best way (but frankly, there isn't a best way, but there's a lot of options).

I was planning on adding some more examples, but well, real life gets in the way.

#

However, feel free to create any feature requests in the github repository. The project is open source after all and already had a handful of external contributions.