CodePark/ questions / Configuration & lifecycle hooks
START PRACTICINGPRACTICE
Backend question · NestJS › Configuration & lifecycle hooks

How do you get type-safe, namespaced config with registerAs and inject only one namespace into a service?

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.

  • registerAs defines a factory for one config namespace.
  • You load it with load: [...] or with forFeature.
  • 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.