Search code examples
microservicesnestjsioc-container

Microservice client registration in dynamicaly configured module for nest.js and loading into service by parameter


I used shared module with dynamic configuration in application. Shared module contains interceptor and service which contains client configured from shared parameters. I injected the service into the interceptor by predefined name (importing SharedModule dynamically into SecondAppModule). So client name can have different value. Inside of service I need to know the clients name before client injecting. Right now it is hard coded:

@Injectable()
export class SumClientService {
    constructor(@Inject('MATH_SERVICE') private client: ClientProxy) {
        console.log('[SumClientService] - created')
    }

    sumCalculation(row: number[]): Observable<number> {
        return this.client.send<number>({ cmd: 'sum' }, row);
    }
}

Question

Is there any ways to load service from context by name in case name known at construction time only?

I've detected two ways at list to past name as a parameter into service without corrupting DI managed by nest.js. But I have no idea how to get access to module context for loading service by specified name (the code of idea fragment is below)

@Injectable()
export class SumClientService {
    constructor(@Inject('service_name') private name: string) {
        console.log('[SumClientService] - created')
        
    }
    client: (clientName: string) => ClientProxy = (clientName: string): ClientProxy => // TODO load by clientName real client from `nest.js` context

// ...
}

P.S.

The idea is to use multiple clients in the same application. I considered scenario one client per module for the first time.

git code


Solution

  • There are few options to create microservice clients in dynamic module:

    1st option

    in module configuration level:

    import { Module } from '@nestjs/common';
    import { ClientModule } from '@nestjs/microservices';
    import { configForYourClient } from '../configs';
    
    @Module({
      imports: [
        ClientModule.register([
          { 'NAME_FOR_DI', ...configForYourClient }
        ])
      ],
    ...
    })
    ...
    

    usage:

    import { Inject } from '@nestjs/common';
    import { ClientProxy } from '@nestjs/microservices';
    
    ...
      constructor(@Inject('NAME_FOR_DI') private readonly client:   ClientProxy) { }
    

    2st option

    For dynamically configured module:

    import { Module, DynamicModule } from '@nestjs/common';
    import { ClientModule } from '@nestjs/microservices';
    import { configForYourClient } from '../configs';
    
    @Module({})
    export class YourModule{
      static register(): DynamicModule {
        return {
          module: YourModule,
          imports: [
            ClientModule.register([
              { 'NAME_FOR_DI', ...configForYourClient }
            ])
          ],
        }
      }
    }
    

    3st option

    import { ClientProxy, ClientProxyFactory } from '@nestjs/microservices';
    import { configForYourClient } from '../configs';
    
    ...
      private client: ClientProxy = ClientProxyFactory.create(configForYourClient)
    

    Following second option it can be created in service directly or registered for module specifying DI key in providers, like:

    import { Module } from '@nestjs/common';
    import { ClientProxyFactory } from '@nestjs/microservices';
    import { configForYourClient } from '../configs';
    
    @Module({
      providers: [
        {
          provide: 'NAME_FOR_DI',
          useValue: ClientProxyFactory.create(configForYourClient)
        },
      ],
    ...
    })
    ...
    

    with following injection by specified key.

    P.S.

    I've described here general idea for dynamic injection. This idea can be enriched with different combinations (injecting list of values for the single provider, or injecting list of configurations into ClientModule.register where you should apply some corrections in you DI approach)