How to Easily Set-up Node Config Following These Best Practices

When building any kind of server side app, you will eventually have the following thoughts:

I would like to bind my app to a specific port.

I would like to enter a connection string so I can connect to my database.

I would like to use a third-party service through a provided API key or credentials.

Given values to these kinds of parameters will yield a configuration. It is a crucial part of the system as our application will have lots of configurable elements, so it must be handled properly throughout the codebase.

In this article, we will look at the DOs and DON’Ts of Node config handling.

Keeping it together

Instead of defining parts of the config in separate files, it is strongly advised to have a config.js file that will hold all configuration in a centralized way.

This will help other developers to locate and adjust config values much more easily, and by having one centralized file ensures reusability of config values and also it can give a quick insight about the project and what technologies/services/libraries are used.

Use the env Luke

During testing, we would like to change the database from the one we are using for development. This way we are able to safely drop all data from it and we don’t mess up our development (or production – yikes!) database. Right now we have to edit the config file each time we would like to run tests.

We will make the app aware of the currently set environment. In Node, the NODE_ENV variable is used for this. Commonly used environments are development or dev when coding on our local machine, production or prod when it is deployed to the live server, test when we are running unit/integration tests (and much more).

The current environment can be retrieved through process.env.NODE_ENV.

Now, when we are running tests (and our NODE_ENV is set to test), the localhost:27017/test will be used instead of localhost:27017/db.

Our app is now aware about its environment; however, when multiple developers are working on a project, their machine setup can (and will) differ from each other and also from the server they are deploying the app.

Environment variables are the go-to solution for this kind of problem too. Aside from creating environment specific configs, we should also offer more fine-grained options too. This means that each element of the configuration should be manipulated through its own environment variable, but a default value should be provided that works for most cases.

For example, when John runs the tests, his Mongo DB runs in Docker on port 27000 instead of the default 27017. By explicitly setting TEST_DB_PORT to 27000, John can easily set the system to his preferences without code modification, and for others connecting to Mongo DB will still work out of the box. Environment variables are also supported by most CIs and PaaS providers so your Node config can also be easily set when your code is verified on the build server, or it is deployed to your production environment.

One quick note here that every variable read from process.env will be a String; thus, type conversion is needed sometimes (parseInt for converting to Number).

We can adjust the system’s individual configurations without code modification. That looks good; however, we need to talk about sensitive configurations.

Keep your secrets to yourself

All these values stored in config.js will be available in the code’s repository. Putting the default port on which the app will bind into the code is not really an issue; however, the login credentials for the production database or the API key for that cool third party service is a big no-no. Both adding and removing them :).

The ideal way to handle these situations is to leave the sensitive configuration variables blank in the code, then set the environment variables on the system where your app is running.

Of course, when running the app on your own machine or deploying it to your own server (or IaaS), every sensitive environment variable must be set every time the app is started. This can be tedious; however, there are great tools which can help you overcome this problem.

Do the dotenv

Dotenv is a great Node library that enables the use of a .env file that can hold environment variables and feed them to your Node app.

In this (rather complex) example we can take a glimpse of what convict combined with dotenv can give us.

Conclusion

In this article we’ve given a step-by-step guide of how to build an efficient configuration module for your Node app. The principles can also be applied to other technologies as well. The key takeaways from this post are the following:

Centralize your app’s configuration to a single module

Enable config variable setting without any code modification

Use the environment variables

Do not expose secret configuration values

Don’t be afraid to use third-party config libraries (dotenv, convict) as they will ease the development