export const dbConfig = registerAs("database", () => ({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT ?? "5432", 10),
}));
ConfigModule.forRoot({ load: [dbConfig] });
@Injectable()
export class DbService {
constructor(@Inject(dbConfig.KEY) private db: ConfigType<typeof dbConfig>) {}
}
Define the namespace with registerAs("database", factory). Then inject it with @Inject(dbConfig.KEY) and the type ConfigType<typeof dbConfig>.
registerAs creates a namespaced config object. A namespace is a named group of settings, like all the database settings. You load it in one of two ways:
ConfigModule.forRoot({ load: [dbConfig] })ConfigModule.forFeature(dbConfig)in the feature module that owns it
The returned object has a KEY token. When you inject @Inject(dbConfig.KEY) with the type ConfigType<typeof dbConfig>, the service gets exactly that namespace. TypeScript types it fully from the factory's return type. You don't need string-based calls like config.get("database.host"), where a typo is not caught.
The service also doesn't depend on unrelated settings. This makes it easier to test: you provide a plain object for dbConfig.KEY with useValue.
Namespaced config can also feed async modules. Use dbConfig.asProvider() inside a forRootAsync call.
registerAsdefines a factory for one config namespace.- You load it with
load: [...]or withforFeature. - You inject it with
@Inject(dbConfig.KEY). ConfigType<typeof dbConfig>gives the injected config full typing.- It is easy to mock in tests with
useValue.