Dynamic modules let you create modules that can change what they provide based on input. This helps you reuse code and configure features easily.
Dynamic modules in NestJS
import { Module, DynamicModule } from '@nestjs/common'; @Module({}) export class MyDynamicModule { static forRoot(options: MyOptions): DynamicModule { return { module: MyDynamicModule, providers: [ { provide: 'CONFIG_OPTIONS', useValue: options }, MyService, ], exports: [MyService], }; } }
The static method (often named forRoot) returns a DynamicModule object.
This object defines what the module provides, including providers and exports.
static forRoot(options: { apiKey: string }): DynamicModule {
return {
module: MyDynamicModule,
providers: [
{ provide: 'API_KEY', useValue: options.apiKey },
ApiService,
],
exports: [ApiService],
};
}static forFeature(featureName: string): DynamicModule {
return {
module: MyDynamicModule,
providers: [
{ provide: 'FEATURE_NAME', useValue: featureName },
FeatureService,
],
exports: [FeatureService],
};
}This dynamic module lets you set a custom greeting message. The service uses that message to greet a user by name.
import { Module, Injectable, Inject } from '@nestjs/common'; @Injectable() class GreetingService { constructor(@Inject('GREETING') private greeting: string) {} sayHello(name: string): string { return `${this.greeting}, ${name}!`; } } @Module({}) export class GreetingModule { static forRoot(greeting: string): DynamicModule { return { module: GreetingModule, providers: [ { provide: 'GREETING', useValue: greeting }, GreetingService, ], exports: [GreetingService], }; } } // Usage example (not part of module): // const appModule = { // imports: [GreetingModule.forRoot('Hello')], // }; // Then inject GreetingService and call sayHello('Alice') to get 'Hello, Alice!'
Dynamic modules help keep your app flexible and DRY (Don't Repeat Yourself).
Always export the providers you want other modules to use.
Use descriptive names for your static methods like forRoot or forFeature to clarify their purpose.
Dynamic modules let you configure modules with custom options.
They return a DynamicModule object with providers and exports.
This pattern helps reuse and customize modules easily.
Practice
Solution
Step 1: Understand dynamic module concept
Dynamic modules let you pass custom options to configure the module when importing it.Step 2: Identify the main purpose
This flexibility helps reuse modules with different settings instead of hardcoding them.Final Answer:
To allow configuration of modules with custom options at runtime -> Option DQuick Check:
Dynamic modules = configurable modules [OK]
- Thinking dynamic modules remove controllers
- Believing dynamic modules auto-generate schemas
- Assuming dynamic modules have no providers
Solution
Step 1: Recall dynamic module pattern
Dynamic modules use a static method, commonly named forRoot, returning a DynamicModule object.Step 2: Match method signature
Only a static method can be called without creating an instance, fitting the dynamic module pattern.Final Answer:
static forRoot(options: ModuleOptions): DynamicModule { ... } -> Option BQuick Check:
Dynamic module method = static forRoot [OK]
- Using constructor instead of static method
- Defining dynamic module as a normal function
- Making dynamic module method async without returning DynamicModule
export class LoggerModule {
static forRoot(prefix: string): DynamicModule {
return {
module: LoggerModule,
providers: [
{ provide: 'LOGGER_PREFIX', useValue: prefix },
],
exports: ['LOGGER_PREFIX'],
};
}
}Solution
Step 1: Identify provider token
The provider uses the key 'provide' with value 'LOGGER_PREFIX' which is the token for injection.Step 2: Confirm exported token
The exports array includes 'LOGGER_PREFIX', so this token is available outside the module.Final Answer:
'LOGGER_PREFIX' -> Option CQuick Check:
Provider token = 'LOGGER_PREFIX' [OK]
- Confusing module name with provider token
- Using variable name 'prefix' as token
- Thinking useValue is the token
export class CacheModule {
static forRoot(options: { ttl: number }): DynamicModule {
return {
module: CacheModule,
providers: [
{ provide: 'CACHE_TTL', useFactory: (options) => options.ttl },
],
exports: ['CACHE_TTL'],
};
}
}Solution
Step 1: Analyze useFactory context
The useFactory function runs later and cannot directly access the outer options variable without injection.Step 2: Identify correct pattern
To use options inside useFactory, it must be passed via inject or closure properly; direct access causes runtime error.Final Answer:
The useFactory function cannot access options parameter directly -> Option AQuick Check:
useFactory needs inject or closure for options [OK]
- Thinking module property must be string
- Believing exports must list provider objects
- Assuming static methods can't return DynamicModule
Solution
Step 1: Understand dynamic module pattern for config
Dynamic modules use a static method (like forRoot) to accept config and return a DynamicModule object with providers.Step 2: Provide config and services properly
Use useValue or useFactory to inject config into providers, and export services to make them available outside.Final Answer:
Use a static forRoot method returning a DynamicModule with providers using useValue for config and export both services -> Option AQuick Check:
Dynamic module with static forRoot and providers = Use a static forRoot method returning a DynamicModule with providers using useValue for config and export both services [OK]
- Trying to configure modules via constructor
- Not exporting services after providing them
- Using global module without providers for config
Ready to prove it?
Pass the test and this pattern moves unknown
