NestJS — Enterprise Node.js

Configuration & Environment Validation

16 min Lesson 17 of 48

Configuration & Environment Validation

Real applications behave differently across environments — local, staging, production — and they hold secrets like database passwords and API keys. Hard-coding these is a mistake. The official @nestjs/config package loads configuration from environment variables and .env files, and lets you validate it at startup.

Setting up ConfigModule

Install the package and import ConfigModule in your root module. isGlobal: true makes the config service injectable everywhere without re-importing:

// npm install @nestjs/config import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), ], }) export class AppModule {}

By default it reads a .env file at the project root:

# .env DATABASE_URL=postgres://localhost:5432/app JWT_SECRET=super-secret PORT=3000

Reading configuration

Inject ConfigService and call get():

import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @Injectable() export class AuthService { constructor(private config: ConfigService) {} signToken() { const secret = this.config.get<string>('JWT_SECRET'); // ... use secret } }
Never commit your .env file. Add it to .gitignore and commit a .env.example with the keys but no real values. Secrets in source control are a serious security risk.

Custom configuration with namespaces

Grouping related settings into typed, namespaced config objects keeps things tidy:

import { registerAs } from '@nestjs/config'; export default registerAs('database', () => ({ url: process.env.DATABASE_URL, poolSize: parseInt(process.env.DB_POOL ?? '10', 10), })); // load it ConfigModule.forRoot({ load: [databaseConfig] }); // read a namespaced value this.config.get('database.url');

Validating configuration at startup

The most valuable feature: fail fast. If a required variable is missing or malformed, the app should refuse to start rather than crash later. Pass a validation schema (commonly Joi):

import * as Joi from 'joi'; ConfigModule.forRoot({ validationSchema: Joi.object({ NODE_ENV: Joi.string().valid('development', 'production').required(), PORT: Joi.number().default(3000), DATABASE_URL: Joi.string().required(), JWT_SECRET: Joi.string().min(16).required(), }), });
Fail fast beats fail late. Validating config at boot means a missing JWT_SECRET stops deployment immediately with a clear message — instead of surfacing as a confusing runtime error on the first login attempt in production.

Summary

Use @nestjs/config to load environment variables and .env files, inject ConfigService to read them, and group settings with registerAs namespaces. Crucially, supply a validationSchema so the app fails fast at startup when configuration is missing or invalid. Keep secrets out of source control. Next: lifecycle hooks and the application context.