Environment Variables in a cloud-native React app (without create-react-app)
Accessing environment variables in Node.js is a straight-forward process: We set the environment variable and we access it on Node’s process.env
object. In React.js, APIs and secrets that need to be accessed (maybe in a fetch) are best hidden from the app code.
Environment variables can help us to obscure this data. However, React is served on the browser… which doesn’t include Node. Create-react-app offers a simple way to access environment variables, but what if we have a custom React app built from the ground-up? Accessing environment variables can make us want to Frisbee our machines through the nearest window.
What we’re solving
We want to figure out how to reliably access and use an environment variable from within React.js code, without using create-react-app, in a containerized app.
How we’re solving
- Declare an environment variable and try to access it in React
- Access an ‘API’ environment variable by leveraging webpack
- Containerize the application in Docker and try it out
Setting environment variables
Typically, if we want to set an environment variable, we’d $ export VARIABLE=VALUE
. We could also store them in a .env
file. As we know, Node gives us the process.env.VARIABLE
for access.
Trying to access process.env in React
Let’s say that we want to perform a fetch
GET request on a URL that’s stored in an environment variable. Maybe the variable is API=http://localhost:5000
. In React, trying to access this variable in the request might look this:
Running that will trigger the .catch
because the browser can’t read this environment variable. In React, we’ll need to use webpack to map the OS’s environment variables to webpack’s env variables.
Using webpack to assign environment variables
Let’s assume we’re working with an app that’s mostly set up, given that we’re in this article right now. If webpack isn’t already installed within the Node application, let’s do that: $ npm i --save-dev webpack webpack-cli
Within our package.json
file, we can access the scripts
key containing npm scripts that run CLI commands.
We can use the webpack CLI to write a command that starts our React app. Within this command, we can add an --env
option which will add webpack placeholders for these variables. It might look something like this:
webpack --env.API=http://localhost:5000 --config webpack.config.js --watch
Before this command does anything useful for us, we need to understand that this env.API
webpack env variable won’t be compiled to the actual OS environment variable value. This is because webpack needs us to explicitly tell it to. We can do that in webpack.config.js
…
Building the webpack config:
We’re going to use webpack’s DefinePlugin
to map webpack’s CLI-defined variables to the system’s environment variables:
This is an entire config. You might not have one yet, or your config may look different, but the important points are:
- module.exports is now a function that accepts the
env
argument - We have a helper
envs
that maps environment variables to webpack’s env variables. - The returned config has a
plugins
key with our newDefinePlugin(envs)
Accessing our set environment variables
Now that webpack is configured to look for environment variables on process.env
and assign them to the variables we include in the --env
option with the CLI script, we can access them in React.
Let’s go back to our API
environment variable. We can set $ export API=http://localhost:5000
. In package.json
at the scripts
key, we‘ll replace our webpack start script value with
webpack --env.API=$API --config webpack.config.js --watch
Now webpack’s variable is mapped to the OS environment variable. So going back to our fetch GET request on process.env.API
base URL:
We’ll pass into the .then and successfully log the data on the other end of the route, if that’s the route that exists on our supposed API. Now we can access environment variables from React!
Accessing environment variables in a container
Now that we’ve gained access to process.env
from React, we can write a Dockerfile
to build an image from. When we run the image as a container, we can use the Docker CLI for running the image to include environment variables we want set in its OS.
I’m again going to make the wild assumption that you have a server to put React on the browser with. Let’s say our Dockerfile in the root of our app looks something like this, then:
So we run a build command with the Docker CLI, maybe:
docker build -t example:1.0.0 .
Now that we have an image to run, we can pass it the API environment variable:
docker run -p 3000:3000 --env API=http://localhost:5000 example:1.0.0
Now, if we have an API backend running on localhost:5000
and we open up localhost:3000
, where we are running our containerized React app, the console will be logging the response object that we told it to in our fetch!
Some prefer to use a .env
file, but I find that in a cloud-native microservice architecture, it’s sometimes best to keep the application ignorant of the environment variables that it accesses.
Congrats! Thanks! And please comment anything missed!