Working with Environment Variables in NestJS

@rnab
3 min readJan 11, 2025

--

NestJS is a robust framework for building efficient and scalable server-side applications. One common requirement when developing applications is managing configuration across different environments. This is often achieved using environment variables, and in this article, we will explore how to handle environment variables effectively in NestJS. Along the way, we will use TypeScript to ensure type safety, which is one of the key selling points of using NestJS.

Why Use Environment Variables?

Environment variables allow us to keep configuration values outside of our code. This practice has several benefits:

  • Security: Sensitive information like API keys or database passwords are not hard-coded into our source files.
  • Flexibility: Easily switch between different configurations for development, testing, and production environments.
  • Consistency: Helps maintain consistency across different environments.

Setting Up @nestjs/config

NestJS provides the @nestjs/config package to manage environment variables easily. Let’s start by installing this package:

npm install @nestjs/config

Now, create a .env file at the root of your project to hold your environment variables:

.env

DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=testuser
DATABASE_PASSWORD=testpassword
DATABASE_NAME=testdb

Next, configure your NestJS application to read from this .env file. Import ConfigModule and ConfigService in your app.module.ts:

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // Makes the configuration globally available
}),
// Other modules
],
controllers: [],
providers: [],
})
export class AppModule {}

With this configuration, we can now inject and use ConfigService across our application.

Using ConfigService in Your Application

To demonstrate how to use ConfigService, let's create a database module that will utilize our environment variables.

First, create a database.service.ts file:

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class DatabaseService {
constructor(private configService: ConfigService) {}

getDatabaseConfig() {
return {
host: this.configService.get<string>('DATABASE_HOST'),
port: this.configService.get<number>('DATABASE_PORT'),
username: this.configService.get<string>('DATABASE_USER'),
password: this.configService.get<string>('DATABASE_PASSWORD'),
dbName: this.configService.get<string>('DATABASE_NAME'),
};
}
}

The ConfigService provides the get method, which we use to fetch our environment variables. Note that we also specify the type we expect from these variables to enforce type safety.

Now, integrate DatabaseService into a module:

import { Module } from '@nestjs/common';
import { DatabaseService } from './database.service';

@Module({
providers: [DatabaseService],
exports: [DatabaseService],
})
export class DatabaseModule {}

Finally, use the database configuration in your application:

import { Controller, Get } from '@nestjs/common';
import { DatabaseService } from './database/database.service';

@Controller()
export class AppController {
constructor(private databaseService: DatabaseService) {}

@Get('db-config')
getDatabaseConfig() {
return this.databaseService.getDatabaseConfig();
}
}

Advanced Configuration

The @nestjs/config module also allows for more advanced use cases, such as validating environment variables and using schemas.

Validating Environment Variables

To ensure the integrity and validity of the environment variables, you can use a validation schema with the Joi library.

First, install Joi:

npm install joi

Then, create a validation schema and apply it to your ConfigModule:

import * as Joi from '@hapi/joi';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
imports: [
ConfigModule.forRoot({
validationSchema: Joi.object({
DATABASE_HOST: Joi.string().hostname().required(),
DATABASE_PORT: Joi.number().default(5432),
DATABASE_USER: Joi.string().required(),
DATABASE_PASSWORD: Joi.string().required(),
DATABASE_NAME: Joi.string().required(),
}),
validationOptions: {
allowUnknown: false,
abortEarly: true,
},
}),
// Other modules
],
controllers: [],
providers: [],
})
export class AppModule {}

Here, we define a schema that validates our environment variables. If any of the variables do not meet the criteria, an error will be thrown, making it easier to catch misconfigurations early.

Conclusion

Managing environment variables in a NestJS application is straightforward with the @nestjs/config package. It provides a clean and efficient way to handle configuration across different environments while ensuring type safety and validation. By keeping sensitive information out of our source code and using consistent configurations, we can build more secure and maintainable applications.

With NestJS and TypeScript, you get the best of both worlds: a powerful backend framework and strong typing to catch errors before they happen. Happy coding!

--

--

@rnab
@rnab

Written by @rnab

Typescript, Devops, Kubernetes, AWS, AI/ML, Algo Trading

No responses yet