12-factor Node.js application configuration management without the `config` npm package
Tags: node, javascript, deployment, webdev
Published: 2019-02-13 08:16:35

---
title: 12-factor Node.js application configuration management without the `config` npm package
published: true
tags: node, JavaScript, deployment, webdev
canonical_url: https://codewithhugo.com/node-configuration-management/
---

The `config` npm package is great ([npmjs.com/package/config](https://www.npmjs.com/package/config)), but it encourages confusing and non-[12-factor-app](https://12factor.net/)-compliant patterns.

We’ll look at some of the patterns it encourages and why they’ll bring you struggles down the road as well a simple, single-file, no-dependency way to define your configuration.

## Sprawling configuration: hard to pinpoint where config is set

The main thing it encourages is configuration sprawl: some of your configuration lives in JSON files, some of your configuration comes from environment variables (and is glued together using JSON files). Some config fields change depending on `NODE_ENV`, others do not.

Worst of all, config is dynamically loaded using a `config.get('path.in.the.json.config.object')` call. This creates an affordance for users to have deeply-nested configuration object(s), which isn’t desirable, your configuration should be minimal and it shouldn’t live in application code.

See the following from the “config” section in “The Twelve-Factor App” (see it in full at [12factor.net/config](https://12factor.net/config)):

> Apps sometimes store config as constants in the code. This is a violation of twelve-factor, which requires **strict separation of config from code**. Config varies substantially across deploys, code does not.

## Non-granular configuration

Here’s another reason why having a package that makes it easy to have config objects isn’t a good idea according to 12 Factor again (see the full config section at [12factor.net/config](https://12factor.net/config)):

> In a twelve-factor app, env vars are **granular controls** , each **fully orthogonal** to other env vars. They are **never grouped together as “environments”** , but instead are independently managed for each deploy.

Having a `default.json`, `production.json`, `test.json`, `custom-environment-variables.json` is just not 12-factor, since you’re not supposed to **group** your config. It should be “here’s a database connection URL”, “here’s a backing service URL”, “here’s a cache connection string”.

It entices developers down the line keep adding switches and settings in a `"database": {}` field. Those concerns will not be **orthogonal** to each other, what’s more, they’re likely to be application-level concerns, eg. “should the database client try to reconnect?”. That’s not something you should be overriding with environment variables or toggling across the environments. It’s a setting that should be hard-coded into the application depending on whether or not the database is critical for example.

## A single config.js file

`config.js` at the root of you application would look like this:

```
module.exports = {
  NAME_OF_CONFIG: process.env.NAME_OF_CONFIG || 'default-config',
  DATABASE_URL: process.env.DATABASE_URL,
  REDIS_URL: process.env.REDIS_URL || 'localhost:6379',
  X_ENABLED: process.env.X_ENABLED === 'true',
};

```

In the above, there are examples for how you would default a configuration variable (`NAME_OF_CONFIG`, `REDIS_URL`) and how you would check a boolean flag (`X_ENABLED`).

## Making process.env fit for purpose

In Node.js `process.env` variables (environment variables) are strings, JavaScript is pretty loose with types, but it’s sometimes useful to convert `process.env` variables to another type.

### Parsing a number from process.env

```
const SESSION_TIMEOUT = parseInt(process.env.SESSION_TIMEOUT, 10)
module.exports = {
  SESSION_TIMEOUT
};

```

### Converting to Boolean from process.env

Comparing against the `'true'` string tends to be enough:

```
module.exports = {
  IS_DEV: process.env.IS_DEV === 'true',
};

```

## Consuming config.js

To get data from `config.js` would be like the following, where we conditionally set some `'json spaces'`, a request timeout and listen on a port with an Express app,

```
const { REQUEST_TIMEOUT, X_ENABLED, PORT } = require('./config')
const express = require('express')
const app = express()

if(X_ENABLED) {
  app.set('json spaces', 2)
}

app.use((req, res, next) => {
  req.setTimeout(REQUEST_TIMEOUT); 
  next()
})

app.listen(PORT, () => {
  console.log(`App listening on ${PORT}`);
});

```

## Bonus: getting values from a .env file

Sometimes you’ll want to export values from a .env file into your shell sessionThe following snippet does just this and is an extract of [this bash cheatsheet](https://codewithhugo/just-enough-bash-to-be-dangerous/#inject-env-into-your-bash-session-environment).

```
export $(cat .env | xargs)
```

> Note the above works for \*NIX environments

[<svg xmlns="http://www.w3.org/2000/svg" style="height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white" viewbox="0 0 32 32"><title>unsplash-logo</title>
<path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z"></path></svg>Filip Gielda](https://unsplash.com/@filipovsky?utm_medium=referral&utm_campaign=photographer-credit&utm_content=creditBadge "Download free do whatever you want high-resolution photos from Filip Gielda")