Reputation: 5569
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
Upvotes: 9
Views: 9549
Reputation: 1
I don't claim to know why, but the @Global
decorator apparently doesn't work for registering a dynamic module as global. The NestJS documentation specifies that a dynamic module can be registered as global by adding a global
field with value true
to the object returned by the static method handling the dynamic configuration (forRoot
in your case):
export class ConfigModule {
static forRoot(baseConfigPath: string): DynamicModule {
const providers = [{ provide: 'Config', useValue: configFactory(baseConfigPath) }];
return {
global: true,
module: ConfigModule,
providers,
exports: providers,
};
}
}
If I find the motivation to figure out why the decorator isn't useful here, I'll edit this response with an explanation.
Upvotes: 0
Reputation: 282
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).
Upvotes: 7
Reputation: 21207
Like @DeX3 has pointed out you can achieve this behavior using the @Global
module decorator. This is mentioned in the NestJS docs in the section about modules:
https://docs.nestjs.com/modules
You can check out a more in depth/real world example of this in action by taking a look at some of the third party modules that have been built for the NestJS ecoystem like the TypeOrm package:
https://github.com/nestjs/typeorm/blob/master/lib/typeorm-core.module.ts
Upvotes: 0