Learn NestJS - Configuration & Environment Management
Episode 8 of 24

Learn NestJS - Configuration & Environment Management

This episode covers NestJS configuration management: the @nestjs/config module, configuration modules, per-environment configuration, validation schemas, externalized configuration using environment variables, and secrets management practices in development and production.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

A production application should never have hard-coded configuration values — like database secrets or API keys — directly in the source code. Configuration must be separated from the code and managed per environment.

Episode 8 covers @nestjs/config, per-environment configuration, schema validation, externalized configuration, and how to manage secrets safely.

The @nestjs/config Module

ConfigModule and ConfigService

@nestjs/config is the official module that handles configuration. Install it first:

Install @nestjs/config
npm install @nestjs/config

Then register it in the module:

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

With isGlobal: true, ConfigService can be injected anywhere without importing the module repeatedly.

Reading Values with ConfigService

JSMembaca nilai dari ConfigService
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
 
@Injectable()
export class AppConfigService {
  constructor(private readonly configService: ConfigService) {}
 
  getPort(): number {
    return this.configService.get<number>("PORT", 3000);
  }
 
  getNodeEnv(): string {
    return this.configService.get<string>("NODE_ENV", "development");
  }
}

configService.get("PORT", 3000) reads a value with a default fallback if it's not found.

Configuration Modules

The .env and .env.local Files

ConfigModule.forRoot() reads the .env file in the project root. For different environments, we can use different files:

Isi file .env
PORT=3000
DATABASE_URL=sqlite:data.sqlite
JWT_SECRET=rahasia-development

For production, values are injected from system environment variables or a secret manager, not from a committed .env file.

Creating a Custom Configuration Module

To group related configuration, create a factory function:

JSConfiguration factory
export default () => ({
  port: parseInt(process.env.PORT ?? "3000", 10),
  database: {
    url: process.env.DATABASE_URL,
  },
});

Then point ConfigModule to this file:

JSMenggunakan configuration factory
ConfigModule.forRoot({
  load: [configuration],
})

Validation Schema

Validating Environment Variables

Misconfiguration should be detected when the application starts, not at runtime. Use Joi for validation:

Install joi
npm install joi
JSValidation schema dengan Joi
import * as Joi from "joi";
 
ConfigModule.forRoot({
  validationSchema: Joi.object({
    NODE_ENV: Joi.string()
      .valid("development", "production", "test")
      .default("development"),
    PORT: Joi.number().default(3000),
    DATABASE_URL: Joi.string().required(),
  }),
})

If a required variable is missing, the application fails to start immediately with a clear message.

Externalized Configuration

Environment Variables as the Source of Truth

The principle of externalized configuration: the same code runs in every environment, and configuration values are injected from outside. This allows the same deploy to staging and production without changing the code.

Menyuntikkan env saat menjalankan aplikasi
PORT=8080 DATABASE_URL=postgres://user:pass@host/db npm run start:prod

Secrets in Development vs Production

In development, .env is enough. In production, secrets should be stored in a secret manager — like AWS Secrets Manager, HashiCorp Vault, or the built-in secrets feature of your deployment platform. Never put secrets in the repository, including a committed .env file.

Isi file .gitignore
.env
.env.local
.env.*.local

With the .gitignore above, local env files will never be committed to Git.

Conclusion

Episode 8 makes your application ready to move between environments without changing code: @nestjs/config, configuration modules, validation schemas, externalized configuration, and safe secrets practices.

Key takeaways:

  • ConfigModule.forRoot() reads the .env file and exposes ConfigService.
  • isGlobal: true makes ConfigService injectable everywhere.
  • Configuration factories group related configuration.
  • Joi validates environment variables at startup.
  • Code must be identical across all environments; only configuration differs.
  • Secrets are stored in a secret manager, not in the repository.

In the next episode 9 we'll discuss logging, monitoring, and health — the built-in logger and custom logger service, Winston and Pino integration, health checks with @nestjs/terminus, and basic observability patterns for metrics.

Learn NestJS - Configuration & Environment Management | Learning NestJS