Nothing really new here, just writing down this pattern I’ve noticed in various JavaScript apps (notably Express apps). First seen in Scott Moss’s ‘API Design’ course at FrontendMasters.

Concept

The basic concept is to collect all application configuration in one place so you don’t have to litter it all over the application. Use a “standard” JS component idea, as one does with Angular and React to put it all into a single location.

Use Case

My use case will be a rather generic Express API application. Using a service- or resource-oriented structure, I have an folder structures like so:

  app/
    posts/
      index.js
      postModel.js
      postsController.js
      postsRoutes.js
    users/
      index.js
      userModel.js
      usersContoller.js
      usersRoutes.js
    index.js
  config/
    development.js
    index.js
    production.js
    test.js
  public/
    images/
    javascripts/
    stylesheets/
    index.html
  index.js
  package.json

I’m taking advantage of the convention of using an index.js at the root of the components (and app) to make requiring the module as simple as providing the directory name.

Creating the configuration module

In ./config/index.js, I have the following:


var baseConfig = {
  env: process.env.NODE_ENV || 'development',
  logging: false,
  jwtSecret: process.env.JWT_SECRET
};

var envConfig = require('./' + baseConfig.env);

module.exports = Object.assign({}, baseConfig, envConfig);


A HUGE caveat, of course, is to NEVER save secrets and credentials in git (or Github, Gitlab, BitBucket, etc.) since they will be there forever, even if you later remove them.

Always use environment variables to configure secret values.

Creating the environment configurations

The environment configurations would be the same as the environment names with ‘.js’ added, so:

Each of these can be empty, but the files need to exist.

As an example, I always want logging during development, but not during production or testing.

In ./config/development.js:

module.exports = {
  logging: true
};

This would be merged during the Object.assign step in ./config/index.js.

Deep merging

The main limitation to the above is if the configuration has a deeper structure. The above works great when everything is in the top level of the configuration object, but as soon as you go deeper (objects within objects, etc), you need to do a deep merge.

The Lodash library has a few different methods that can be used here. The one I like to use is _.merge since it works like Object.assign in merging the members from left to right. (See stackoverflow for a more thorough explanation and comparison.)

Using the configuration

Now, with my per-environment configuration set, I use it in my application. In ./index.js (the root of the app), I put:

var config = require('./config');

Deeper down, in a server component, for example, I need to use the appropriate path lift to find it. Getting the configuration into the posts controller:

File ./app/posts/postsController.js

var config = require('../../../config'); // might need more '../' to get to the right level

When you need a configuration value, it’s right there on the config object:

if(config.env === 'development'){
   // ... do something you only do in development
}