Yos Riady optimize for learning 🚀

A Node.js Configuration Pattern

A Node.js Configuration Pattern

In this post, I’d like to share with you a pattern I’ve used time and time again for handling app configuration in Node projects using nconf.

I’ve found this approach to be a most flexible and intuitive way for storing and accepting configuration variables.

First, here’s an example project structure:

|-- project/
    |-- config/
            |-- default.json
            |-- development.json
            |-- production.json
            |-- test.json
    |-- lib/
            |-- configuration/
            |-- models/
            |-- utils/
    |-- test/
    |-- app.js
    |-- package.json
    |-- README.md

We create a Config object that is defined as follows:

// lib/configuration/index.js

var nconf = require('nconf');

function Config() {
  nconf.argv().env();
  var environment = nconf.get('NODE_ENV') || 'development';
  nconf.file(environment, './config/' + environment.toLowerCase() + '.json');
  nconf.file('default', './config/default.json');
}

Config.prototype.get = function(key) {
  return nconf.get(key);
};

module.exports = new Config();

We’re using nconf to give us the ability to add configuration with files, environment variables, command-line arguments, and atomic object merging.

Notice in the Config object constructor, we’re reading command line arguments (.argv()), environment variables (.env()), and environment config files (.file())in that order of specificity. Later loaded config items will override any previous item, so feel free to re-order them to what makes the most sense based on your use case. We merges the config items supplied across all the different methods into one single dictionary.

Config will load environment-specific config files such as development.json which looks like this:

{
  "app": {
    "port": 3000
  },
  "db": {
    "dialect": "postgres",
    "host": "localhost",
    "port": 5432,
    "dialect": "postgres",
    "database": "auth_development",
    "username": "yosriady",
    "password": ""
  },
  "key_service": {
    "host": "localhost",
    "port": 6379,
    "key_namespace": "auth:",
    "key_separator": ":",
    "expires_seconds": 6000
  },
  "jwt": {
    "secret": "placeholder-secretKey",
    "algorithm": "HS256",
    "secret_separator": ":"
  }
}

In addition, a default.json config file with a higher specificity than environment-specific .json files serve as a common configuration source across the different environments if needed.

Thanks to nconf, we can supply configuration values through environment variables and command line arguments as well. Config merges the config items supplied across all the different methods into one single dictionary.

We can then import Config whenever we need to access configuration values like so:

// app.js

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

var server = app.listen(config.get('app:port'), function() {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Auth Listening at https://%s:%s', host, port);
});

Let me know what you think!

Subscribe

Get notified of my latest articles by providing your email below.


Author

Hi, I'm Yos. I live and work in Singapore building nifty software products.