When building for the web, we often have to deal with some sensitive data (like API keys), which cannot be pushed to source control or some things that are supposed to be different for development and production environments (like sending error logs to sentry only in a production environment). That's where environment variables come in as they let us store this kind of data with a breeze.
Now there are two ways of using environment variables -
- Storing those variables in the system environment, via the Terminal in Mac/Linux or using the GUI in Windows.
- Storing and accessing those variables from a
.env
file in the project's root folder, which is ignored from version control.
This post will cover the second method and show you how to inject environment variables from a .env
file to your application via Webpack's dotenv-webpack
plugin via discussing the following steps -
- The .env file
- Accessing environment variables via Webpack
- Using different .env files for production and development environments
The .env file
I prefer creating a .env
file with all the environment variables in the project root and adding a line that references this file to your .gitignore
so that they are not pushed to source control. This way, whenever someone new is cloning your repository, they wouldn't need to add these variables to their system environment (which can be really painful in operating systems like Windows).
Since we are not adding the .env
file to the source control, it is good practice to create a .env.example
file, which is added to source control and contains the value of the variables for the development environment or contains comments on how to obtain them.
For instance, the .env
file for your production environment could be like -
API_ROOT=https://myawesomeAPIRoot.com/
NODE_ENV=production
SOME_IMPORTANT_API_KEY=th1s181mpo97@n7
And the corresponding .env.example
will be -
# The base API endpoint to which requests are made
API_ROOT=http://localhost:5000/
# Whether we are using a production or development environment
NODE_ENV=development
# API key fetching some important things, get the key for your
# development environment at https://somerandomthings.com/important-keys
SOME_IMPORTANT_API_KEY=
This way, someone new cloning your repo can create .env
and add all the required variables accordingly.
Accessing environment variables via Webpack
Now let's read these variables in our code. To do that, we would be using the dotenv-webpack
plugin. Install this plugin as a dev dependency -
yarn install dotenv-webpack --dev
OR
npm install dotenv-webpack --save-dev
This plugin is to read the environment variables from the .env
file securely by only exposing the variables used in the code.
Add it to the plugins in your webpack config file -
// webpack.config.js
const Dotenv = require('dotenv-webpack');
module.exports = {
...
plugins: [
new Dotenv(),
...
]
...
};
Now you can use the variables in your code using the process.env
syntax anywhere in your code and then webpack will replace them with the corresponding values in the .env
file.
For example -
const requestMyAwesomeService = () => {
const data = await fetch(`${process.env.API_ROOT}awesome`)
...
}
On a sidenote a slightly cleaner approach would be store these variables in a separate file and export them from that file.
// constants/envrionment.js
const API_ROOT = process.env.API_ROOT;
const NODE_ENV = process.env.NODE_ENV;
const SOME_IMPORTANT_API_KEY = process.env.SOME_IMPORTANT_API_KEY
export {
API_ROOT,
NODE_ENV,
SOME_IMPORTANT_API_KEY
};
And if you are using webpack aliases for your folders, you could consume these constants in any file like this -
import { API_ROOT } from 'constants/environment';
const requestMyAwesomeService = () => {
const data = await fetch(`${API_ROOT}awesome_route`)
...
}
This way, linters like ESLint or compilers like TypeScript would prevent you from spelling mistakes, and you would get nifty auto-complete while importing these variables with editors like VSCode.
Using different .env files for production and development environments
When using environment variables, you might need to use different values for some keys depending on whether you are in a development or production environment.
For example, SOME_IMPORTANT_API_KEY
could have some domain restrictions for the production environment, which would not work on the development localhost
domains, so we need a way to have separate values for our environment variables in development and production environments.
So we would need to create two files here, namely .env.production
and .env.development
, which would contain the variables for the production and development environments, respectively.
To read these .env
files, we would need to pass environment variables via the CLI to our scripts in the package.json
to read them in our webpack.config.js
file. Assuming you are using webpack-dev-server
for your development environment, your scripts would look something like this -
// package.json
{
...
scripts: {
"build": "webpack --env production --mode production",
"dev": "webpack-dev-server --env development --mode development",
...
}
...
}
Now you can read the value of environment variables you just passed in through your CLI in your webpack config, and load the appropriate .env
file using the dotenv-webpack
's path property.
// webpack.config.js
...
module.exports = env => ({
...
plugins: [
new Dotenv({
path: `./.env.${env}`
}),
...
]
...
});
And that's it. With this setup you can securely access multiple environment values for separate values for production and development environments.