The official nest docs on modules explain about global modules and dynamic modules. I'm wondering if it is possible to combine the two patterns?
My use case is the following: I have a dynamic config module:
export class ConfigModule {
static forRoot(baseConfigPath: string): DynamicModule {
const providers = [{ provide: 'Config', useValue: configFactory(baseConfigPath) }];
return {
module: ConfigModule,
providers,
exports: providers,
};
}
}
This enables the config-module to be dependent on the passed in base config path. I can then import the module in the main app module as follows:
@Module({
imports: [ConfigModule.forRoot(path.resolve(__dirname, '../config'))],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {}
which is kind of nice. However, I do have lots of other modules (child modules of the app module, siblings to the config module), where I also want that same instance of the dynamic config module to be injectable. Would be great if I could mark the dynamic ConfigModule
somehow as global - or is there another way?
I've already tried making the ConfigModule
global with @Global
, but that didn't work - here's a super minimal reduced example repo based on the nest starter created by nest new
: https://github.com/DeX3/nest-di-playground
In fact, what our register() method will return is a DynamicModule . A dynamic module is nothing more than a module created at run-time, with the same exact properties as a static module, plus one additional property called module .
A DTO is an object that defines how the data will be sent over the network. We could determine the DTO schema by using TypeScript interfaces, or by simple classes. Interestingly, we recommend using classes here.
The forRoot sets up the loading of the . env file and the forChild uses it in another module. The problem is that forChild is called before forRoot . The ConfigService would be injected with missing config because forRoot hasn't executed first. > AppModule > ConfigModule.
Recently I built something like what you are describing, I got inspiration from nest/typeorm
@Module({})
export class LoggerModule {
static forRoot(rootNamespace: string): DynamicModule {
return {
module: LoggerModule,
imports: [LoggerCoreModule.forRoot(rootNamespace)],
};
}
}
@Global()
@Module({})
export class LoggerCoreModule {
static forRoot(rootNamespace: string): DynamicModule {
const namespaceProvider = {
provide: LOGGER_ROOT_NAMESPACE,
useValue: rootNamespace,
};
const loggerServiceProvider: Provider = {
provide: LoggerService,
useFactory: (namespace) => new LoggerService(namespace).init(),
inject: [LOGGER_ROOT_NAMESPACE],
};
return {
module: LoggerCoreModule,
providers: [loggerServiceProvider, namespaceProvider],
exports: [loggerServiceProvider, namespaceProvider],
};
}
}
Then, you will have a LoggerService
in the global scope which was exported from LoggerCoreModule
. You don't have to export the config you pass but I did it since my noncore module has a forFeature
static method which needs it(I didn't paste the whole thing I built).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With