--- title: Use ConfigModule for Environment Configuration impact: LOW-MEDIUM impactDescription: Proper configuration prevents deployment failures tags: devops, configuration, environment, validation --- ## Use ConfigModule for Environment Configuration Use `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety. **Incorrect (accessing process.env directly):** ```typescript // Access process.env directly @Injectable() export class DatabaseService { constructor() { // No validation, can fail at runtime this.connection = new Pool({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT), // NaN if missing password: process.env.DB_PASSWORD, // undefined if missing }); } } // Scattered env access @Injectable() export class EmailService { sendEmail() { // Different services access env differently const apiKey = process.env.SENDGRID_API_KEY || 'default'; // Typos go unnoticed: process.env.SENDGRID_API_KY } } ``` **Correct (use @nestjs/config with validation):** ```typescript // Setup validated configuration import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; import * as Joi from 'joi'; // config/database.config.ts export const databaseConfig = registerAs('database', () => ({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT, 10), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, })); // config/app.config.ts export const appConfig = registerAs('app', () => ({ port: parseInt(process.env.PORT, 10) || 3000, environment: process.env.NODE_ENV || 'development', apiPrefix: process.env.API_PREFIX || 'api', })); // config/validation.schema.ts export const validationSchema = Joi.object({ NODE_ENV: Joi.string() .valid('development', 'production', 'test') .default('development'), PORT: Joi.number().default(3000), DB_HOST: Joi.string().required(), DB_PORT: Joi.number().default(5432), DB_USERNAME: Joi.string().required(), DB_PASSWORD: Joi.string().required(), DB_NAME: Joi.string().required(), JWT_SECRET: Joi.string().min(32).required(), REDIS_URL: Joi.string().uri().required(), }); // app.module.ts @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, // Available everywhere without importing load: [databaseConfig, appConfig], validationSchema, validationOptions: { abortEarly: true, // Stop on first error allowUnknown: true, // Allow other env vars }, }), TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService) => ({ type: 'postgres', host: config.get('database.host'), port: config.get('database.port'), username: config.get('database.username'), password: config.get('database.password'), database: config.get('database.database'), autoLoadEntities: true, }), }), ], }) export class AppModule {} // Type-safe configuration access export interface AppConfig { port: number; environment: 'development' | 'production' | 'test'; apiPrefix: string; } export interface DatabaseConfig { host: string; port: number; username: string; password: string; database: string; } // Type-safe access @Injectable() export class AppService { constructor(private config: ConfigService) {} getPort(): number { // Type-safe with generic return this.config.get('app.port'); } getDatabaseConfig(): DatabaseConfig { return this.config.get('database'); } } // Inject namespaced config directly @Injectable() export class DatabaseService { constructor( @Inject(databaseConfig.KEY) private dbConfig: ConfigType, ) { // Full type inference! const host = this.dbConfig.host; // string const port = this.dbConfig.port; // number } } // Environment files support ConfigModule.forRoot({ envFilePath: [ `.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env', ], }); // .env.development // DB_HOST=localhost // DB_PORT=5432 // .env.production // DB_HOST=prod-db.example.com // DB_PORT=5432 ``` Reference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration)