Environment variable configuration module for Nest.js featuring validation using class-validator decorators.
$ npm i @applifting-io/nest-decorated-config
Let's say you have the following .env file (or start your application in an environment providing those variables)
SECRET="WmOZI77oOQf76cM9GASDWB7tS3MZ01TPM395FCUN9oE="
URL="http://localhost:3000"
Create your own ConfigService. Define a property for each environment variable. Decorate them with @Env decorator - make sure the first parameter matches your environment variable name.
Optionally decorate your properties with class-validator decorators.
// config.service.ts
import { Env } from '@applifting-io/nestjs-decorated-config';
import { Injectable } from '@nestjs/common';
import {
IsNotEmpty,
IsUrl,
} from 'class-validator';
@Injectable()
export class ConfigService {
@Env('URL')
@IsUrl()
readonly url: string;
@Env('SECRET')
@IsNotEmpty()
readonly secret: string;
}
@Env optionally expects EnvDecoratorOptions
as second parameter:
type EnvDecoratorOptions<T = any> = {
/**
* Variable's default value, used if no other value is populated from environment. Undefined by default
*/
defaultValue?: T | (() => T);
/**
* Exposing will show the value of variable in log. False by default
*/
expose?: boolean;
/**
* Removes last string character, if it is a forward slash. False by default
*/
removeTrailingSlash?: boolean;
/**
* Parses the value to a JavaScript object with JSON.parse. Throws on invalid input. False by default
*/
parseJson?: boolean;
};
Import and add ConfigModule to imports array of your root module.
// app.module.ts
import { ConfigModule } from '@applifting-io/nestjs-decorated-config';
import { Module } from '@nestjs/common';
import { ConfigService } from 'config.service.ts'
@Module({
imports: [
ConfigModule.forRootAsync(ConfigService)
]
})
export class AppModule {}
ConfigModule.forRootAsync optionally expects ConfigModuleOptions
as second parameter:
type ConfigModuleOptions = {
/**
* Print all variables with `expose: true`. False by default
*/
printOnStartup?: boolean;
/**
* Validate all variables using class-validator decorators. True by default
*/
validate?: boolean;
};
ConfigService is available globally, inject it like any other provider
// example.service.ts
@Injectable()
export class ExampleService {
constructor(private readonly config: ConfigService) {}
example() {
console.log(config.url); // => 'http://localhost:3000'
console.log(config.secret); // => 'WmOZI77oOQf76cM9GASDWB7tS3MZ01TPM395FCUN9oE='
}
}
IMPORTANT If using .env files:
Env
decorators read environment variable values fromprocess.env
. Due to the nature of decorators in TS, you need to make sure variables from .env file are loaded into process.env before importingEnv
or any other JS module that itself importsEnv
.
One way to achieve this in practice is to run dotenv and then dynamically import the rest of your application in your main.ts entrypoint
// main.ts
import * as dotenv from 'dotenv';
// Dotenv needs to run before importing bootstrap, because @Env decorators are
// executed when JS interpreter interprets the decorated class which is in
// bootstrap's import tree, and we need env var resolution to be finished before that
dotenv.config();
import('./bootstrap').then(({ bootstrap }) => bootstrap());