Using config and environment variables for client and back-end use with Javascript
August 18, 2017
We all need passwords and other credentials for API connections stored in our apps and websites, but it’s not a good idea to “hard code” them in your app because your code will likely be shipped all over the place, including Github, staging/production areas, or to another organization. Config variables are a solution to this problem of keeping sensitive data decoupled from your main app, but there are a few ways to do this depending on your setup.
This post will give a short overview of a few methods I’ve used in my Node/Javascript environments and discuss where you’d likely use them.
It’s worth noting at this point that using the environment variables described in this post only apply to projects which are running or built using Node. This includes the obvious ones like Vue, React and Node servers, but also extend to Wordpress front-end systems if they have something like Gulp or Webpack which builds the JS assets.
Read from config file
A common way of dealing with global variables is to have a configuration javascript file in your app somewhere, for example:
//config.js
{
"secretKey": "SAS110G4CRG",
"facebook": {
"clientID": "FBID01",
"clientSecret": "435SDFSF5DZVD7S"
}
}
This would mean you can import the config.js file into your website or app using ES6 imports with a bundler like Webpack, and have the config variables available anywhere in your app.
One benefit of a config file in this way is that it can be added to a .gitignore file so all sensitive information is kept out of version control. Obviously then when sharing the project with a colleague, the config file will need passing over and adding to their repos separately.
Using Node environment variables
Another way is to use environment variables. Your default environment variables can be accessed in your Terminal by entering printenv
. This will list a few random key-value pairs used by your machine (local or remote server).
Most of these can be accessed via Node (as it’s a back-end environment after all) using the process.env
command. Give it a try - create a file called env.js (or anything really) and within that file have the following:
//env.js
console.log(process.env);
And then simply run:
node env.js
This will list the Node accessible environment variables.
Using environment variables on the command line
You can define and pass environment variables to your node script when running the command straight from the Terminal. For example, the following will add 2 extra environment variables to the output of the previous env.js file, enabling you to use the variables in your code:
BADASS=Tyrion FACEBOOK_SECRET=fghasqwkefkvdls node env.js
This method is used in many popular projects, such as Jekyll which uses the following command to build a production ready static site:
JEKYLL_ENV=production jekyll build
In the Jekyll command above, the production variable is referenced in the Jekyll build script to make the necessary changes to build a Jekyll site in production mode.
This command line method is great for passing one or more environment variables into your project. It can get a bit messy though, especially if you have a few, plus you need to manually type them into the Terminal each time. One answer to this is by using npm scripts.
Using environment variables with npm scripts
The above command line method is good but by using npm scripts, you can save typing them out and add it all to your package.json file:
//package.json
{
...
"scripts": {
"start": "FACEBOOK_SECRET=435SDFSF5DZVD7S node env.js"
}
...
}
Of course, this means your FACEBOOK_SECRET environment variable is hard coded in to your package.json file so you’d end up checking it in to version control, which you don’t want!
You may only want to include some less important information in your npm script build command. Perhaps something like defining what environment you’re building for - development, production or staging for example, and what your remote API URL is for that environment. That could look like:
//package.json
{
...
"scripts": {
"start_staging": "ENV=staging API_URL=http://my.api.com node env.js"
}
...
}
So there are some good use cases for using environment variables in this way, but none of these ways seem good for API keys or other secret information. You may have a fair few different secret information sets which will be a pain to manually enter in to the Terminal for each build of your app.
Using environment variables with a .env file
A popular way of managing secret variables in your projects is to use a .env file. Simply create a .env file and add your variables, for example:
//.env
SOME_KEY=someValue
HELLO=world
FACEBOOK_SECRET=435SDFSF5DZVD7S
This is so popular that it exists in other languages such as PHP and Python as it exists at the OS level, not the language level, meaning it’s universal and everyone picking up your project will know what the .env file is for.
In Node, the variables defined in the .env file are not instantly accessible via the process.env
variable. You must first install a package such as dotenv, and run the following line of code in your project as early as possible:
require('dotenv').config()
This will allow you to grab the variables in your code using process.env.SOME_KEY
for example.
Similar to having a .js config file, .env files can be excluded from version control to keep your secret keys nice and… secret. And it is very similar to the first config file example at the start of this post, right? In fact, there’s not much difference at all except for the .env files require another dependency (dotenv npm package). That’s what I thought until I read this Reddit post. As mentioned before, .env files are very widely used, so you can keep your process.env.API_SECRET
in your code and set the variables using your Heroku, Docker, Upstart etc as they all support environment variables.
Using between different environments
All the above methods are great for working locally with your code. They de-couple the sensitive and re-usable information from your code whilst keeping it all in one place.
It gets a little more complicated when it comes to deploying to different environments - staging, production and testing for example, which is quite common as they may share different API URLs or other global variables. I like the simple way of keeping all instances of your config info in one place:
//config.js
var config = {
production: {
facebookSecret: 'efjosnadjfbri',
api_url: 'http://my.remote.api'
},
default: {
facebookSecret: efjosnadjfbri,
api_url: 'http://my.dev.api'
}
}
exports.get = function get(env) {
return config[env] || config.default;
}
Then call the config.js file inside your app, specifying the config you need using the process.env.NODE_ENV
environment variable:
var config = require('./config.js').get(process.env.NODE_ENV);
However as with anything, it all depends on your environment. Here’s a few thoughts:
- You may have a setup whereby your code is pushed to your own server via Git and built there. If this is the case, you can have a .env file for your local version which isn’t sent up to the production site, and manually enter the environment variables when running your build command:
FACEBOOK_SECRET=fghasqwkefkvdls node env.js
Or simply SSH into the server and manually add/edit a .env file!
-
As mentioned earlier, systems like Heroku and Docker enable you to define environment variables on their own systems, which is great if that’s what you’re using. It keeps all sensitive info out of source control, and means you don’t need to worry about pushing the sensitive data up to your remote apps.
-
It might be that environment variables and the whole .env file doesn’t appeal you or you’d rather use .js config files, which is fine too! In fact, it’s common to use a mixture of both environment variables and .js config files. Then you could have all your secret information in your .env file and your non-secret information in a checked-in .js file:
var config = {};
config.api = {};
config.api.url = process.env.API_URL || http://my.api.com;
config.axios = 'General http send information'
module.exports = config;
This will use default values or the value you set in the environment variables.
-
What about Wordpress/PHP local and production variables? As mentioned before, this post of mainly about Node or client side bundled environments, but if you want to read a little into the PHP side, check out my post. on deployment using MAMP, Git and Wordpress.
-
You can also use environment variables in frameworks like React and Vue by using Webpack’s
webpack.DefinePlugin()
, allowing even more DRY code across front and back-end envrionments all the while adhering to the 12 Factor App mantra.
Final thoughts
Thanks for reading! I know there are so many ways to achieve what I’ve been discussing in this post as the topic is pretty broad. I’ve covered a few of ways I’ve used, but if you can suggest a better way, please get in touch on Twitter and let me know - I’m always open to hearing new ideas.
Senior Engineer at Haven