`forRootAsync` for modules with dependent configuration
Hard-coded module config — environments differ only by editing code.
nestjs-config-for-root-async
Why it matters
| Stake | If ignored |
|---|---|
| Hard to change |
|
| Hard to test |
|
How to fix
A module that needs configuration (DB connection, API key) provides a static forRoot / forRootAsync. Without it, you slide back to singletons and global state.
Examples
ts
@Module({
providers: [
{
provide: 'REDIS_CLIENT',
useValue: new Redis(process.env.REDIS_URL!), // ← bound to env, not to config
},
],
})
export class RedisModule {}ts
@Module({})
export class RedisModule {
static forRootAsync(options: {
useFactory: (...args: any[]) => Promise<RedisOptions> | RedisOptions;
inject?: any[];
}): DynamicModule {
return {
module: RedisModule,
providers: [
{
provide: 'REDIS_CLIENT',
useFactory: async (...args) => new Redis(await options.useFactory(...args)),
inject: options.inject,
},
],
exports: ['REDIS_CLIENT'],
};
}
}
// app.module.ts
RedisModule.forRootAsync({
useFactory: (config: ConfigService) => ({ url: config.get('REDIS_URL') }),
inject: [ConfigService],
})