I am working with NestJS and I am building re-usable modules, configurable via forRoot
and forRootAsync
static methods.
I am looking for a way to provide multiple providers of the same class, based on the module options.
export type MyModuleOptions = {
services: MyServiceOptions[];
}
export type MyServiceOptions = {
name: string;
url: string;
}
Based on this options, the result is easy to achieve in a basic forRoot
method:
export class MyModule {
static forRoot(options: MyModuleOptions): DynamicModule {
const providers = options.services.map((service_options) => {
// \__________________/
// loop over the options to generate each provider
return {
provide: 'SERVICE_' + service_options.name,
// \_______________________________/
// generate the provider token based on the options
useFactory: () => {
return new MyService(service_options.url);
}
}
});
return {
module: MyModule,
providers: [...providers]
}
}
}
Now I can use it in my AppModule ...
@Module({
imports: [
MyModule.forRoot({
services: [
{ name: 'SERVICE_1', url: 'https://...' },
{ name: 'SERVICE_2', url: 'https://...' }
]
})
]
})
export class AppModule {}
... and inject the specific service I need:
export class TestService {
constructor(@Inject('SERVICE_SERVICE_1') private service: MyService) {}
// \_________________________/
// Dynamically generated by MyModule
}
The issue
Now I want to implement something similar but using a forRootAsync
method, so instead of providing hard-coded urls for my services I can fetch them from environment variable with the config service.
The desired usage would look something like this:
@Module({
imports: [
MyModule.forRootAsync({
useFactory: (config: ConfigService) => {
return {
services: [
{ name: 'service_1', url: config.get('url_1') },
{ name: 'service_2', url: config.get('url_2') },
// \_________________/
// use external dependency to configure the module, config or something else
]
}
},
inject: [ConfigService]
})
]
})
export class AppModule {}
So I have created the async options for the module:
export type MyModuleAsyncOptions = {
useFactory: (...args: any[]) => MyModuleOptions;
inject: InjectionToken[];
}
When looking to other modules implementation, the common practice seems to create a provider for the module options in the forRootAsync
method:
export class MyModule {
forRootAsync(options: MyModuleAsyncOptions) {
return {
module: MyModule,
providers: [
{
provide: 'MY_MODULE_OPTIONS',
useFactory: (...args: any[]): MyModuleOptions => {
return options.useFactory(...args);
},
inject: [...options.inject]
}
]
}
}
}
Now that I have my module options, how can I build multiple providers with it ?
It seems like the only thing I can do is to inject the options in a single provider, I could not find a way to loop over the options to generate the desired providers.
Thank you in advance for any help on the topic !