Dynamic Module dependency injection

Viewed 3335

I am trying to do something fancy: a DynamicModule that chooses a Controller/Providers depending on a provider. The provider reads a config in a DB at onModuleInit, so there is no way to hard-code its values.

It is similar to this issue: https://github.com/nestjs/nest/issues/601 which was never answered...

I have tried a few approaches and the closest I have got is this:

export class FancyModule implements DynamicModule {
  public module: Type<FancyModule>;
  public controllers: Type<unknown>[];
  public providers: Provider<unknown>[];
  constructor (
    @Inject(StartupConfigService) private readonly startup: StartupConfigService,
  ) {
    this.module = FancyModule;
    if (startup.featureFlagEnabled) {
      this.controllers = [ FancyFeatureController ];
      this.providers = [ FancyFeatureService ];
    } else {
      this.controllers = [ ErrorController ];
      this.providers = [];
    }
  }
}

Second approach:

app.module.ts

@Module({
  imports: [
    StartupConfigModule.register(startupConfigOption),
    FancyModule.registerAsync({
      imports: [StartupConfigModule],
      useFactory: function (startupConfigService: StartupConfigService) {
           return startupConfigService.config
      },
      inject: [StartupConfigService],
   }),
 ],
})

fancy.module.ts

export class FancyModule {
  static registerAsync (config: IAppConfig): DynamicModule {
    if (config.featureFlagEnabled) {
      return {
        module: FancyModule,
        controllers: [ FancyFeatureController ],
        providers: [ FancyFeatureService ],
      };
    } else {
      return {
        module: FancyModule,
        controllers: [ ],
        providers: [ ],
      };
    }
  }
}

This injects as expected but the controllers and providers are never spun up. Is the meta-data evaluated before the constructor? Is that a bug?

1 Answers

It turns out what I'm trying to do is, in fact, impossible. The evaluation of the FancyModule.registerAsync() method happens before the onModuleInit od the StartupconfigService (which sets the feature flag). A DynamicModule cannot use the contents of an instantiated provider to register() itself.

Other reading:

Fundamentals - Dynamic Modules

Advanced - Dynamic Modules

Related