Lesson 12-NestJS Configuration

In NestJS, configuration management is critical as it involves securely handling environment variables, configuration files, and sensitive data. NestJS provides multiple ways to manage and inject configurations, including the @nestjs/config module. Below is a detailed explanation of configuration management in NestJS v10.

Installing the Configuration Module

First, you need to install the @nestjs/config module.

npm install @nestjs/config

Configuring the Global Module

To use the configuration module throughout the application, configure it in app.module.ts as follows:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    // Import the configuration module globally
    ConfigModule.forRoot({
      isGlobal: true, // Set as global configuration
      envFilePath: ['.env'], // Specify the environment variable file
      validationSchema: Joi.object({ // Optional: Use Joi for schema validation
        PORT: Joi.number().default(3000),
        DATABASE_URL: Joi.string().required(),
      }),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Using Environment Variables

You can define environment variables in a .env file, for example:

# .env file
PORT=3000
DATABASE_URL=postgres://user:password@localhost:5432/dbname

Then, use the ConfigService to access these values in services or controllers:

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Controller()
export class AppController {
  constructor(private readonly configService: ConfigService) {}

  @Get()
  getHello(): string {
    const port = this.configService.get<number>('PORT');
    const dbUrl = this.configService.get<string>('DATABASE_URL');
    return `Hello World! Running on port ${port}, connected to ${dbUrl}`;
  }
}

Using Configuration Files

In addition to environment variables, you can use JSON or TypeScript files to store configurations. For example, create a config directory and add a database.ts file:

// config/database.ts
export default {
  type: process.env.DATABASE_TYPE,
  host: process.env.DATABASE_HOST,
  port: parseInt(process.env.DATABASE_PORT),
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_NAME,
};

Then, import and use it in app.module.ts:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import databaseConfig from './config/database';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [databaseConfig], // Load configuration file
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Injecting Configuration

Inject configuration into services:

// app.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

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

  getHello(): string {
    const dbConfig = this.configService.get('database');
    return `Hello World! Database config: ${JSON.stringify(dbConfig)}`;
  }
}

Environment-Specific Configuration

You can use different .env files for different environments, such as .env.development, .env.production, etc.

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: ['.env', `.env.${process.env.NODE_ENV}`], // Automatically select based on NODE_ENV
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Configuration Validation

You can use Joi or other validation libraries to validate configurations:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import Joi from 'joi';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        PORT: Joi.number().default(3000),
        DATABASE_URL: Joi.string().required(),
      }),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Dynamic Configuration

You can use dynamic configuration to load specific configuration files:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: [`.env.${process.env.NODE_ENV}`], // Dynamically select environment file
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Using TypeORM

If you are using TypeORM, you can configure database connections through the configuration:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { User } from './entities/user.entity';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: ['.env'],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: configService.get('database.type'),
        host: configService.get('database.host'),
        port: configService.get('database.port'),
        username: configService.get('database.username'),
        password: configService.get('database.password'),
        database: configService.get('database.database'),
        entities: [User],
        synchronize: true,
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Using Other Configuration Tools

In addition to @nestjs/config, you can use other tools like dotenv or yargs to handle command-line arguments or environment variables.

Configuration Example

Below is a complete example including the use of a .env file, configuration files, and TypeORM database configuration.

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { User } from './entities/user.entity';
import databaseConfig from './config/database';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: ['.env', `.env.${process.env.NODE_ENV}`],
      load: [databaseConfig],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: configService.get('database.type'),
        host: configService.get('database.host'),
        port: configService.get('database.port'),
        username: configService.get('database.username'),
        password: configService.get('database.password'),
        database: configService.get('database.database'),
        entities: [User],
        synchronize: true,
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

.env File

# .env file
PORT=3000
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs

app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly configService: ConfigService, private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    const port = this.configService.get<number>('PORT');
    const dbConfig = this.configService.get('database');
    return `Hello World! Running on port ${port}, connected to ${JSON.stringify(dbConfig)}`;
  }
}

app.service.ts

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

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

  getHello(): string {
    const dbConfig = this.configService.get('database');
    return `Hello World! Database config: ${JSON.stringify(dbConfig)}`;
  }
}

config/database.ts

// config/database.ts
import { ConfigService } from '@nestjs/config';

export default () => ({
  type: process.env.DATABASE_TYPE,
  host: process.env.DATABASE_HOST,
  port: parseInt(process.env.DATABASE_PORT),
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_NAME,
});

Using .env Files

Define environment variables in the .env file:

# .env file
PORT=3000
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs

Using .env.development and .env.production

Create environment-specific .env files:

# .env.development
PORT=3001
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_dev

# .env.production
PORT=3000
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs_prod

Using .env.test

For the test environment, create a .env.test file:

# .env.test
PORT=3002
DATABASE_TYPE=sqlite
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_test

Using .env.example

To help new team members understand how to configure the .env file, create a .env.example file as a template:

# .env.example
PORT=3000
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs

Using .env.local

To allow developers to override default configurations, create a .env.local file:

# .env.local
PORT=3003
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs_local

Using .env.ci

For continuous integration environments, create a .env.ci file:

# .env.ci
PORT=3004
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_ci

Using .env.staging

For staging environments, create a .env.staging file:

# .env.staging
PORT=3005
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs_staging

Using .env.docker

For Docker environments, create a .env.docker file:

# .env.docker
PORT=3006
DATABASE_TYPE=postgres
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs_docker

Using .env.k8s

For Kubernetes environments, create a .env.k8s file:

# .env.k8s
PORT=3007
DATABASE_TYPE=postgres
DATABASE_HOST=postgres-service
DATABASE_PORT=5432
DATABASE_USERNAME=user
DATABASE_PASSWORD=password
DATABASE_DATABASE=nestjs_k8s

Using .env.aws

For AWS environments, create a .env.aws file:

# .env.aws
PORT=3008
DATABASE_TYPE=rds
DATABASE_HOST=database-1.cjgqy5kx907r.us-east-1.rds.amazonaws.com
DATABASE_PORT=5432
DATABASE_USERNAME=admin
DATABASE_PASSWORD=admin123
DATABASE_DATABASE=nestjs_aws

Using .env.gcp

For GCP environments, create a .env.gcp file:

# .env.gcp
PORT=3009
DATABASE_TYPE=cloudsql
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_USERNAME=admin
DATABASE_PASSWORD=admin123
DATABASE_DATABASE=nestjs_gcp

Using .env.azure

For Azure environments, create a .env.azure file:

# .env.azure
PORT=3010
DATABASE_TYPE=mssql
DATABASE_HOST=server.database.windows.net
DATABASE_PORT=1433
DATABASE_USERNAME=admin
DATABASE_PASSWORD=admin123
DATABASE_DATABASE=nestjs_azure

Using .env.heroku

For Heroku environments, create a .env.heroku file:

# .env.heroku
PORT=3011
DATABASE_TYPE=postgres
DATABASE_HOST=dpg-ch4113066g427759l0g0-a.oregon-postgres.render.com
DATABASE_PORT=5432
DATABASE_USERNAME=heroku_user
DATABASE_PASSWORD=heroku_password
DATABASE_DATABASE=nestjs_heroku

Using .env.localhost

For local development environments, create a .env.localhost file:

# .env.localhost
PORT=3012
DATABASE_TYPE=sqlite
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_local

Using .env.travis

For Travis CI environments, create a .env.travis file:

# .env.travis
PORT=3013
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_travis

Using .env.circleci

For CircleCI environments, create a .env.circleci file:

# .env.circleci
PORT=3014
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_circleci

Using .env.github

For GitHub Actions environments, create a .env.github file:

# .env.github
PORT=3015
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_github

Using .env.gitlab

For GitLab CI environments, create a .env.gitlab file:

# .env.gitlab
PORT=3016
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_gitlab

Using .env.bitbucket

For Bitbucket Pipelines environments, create a .env.bitbucket file:

# .env.bitbucket
PORT=3017
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_bitbucket

Using .env.netlify

For Netlify environments, create a .env.netlify file:

# .env.netlify
PORT=3018
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_netlify

Using .env.vercel

For Vercel environments, create a .env.vercel file:

# .env.vercel
PORT=3019
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=root
DATABASE_DATABASE=nestjs_vercel
Share your love