Environmental variables are an extremely important aspect of any application. Therefore, it is also important to validate environment variables. Using NestJS Config Validation, it is possible to validate environment variables. Based on the validation result, we can also throw exceptions in case an invalid value is provided.

If you are new to configuration aspects in NestJS, I will recommend going through our detailed post on NestJS Config.

With regards to validation, there are two main approaches to validate configuration in NestJS.

  • Using Joi built-in validator
  • Custom Validation Function

We will look at both the approaches one-by-one.

1 – NestJS Config Validation using Joi Validator

NestJS Config Validation using Joi Validator works on the concept of schema validation. Basically, in this approach, we define an object schema and validate Javascript objects using that schema.

To get started with Joi validation, we need to install the necessary package as below.

$ npm install --save joi

Once the package is installed, we can define a Joi validation schema and pass it to the ConfigModule via the validationSchema().

See below example:

import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
            .valid('dev', 'prod')
      }),
    }),
  ],
})
export class AppModule {}

Basically, in the above code snippet, we are specifying that the environment variable NODE_ENV has valid values as dev and prod. Any other value except these two values will throw an error as below:

> NODE_ENV=dov nest start


/Users/saurabhdashora/NestProjects/nestjs-typeorm-sample/node_modules/@nestjs/config/dist/config.module.js:71
                throw new Error(`Config validation error: ${error.message}`);
                      ^
Error: Config validation error: "NODE_ENV" must be one of [dev, prod]

Some important points to note with regards to NestJS Config Validation with Joi are as follows:

  • By default, all keys are optional. In other words, no error is thrown if a particular key is missing in the schema. In the above example, if we don’t pass NODE_ENV, there will be no error.
  • We can also set default value as part of schema. NestJS uses the default value if no values are provided in the environment.
ConfigModule.forRoot({
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
            .valid('dev', 'prod')
            .default('dev')
      }),
})
  • We can also set a particular property as required. In this case, we get an error if we don’t provide a valid value.
ConfigModule.forRoot({
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
            .valid('dev', 'prod')
            .required()
      }),
})

NestJS Config package also provides a couple of optional parameters.

  • allowUnknown – This property controls whether we can use unknown keys or not. Default value is true. In other words, no error is thrown if we use a key not present in the schema
  • abortEarly – This property stops the validation on the first error itself. The default value is false. In other words, all errors are shown by default. However, we can restrict it by setting abortEarly to true.

We can specify both of these options by adding the validationOptions object to the ConfigModule configuration object. See below:

ConfigModule.forRoot({
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
            .valid('dev', 'prod')
            .required()
      }),
      validationOptions: {
        allowUnknown: false,
        abortEarly: true,
      },
})

2 – NestJS Config Custom Validation

This is the second option we have when it comes to configuration validation in NestJS.

In NestJS Config Custom Validation, we can specify a synchronous function that validates an object. This object contains the environment variables from the env files and the runtime process. The custom validation function returns an object that contains validated environment variables. In case the validation function throws an error, the application will not bootstrap successfully.

To use NestJS Config Custom Validation, we can use the class-transformer and class-validator npm packages.

$ npm install class-transformer class-validator

Next, we can define the validation class and validate function as below:

import { plainToClass } from "class-transformer";
import { IsAlphanumeric, IsEnum, IsNumber, validateSync } from "class-validator";

enum EnvironmentType {
    Dev = "dev",
    Prod = "prod"
}

class EnvironmentVariables {

    @IsEnum(EnvironmentType)
    NODE_ENV: EnvironmentType

    @IsNumber()
    PORT: number;

    @IsAlphanumeric()
    REDIS_HOST: string;

    @IsNumber()
    REDIS_PORT: string;
}

export function validate(configuration: Record<string, unknown>) {
    const finalConfig = plainToClass(
        EnvironmentVariables,
        configuration,
        { enableImplicitConversion: true }
    );

    const errors = validateSync(finalConfig, { skipMissingProperties: true });

    if (errors.length > 0) {
        throw new Error(errors.toString());
    }

    return finalConfig;
}

There are two main parts in the above code:

  • The EnvironmentVariables class defines the validation constraints. For example, NODE_ENV is an enum with two possible values. Similarly, PORT and REDIS_PORT are numeric whereas REDIS_HOST can be alphanumeric. In other words, we are declaratively specifying constraints.
  • The validate() function actually validates the environment variables against the constraints. The plainToClass function maps the configuration to the EnvironmentVariables class. Next, the validateSync() function applies the validation rules and populates the errors object.
  • If the errors object has a length greater than 0, we throw an Error exception.
  • In case there are no errors, we return the validatedConfig.

Now, we can simply use the validate function to configure the ConfigModule. See below.

import { validate } from './config/env.validation';

@Module({
  imports: [
    ConfigModule.forRoot({
      validate,
    }),
  ],
})
export class AppModule {}

Conclusion

Validating environment variables becomes pretty straightforward using the above approaches. We can use schema-driven Joi validation or the more flexible custom validate function. Either approach can provide good results when it comes to performing configuration validation in NestJS.

Want to see more applications of NestJS Configuration? Check out this post on how we setup configuration for sending email using NestJS Sendgrid integration.

If you have any comments or queries about this post, please feel free to write them in the comments section below.

Categories: BlogNestJS

Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

2 Comments

Armando · April 22, 2022 at 12:32 am

estuvo muy bueno

    Saurabh Dashora · April 22, 2022 at 1:19 am

    Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *