This recipe shows how to integrate ConfigLoaderDirector and ConfigLoaderPool from
@tusss/ood with the @nestjs/config
module to manage environment-specific configuration in a type-safe way.
npm install @nestjs/config
Extend Config with your application-specific fields.
// src/config/types.ts
import { Config } from "@tusss/ood";
export type AppConfig = Config<{
port: number;
dbUrl: string;
}>;
Each environment gets its own IConfigLoader implementation.
// src/config/loader.development.ts
import { IConfigLoader } from "@tusss/ood";
import { AppConfig } from "../types";
export class LoaderDevelopment implements IConfigLoader<AppConfig> {
async load(): Promise<AppConfig> {
return {
environment: "development",
port: 3000,
dbUrl: process.env.DATABASE_URL ?? "postgres://localhost:5432/dev",
};
}
}
// src/config/loader.production.ts
import { IConfigLoader } from "@tusss/ood";
import { AppConfig } from "../types";
export class LoaderProduction implements IConfigLoader<AppConfig> {
async load(): Promise<AppConfig> {
return {
environment: "production",
port: Number(process.env.PORT),
dbUrl: process.env.DATABASE_URL || "postgres://localhost:5432/prod",
};
}
}
Create a pool that maps environment names to their loaders and declares a default fallback loader.
// src/config/loader.pool.ts
import { ConfigLoaderPool, IConfigLoader, KeyOrString } from "@tusss/ood";
import { AppConfig } from "./app.config";
import { LoaderDevelopment } from "./loader.development";
import { LoaderProduction } from "./loader.production";
type AppEnv = KeyOrString<"development" | "production">;
export class AppConfigLoaderPool extends ConfigLoaderPool<AppEnv, AppConfig> {
override readonly defaultLoader: IConfigLoader<AppConfig> = new LoaderDevelopment();
}
export const appConfigLoaderPool = new AppConfigLoaderPool([
["development", new LoaderDevelopment()],
["production", new LoaderProduction()],
]);
Use ConfigModule.forRoot to load the configuration via
ConfigLoaderDirector, then make it available to ConfigService.
// src/app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { ConfigLoaderDirector } from "@tusss/ood";
import { appConfigLoaderPool } from "./config/app-config-loader.pool";
const director = new ConfigLoaderDirector(appConfigLoaderPool);
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [
async () => {
return director.load();
},
],
}),
],
})
export class AppModule {}
NODE_ENV is read automatically by ConfigLoaderDirector.getEnv(), so setting
NODE_ENV=production before starting the app is all that is needed to switch
loaders.
// src/app.service.ts
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { AppConfig } from "./config/app.config";
@Injectable()
export class AppService {
constructor(private readonly config: ConfigService<AppConfig>) {}
getDatabaseUrl(): string {
return this.config.get("dbUrl", { infer: true });
}
getPort(): number {
return this.config.get("port", { infer: true });
}
}
If you need to derive the environment from somewhere other than NODE_ENV,
use customizeEnv:
const director = new ConfigLoaderDirector(appConfigLoaderPool);
director.customizeEnv(() => myCustomEnvResolver());
const config = await director.load();
This is useful for multi-tenant setups or when the environment is stored in a database or remote configuration service.