How to validate environment file in NodeJS

sukruozdemir

Sukru Ozdemir

Posted on January 8, 2021

How to validate environment file in NodeJS

I'll show you how to validate .env file with Joi validation. I'm currently using almost all of my ExpressJS apps. So let's go..

1. Creating A Project

First let's create a project. We'll use npm for creating project. Open your terminal and go to location where you wanna create project folder. Then run the following commands in order.

mkdir project-folder-name
cd project-folder-name/
npm init -y
npm install joi dotenv
Enter fullscreen mode Exit fullscreen mode

After all commands are executed open your project with your favourite IDE or text editor.

2. Creating Config File

In your terminal or IDE create a folder named config. Then inside config folder create a file named config.js.

Terminal command:

mkdir config
cd config/
touch config.js
Enter fullscreen mode Exit fullscreen mode

After all commands are executed, our project folder structure will look like this:

Project folder structure

3. Writing Config Code

Open config.js file in your IDE or text editor.

First we'll import our required packages.

const dotenv = require('dotenv');
const joi = require('joi');
const path = require('path');
Enter fullscreen mode Exit fullscreen mode

Why are we using these packages?

  1. dotenv : Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env.
  2. joi : The most powerful schema description language and data validator for JavaScript.
  3. path : The path module provides utilities for working with file and directory paths.

After importing packages, we're passing .env file location as an option to dotenv package. At the moment we don't have a .env file yet. We'll create later.

dotenv.config({ path: path.join(__dirname, '../.env') });

Creating Environment File Schema
const envVarsSchema = joi
  .object()
  .keys({
    NODE_ENV: joi
      .string()
      .valid("production", "development", "test")
      .required(),
    PORT: joi.number().postive().required(),
    API_SECRET: joi.string().required().description("My api secret"),
  })
  .unknown();
Enter fullscreen mode Exit fullscreen mode

What Did We Do?

We created joi object type validation with our environment variable names and determined our rules.

  1. NODE_ENV: This key is a string and only accepts 'production', 'development' and 'test'. If we give a different value it'll throw an error. And it's required.
  2. PORT: This key is a number and positive. If we give a negative value it'll throw an error. And it's required.
  3. API_SECRET: This key is a string and it's required.
IMPORTANT NOTE

There's a method named unknown at the end of the code. It's overrides the handling of unknown keys for the scope of the current object only. (does not apply to children)

We're using this method because; there are dozens of environment variables in process.env used by our operation system or other programs. So if we don't use this method joi will throw an error.

Validating Our Schema

const { value: envVars, error } = envVarsSchema
  .prefs({ errors: { label: 'key' } })
  .validate(process.env);
Enter fullscreen mode Exit fullscreen mode

We validated joi schema and we got destructured 'value' and 'error' variables from joi validation and we give an alias to 'value' variable named 'envVars'.

Checking There's An Error

if (error) {
  throw new Error(`Config validation error: ${error.message}`);
}
Enter fullscreen mode Exit fullscreen mode

If there's an error in schema we will throw an error.

Exporting Our Values

module.exports = {
  env: envVars.NODE_ENV,
  port: envVars.PORT,
  apiSecret: envVars.API_SECRET,
};
Enter fullscreen mode Exit fullscreen mode

We exported our values as object.

Full Code Of config.js File

const dotenv = require("dotenv");
const joi = require("joi");
const path = require("path");

dotenv.config({ path: path.join(__dirname, "../.env") });

const envVarsSchema = joi
  .object()
  .keys({
    NODE_ENV: joi
      .string()
      .valid("production", "development", "test")
      .required(),
    PORT: joi.number().positive().required(),
    API_SECRET: joi.string().required().description("My api secret"),
  })
  .unknown();

const { value: envVars, error } = envVarsSchema
  .prefs({ errors: { label: "key" } })
  .validate(process.env);

if (error) {
  throw new Error(`Config validation error: ${error.message}`);
}

module.exports = {
  env: envVars.NODE_ENV,
  port: envVars.PORT,
  apiSecret: envVars.API_SECRET,
};

Enter fullscreen mode Exit fullscreen mode

4. Creating .env File

Go to project root directory and create a file named .env. Then write your environment variables to this file.

Your .env file should look like this:

NODE_ENV="development"
PORT=3000
API_SECRET="secret"
Enter fullscreen mode Exit fullscreen mode

5. package.json "start" script

Open your 'package.json' file and write your 'start' script.

"start": "node ."

My package.json file:

{
  "name": "project-folder-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^8.2.0",
    "joi": "^17.3.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

6. Creating index.js File

Go to project root directory and create a file named index.js. Then write your code.

I'll print my config file to console. Every time i use the config file it'll check my .env file is valid. So using before the application is useful because if there's an error my application will not start.

My index.js File:

const config = require("./config/config");

console.log(config);
process.exit(0);

Enter fullscreen mode Exit fullscreen mode

Run the start command from your terminal.

npm run start or npm start

My Output:

Console Output

7. Let's Check Is Validation Working?

I'll remove 'PORT' variable from '.env' file and will start my application again.

Result:

Error

And it's working as expected. It's throwing an error and saying; "PORT" is required.


Thanks to everyone who reads. I hope it was useful.


I learned from this repository. Thanks the all contributors!: https://github.com/hagopj13/node-express-boilerplate
Cover Image: https://unsplash.com/photos/oqStl2L5oxI

💖 💪 🙅 🚩
sukruozdemir
Sukru Ozdemir

Posted on January 8, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related