Node environment variables and race conditions

I recently got into a race condition situation when loading a node module that I use for configuration. This config file sets values differently depending on the environment. I was getting different environemnts in my code at the same time!

My strategy below is to set the environment variable first thing. Then, everything that looks up that environment variable (i.e. my config module) will be good to go! Easy right? Well…. not so much.

setup.js

1
2
3
4
5
6
process.env.NODE_ENV = 'test';
console.log('calling config from setup.js');
var config = require('../config/config');
var seeder = require('./seeder');

seeder.js

1
2
console.log('calling config from seeder.js');
var config = require('../config/config');

Here’s the output I was seeing:

1
2
calling config from seeder.js
calling config from setup.js

That’s backwards from what I expect. Now, to be fair, when I hit this script directly, it works as expected. However, when I require setup.js as a module, that’s when I see my issues.

Because the seeder.js script is running first, the environment remains unset. Because of this, there’s no certainly about my environment at any given time.

The workaround?

The workaround is to avoid the problem altogether. But it really is the best solution.

What we can do is launch the script with the environment set and then remove the setting of the environment within the code. Here’s how we can launch the script in an environment called “test”:

1
$ NODE_ENV=test mocha test

…where mocha could be node or any other executable.

To avoid writing this every time, we can use the npm’s script functionality. In our package.json file, we can add a line under an entry called scripts.

1
2
3
"scripts": {
"test": "NODE_ENV=test mocha test"
}

Now, when we run tests, we can run:

1
$ npm test

This is a great lesson why global variables should be avoided in node. I’ll give the main environment variable a pass though ;)

avatar

Dev Blog