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
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
After all commands are executed, our project folder structure will look like this:
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');
Why are we using these packages?
- dotenv : Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env.
- joi : The most powerful schema description language and data validator for JavaScript.
- 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();
What Did We Do?
We created joi object type validation with our environment variable names and determined our rules.
- 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.
- PORT: This key is a number and positive. If we give a negative value it'll throw an error. And it's required.
- 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);
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}`);
}
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,
};
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,
};
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"
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"
}
}
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);
Run the start command from your terminal.
npm run start
or npm start
My Output:
7. Let's Check Is Validation Working?
I'll remove 'PORT' variable from '.env' file and will start my application again.
Result:
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
Top comments (2)
Awesome article, very useful for my current project, thank you!.
I found a little typo y step 3, in "Creating Environment File Schema", it's ".positive()" instead of ".postive()"
thanks, was so clear and helpful.