How to setup environment variables for a React.js application for multiple environments
Tags: react, 12fa, docker
Published: 2020-12-10 09:41:49

One build to rule them all

Using environment variables in a client-side application is a bit tricky, but it becomes easy when you know how client-side applications work.

When you are working on a web application (client-side), you will certainly need to communicate with one or many backends to retrieve /send some data.

> When you are in an enterprise-scale, the backend URLs will be different and they will depend on the environment where the application is running (dev, staging, ,UAT and production). This is reasonable, isn’t it?

**So, how can we define the values depending on the environment?**

**The short answer**: In many different ways.

I’ll be brief in the first 2 methods and you will know why in a minute :)

### Method 1: Using dotenv module.

This approach consists of using files prefixed with ‘.env’ containing environment variables in a key=value representation.

The content of a file will look like this:

```
REACT_APP_API_URL=https://path/to/my/backend/api
```

Each environment has its own specific file :

- **.env** : for production environment

- **.env.development** : for development environment

In order to retrieve the value of the variable in your application’s code, you can find the value in the **process.env** global variable, like this :

```
const App = () => <h1>{process.env.REACT_APP_API_URL}</h1>;
```

If you are using **CRA (Create React App)**, you will find all the details about this is the official [documentation](https://create-react-app.dev/docs/adding-custom-environment-variables/) since it uses the dotenv module internally.

**NOTE:** You will have to install the dependency if you are not using **CRA**.

### Method 2: Using npm scripts and Webpack.

If you are not using CRA, which does some **“MAGIC”** behind the scenes, you will have to do that **“MAGIC”** by yourself ;)

Nothing to be afraid of, **CRA** uses webpack behind the scenes to bundle your application, it replaces the `process.env.REACT_APP_API_URL` with the value in the .env file depending on the environment specified for the build.

You can do the same thing with a small chunk of code.

First, you’ll have to install [webpack](https://www.npmjs.com/package/webpack) and [webpack-cli](https://www.npmjs.com/package/webpack-cli) from npm :

    npm install --save-dev webpack webpack-cli

After that, go to your package.json and set your environment variable for each npm script you have.

I suppose that you know Webpack, so I won’t go into the details of the config files of each environment.

It’s not over yet !!

The application will not work since we are not telling webpack how the handle the process.env statements in our code.

```
const App = () => <h1>{process.env.REACT_APP_API_URL}</h1>;
```

Inside your webpack configuration file, you will need to add an entry in the plugins list which will interpolate the expression and places the actual value of the environment variable.

Now, you will be able to see the value printed on your web page.

### Method 3: Setting values in the deployment phase.

Before going into the details, I would like to mention an issue with the previous methods.

The issue is that you will have to build the application for every environment that you have since the values are injected at the build time.

This is not ideal when you are dealing with multiple environments and you will have to store each build for each environment somewhere making it hard to manage (in a cloud and container point of view).

The idea here is the have one **“generic”** build which isn’t related to any environment and at the deployment phase, the environment-specific values will be injected into the application.

Let’s see this in action :

First of all, we will need a shell script that will be responsible for extracting the environment variables values and injecting them into a javascript file.

This script is reading all the environment variables starting with `REACT_APP` in the machine (server). It writes them in a .env file which is transformed into a JSON object placed in the `env-config.js` in the `window._env_` object that is accessible from the browser.


```
#.env.sh

#!/bin/bash

#generate a .env file containing your environment variables
env >> .env

# Recreate config file
rm -rf ./env-config.js
touch ./env-config.js

# Add assignment 
echo "window._env_ = {" >> ./env-config.js

# Read each line in .env file
# Each line represents key=value pairs
while read -r line || [[ -n "$line" ]];
do
  # Split env variables by character `=`
  if printf '%s\n' "$line" | grep -q -e '='; then
    varname=$(printf '%s\n' "$line" | sed -e 's/=.*//')
    varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//')
  fi

  # Read value of current variable if exists as Environment variable
  value=$(printf '%s\n' "${!varname}")
  # Otherwise use value from .env file
  [[ -z $value ]] && value=${varvalue}
  
  # Append configuration property to JS file
  echo "  $varname: \"$value\"," >> ./env-config.js
done < .env

echo "}" >> ./env-config.js
```
Now you will need to place the `env-config.js` in the public folder of your application and import it in your `index.html` (You can make another shell script that does this for you if you want to automate stuff)

```
<!-- index.html -->
<script src="%PUBLIC_URL%/env-config.js"></script>
```


Accessing your environment variables will be like this :

    const App = () => <h1>{window._env_.REACT_APP_API_URL}</h1>;

And that’s it, you are all set now!

I hope that you’ve liked the post.

Until next time, I would like to say: Stay Safe, Stay at home, and Keep coding.

Cheers.