How to store Node.js deployment settings/configuration files?
Asked Answered
A

30

718

I have been working on a few Node apps, and I've been looking for a good pattern of storing deployment-related settings. In the Django world (where I come from), the common practise would be to have a settings.py file containing the standard settings (timezone, etc), and then a local_settings.py for deployment specific settings, ie. what database to talk to, what memcache socket, e-mail address for the admins and so on.

I have been looking for similar patterns for Node. Just a config file would be nice, so it does not have to be jammed in with everything else in app.js, but I find it important to have a way to have server-specific configuration in a file that is not in source control. The same app could well be deployed across different servers with wildly different settings, and having to deal with merge conflicts and all that is not my idea of fun.

So is there some kind of framework/tool for this, or does everyone just hack something together themselves?

Aviator answered 3/5, 2011 at 12:9 Comment(1)
i really like the way the configuration is done in mean.js. basically, they store app relevant config in a seperat sort of module, based on different settings per app environment (for production, development, testing) and passing specific details through app environment variables, like secrets etc.Jeffry
A
219

Much later, I found a pretty good Node.js module for managing configuration: nconf.

A simple example:

var nconf = require('nconf');

// First consider commandline arguments and environment variables, respectively.
nconf.argv().env();

// Then load configuration from a designated file.
nconf.file({ file: 'config.json' });

// Provide default values for settings not provided above.
nconf.defaults({
    'http': {
        'port': 1337
    }
});

// Once this is in place, you can just use nconf.get to get your settings.
// So this would configure `myApp` to listen on port 1337 if the port
// has not been overridden by any of the three configuration inputs
// mentioned above.
myApp.listen(nconf.get('http:port'));

It also supports storing settings in Redis, writing configuration files, and has a fairly solid API, and is also backed by one of the more well-respected Node.js shops, Nodejitsu, as part of the Flatiron framework initiative, so it should be fairly future-proof.

Check out nconf at Github.

Aviator answered 19/12, 2011 at 15:28 Comment(16)
Maybe a dumb question but I haven't seen a clear explaination: Where do I set node environment variables? I am already using nconf but it's not clear where I would set environmental variables. Is it in nginx/apache? Is it another config file?Maize
I don't think use .json file as config is a good idea, since comments are not allowed.Burnet
This looks great. I think you'll surprise a lot of Unixheads if the config file overrides command-line options and environment variables. We're used to the following order of ascending precedence: config file(s), environment variables, command-line options.Immerge
@Immerge Wait until you find out that boolean options are always set on argv, therefore breaking precedence... :/Tova
@DanielC.Sobral It's a real shame. Oh, and LTNS! :-)Immerge
would it make sense to attach nconf to the process variable or perhaps nconf does that out of the box?Floriated
Using a custom json format with nconf solves many of the issues people have with json files, including commenting ability. A few solutions are provided here: github.com/indexzero/nconf/issues/113Tuque
Plus, JS files can be require'd and fed to an nconf literal store!Tuque
I prefer the nconf approach because it allows me to have environment-variable-based config on the server and file-based config for development. Locally, I prefer not to use environment-variables because they are global, which might be a problem when working on multiple projects. For the server, I use the AWS dashboard, which is actually quite nice. Keeping config out of source-control means that eb deploy continues to work ootb.Toner
The benefit to using a system like this over require is that you will use the process.env variables. Which is beneficial during automated deploys where you don't push a config.js file with secrets.Aquilar
@KeithHolliday There are other ways to handle keys. Using environment variables doesn't solve this problem because something still needs to know what the key is to set an environment variable with that key. If you're doing this manually, you could do the same thing in a config file - have a keys config file (outside of version control) that you set manually and load into your app somewhere.Womanly
I would definitely not recommend having environment variables and a config file and the defaults. Using environment variables and config files is needlessly complicated.Womanly
@BT How would you use a config file with sensitive variables to deploy to a system without access the server? For example, using Kubernetes or Heroku, you must separate the config variables from your app. Using a config file wouldn't be easy (or even possible) without loging into individual nodes.Aquilar
@KeithHolliday I don't quite follow the case you're explaining, but I certainly agree with making config completely separate from your app. If for example, you deploy a frontend bundle for use in a web app, the public configuration should be a completely separate bundle. If you're talking about writing keys to a server, then the keys should be separate from the rest of the config (I consider keys "data" akin to database data, rather than config-proper). Also, if you don't have access to the server, wouldn't environment variables be inaccessible?Womanly
While nconf may be stilll active, apparently "part of the Flatiron framework initiative, so it should be fairly future-proof" was not a reliable indicator... trying to access flatironjs.org results in "Server not found: Firefox can’t find the server at www.flatironjs.org."Dealt
@Immerge The order is actually reversed, i.e. nconf uses the first setting it sees (from argv, ENV, config.json, defaults). So the outcome is as Unixheads would expect.Quahog
S
838

I use a package.json for my packages and a config.js for my configuration, which looks like:

var config = {};

config.twitter = {};
config.redis = {};
config.web = {};

config.default_stuff =  ['red','green','blue','apple','yellow','orange','politics'];
config.twitter.user_name = process.env.TWITTER_USER || 'username';
config.twitter.password=  process.env.TWITTER_PASSWORD || 'password';
config.redis.uri = process.env.DUOSTACK_DB_REDIS;
config.redis.host = 'hostname';
config.redis.port = 6379;
config.web.port = process.env.WEB_PORT || 9980;

module.exports = config;

I load the config from my project:

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

and then I can access my things from config.db_host, config.db_port, etc... This lets me either use hardcoded parameters, or parameters stored in environmental variables if I don't want to store passwords in source control.

I also generate a package.json and insert a dependencies section:

"dependencies": {
  "cradle": "0.5.5",
  "jade": "0.10.4",
  "redis": "0.5.11",
  "socket.io": "0.6.16",
  "twitter-node": "0.0.2",
  "express": "2.2.0"
}

When I clone the project to my local machine, I run npm install to install the packages. More info on that here.

The project is stored in GitHub, with remotes added for my production server.

Shellans answered 3/5, 2011 at 13:54 Comment(20)
what happens when you have different config settings for dev vs. prod?Iseabal
I haven't but here's one way to do it.. for each env, set the env name in an ENV variable. Then in this file, its just javascript.. use a case or if statement to selectively load the appropriate variables. You could even make a separate config subfile for each env, and in the if statement, reload the subfile here into a subconfig var, and export that subconfig var to the main config.. All i'm basically trying to say is that its just js, so you can be creativeShellans
what process.env? where does it locate? And how to set it?Hammurabi
I'm just wondering how secure this approach is ? Can this config.js not be read in a fairly easy way ?Pigeon
its source code.. it can be read as little or as much as the rest of the source code it goes with, depending on how you set your permissionsShellans
What was your thinking behind the choice of not nesting the values syntactically?Konstanz
I was thinking "wow.. i've been looking at node.js for a few hours and my app is already working.. btw, maybe i'll share this random bit of code I came up with"Shellans
Each configuration item has its full name included on the same line which is very convenient if using shell scripts with sed expressions to deploy.Grammarian
This is a great approach for the common scenario. It comes unstuck if you have a non-node process that shares the config file. For example if you have a legacy app and node sharing the config. JSON is a good common format. But again, maybe I am just dealing with an edge case.Savoyard
A plain js file is really the simplest and most flexible approach! I sometimes freeze the config object (and all its embedded objects) to make it immutable... Might be a good idea in a larger development group I guess.Marrowfat
Be wary of this approach if your project has any security considerations, as it means checking things like DB passwords, auth tokens, etc. into your repository, which is (a) insecure and (b) resistant to collaboration. Use nconf or similar, see below.Neddra
Can't you still use environment variables to store those pass words? Isn't that what this line is for: config.twitter.password= process.env.TWITTER_PASSWORD || 'password';Floatable
This is how I separate configurations: I not only have a config.js, which gets called in the code, but also a config folder. I have several versions of config.js in there, such as config/prod.js and config/dev.js. I exclude the config.js and /config folder in my .gitignore, so the folder only exists on my private dev server. Then I copy the configurations to their respective environments, like with scp /path/to/project/config/prod.js prodsrvr:/path/to/project/config.js.Mannerism
For the sake of security you can now use attosol.com/… so that all the secrets are saved to Azure Key Vault instead.Flavorous
This is static config only. You can't store anything dynamically with this approach. For example, settings that are hard to store in DB.Darbydarce
This answer describes how to handle different configs for prod and dev environments.Minh
@Shellans Just to be safe and prevent those settings from being changed in code I would suggest you update your answer and do an object.freeze on each of your objects such as : Object.freeze(config.twitter);Recuperative
hii how can I get my site_url in my ejs file . I have given var config = require('./config'); in my app.js and in config.js all configs are present.Drily
hello how to exlude the config.js from bundling in webpack 4Mada
Tht's the only answer which shows how to override variable with ENV_VAARTrembly
P
269

You can require JSON files as of Node v0.5.x (referencing this answer)

config.json:

{
    "username" : "root",
    "password" : "foot"
}

app.js:

var config = require('./config.json');
log_in(config.username, config.password);
Pyrrha answered 3/2, 2013 at 23:43 Comment(6)
Not so impressed with that feature. You could require("./config.js") and you get the ability to add comments to config files which I consider very important, and other bells and whistles. If you config is just properties and no code you loose nothing by require(config.js) with you JSON prefixed by exports.config =Spurn
@Spurn you're right but there used to be a big discussion going on about the 'correctness' / usability of using dumb vs. smart templating systems that told me: (1) you typically want a declarative / dumb language for templating / options (2) it's a bad idea to reconstruct an "almost-PL" to just do templating (or configuration)—better to re-use your existing real PL with known behaviors. in so far +1 for recycling JS to do user settings; -1 for not going the declarative approach. we've seen some pretty complex config stuff done the declarative way; my gut tells me this is the way to go.Darien
No intellisense on objects from json files in VScode (end 2017). Fully working intellisense for objects from module.exports.Bareback
using this method, where would you store the config.json file? And how can the user edit it?Rhinitis
@Rhinitis you can put it anywhere in your project, it's just a file. If it's deeper in your project, you can write the whole path: require('./path/to/config.json'). If you want a user to edit it, you should switch to using fs.read and fs.write to read and write to the file.Pyrrha
@Pyrrha the problem with require is that tell webpack to pack it unless you put it in a static folder. I would think a hacker could access it. Point being, if web pack, packs it then you can't customize based on the environment dev,qa,stage,preprod, etc..Xyloid
A
219

Much later, I found a pretty good Node.js module for managing configuration: nconf.

A simple example:

var nconf = require('nconf');

// First consider commandline arguments and environment variables, respectively.
nconf.argv().env();

// Then load configuration from a designated file.
nconf.file({ file: 'config.json' });

// Provide default values for settings not provided above.
nconf.defaults({
    'http': {
        'port': 1337
    }
});

// Once this is in place, you can just use nconf.get to get your settings.
// So this would configure `myApp` to listen on port 1337 if the port
// has not been overridden by any of the three configuration inputs
// mentioned above.
myApp.listen(nconf.get('http:port'));

It also supports storing settings in Redis, writing configuration files, and has a fairly solid API, and is also backed by one of the more well-respected Node.js shops, Nodejitsu, as part of the Flatiron framework initiative, so it should be fairly future-proof.

Check out nconf at Github.

Aviator answered 19/12, 2011 at 15:28 Comment(16)
Maybe a dumb question but I haven't seen a clear explaination: Where do I set node environment variables? I am already using nconf but it's not clear where I would set environmental variables. Is it in nginx/apache? Is it another config file?Maize
I don't think use .json file as config is a good idea, since comments are not allowed.Burnet
This looks great. I think you'll surprise a lot of Unixheads if the config file overrides command-line options and environment variables. We're used to the following order of ascending precedence: config file(s), environment variables, command-line options.Immerge
@Immerge Wait until you find out that boolean options are always set on argv, therefore breaking precedence... :/Tova
@DanielC.Sobral It's a real shame. Oh, and LTNS! :-)Immerge
would it make sense to attach nconf to the process variable or perhaps nconf does that out of the box?Floriated
Using a custom json format with nconf solves many of the issues people have with json files, including commenting ability. A few solutions are provided here: github.com/indexzero/nconf/issues/113Tuque
Plus, JS files can be require'd and fed to an nconf literal store!Tuque
I prefer the nconf approach because it allows me to have environment-variable-based config on the server and file-based config for development. Locally, I prefer not to use environment-variables because they are global, which might be a problem when working on multiple projects. For the server, I use the AWS dashboard, which is actually quite nice. Keeping config out of source-control means that eb deploy continues to work ootb.Toner
The benefit to using a system like this over require is that you will use the process.env variables. Which is beneficial during automated deploys where you don't push a config.js file with secrets.Aquilar
@KeithHolliday There are other ways to handle keys. Using environment variables doesn't solve this problem because something still needs to know what the key is to set an environment variable with that key. If you're doing this manually, you could do the same thing in a config file - have a keys config file (outside of version control) that you set manually and load into your app somewhere.Womanly
I would definitely not recommend having environment variables and a config file and the defaults. Using environment variables and config files is needlessly complicated.Womanly
@BT How would you use a config file with sensitive variables to deploy to a system without access the server? For example, using Kubernetes or Heroku, you must separate the config variables from your app. Using a config file wouldn't be easy (or even possible) without loging into individual nodes.Aquilar
@KeithHolliday I don't quite follow the case you're explaining, but I certainly agree with making config completely separate from your app. If for example, you deploy a frontend bundle for use in a web app, the public configuration should be a completely separate bundle. If you're talking about writing keys to a server, then the keys should be separate from the rest of the config (I consider keys "data" akin to database data, rather than config-proper). Also, if you don't have access to the server, wouldn't environment variables be inaccessible?Womanly
While nconf may be stilll active, apparently "part of the Flatiron framework initiative, so it should be fairly future-proof" was not a reliable indicator... trying to access flatironjs.org results in "Server not found: Firefox can’t find the server at www.flatironjs.org."Dealt
@Immerge The order is actually reversed, i.e. nconf uses the first setting it sees (from argv, ENV, config.json, defaults). So the outcome is as Unixheads would expect.Quahog
I
111

My solution is fairly simple:

Load the environment config in ./config/index.js

var env = process.env.NODE_ENV || 'development'
  , cfg = require('./config.'+env);

module.exports = cfg;

Define some defaults in ./config/config.global.js

var config = module.exports = {};

config.env = 'development';
config.hostname = 'dev.example.com';

//mongo database
config.mongo = {};
config.mongo.uri = process.env.MONGO_URI || 'localhost';
config.mongo.db = 'example_dev';

Override the defaults in ./config/config.test.js

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

config.env = 'test';
config.hostname = 'test.example';
config.mongo.db = 'example_test';

module.exports = config;

Using it in ./models/user.js:

var mongoose = require('mongoose')
, cfg = require('../config')
, db = mongoose.createConnection(cfg.mongo.uri, cfg.mongo.db);

Running your app in test environment:

NODE_ENV=test node ./app.js
Iseabal answered 26/11, 2012 at 23:20 Comment(5)
I prefer this one. As mentioned by others JSON is not a preferred storage structure and this layering with globals is simple & effectiveKlong
The only reason i would prefer this over nconf is because it allows .js format for config (dev, test and prod) files. allowing us to document each config option which otherwise is not possible with JSON format.Hannelorehanner
BTW, NODE_ENV defaults to 'development'. You should check for 'production' instead.Confucian
I'm not checking for development. I'm defaulting to it. Not sure why I would ever default to production.Iseabal
This is the simplest solution. If you deploy your node app to Azure App Service, you can set the process' environment variable in the app service's configuration setting, see learn.microsoft.com/en-us/azure/app-service/configure-commonDiorama
O
45

You might also look to dotenv which follows the tenets of a twelve-factor app.

I used to use node-config, but created dotenv for that reason. It was completely inspired by ruby's dotenv library.

Usage is quite easy:

var dotenv = require('dotenv');
dotenv.load();

Then you just create a .env file and put your settings in there like so:

S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
OTHER_SECRET_STUFF=my_cats_middle_name

That's dotenv for nodejs.

Oakleil answered 30/4, 2014 at 0:59 Comment(4)
Or just use foreman run node xx.js this will automatically read in your .env file too.Yoko
would I use this approach for production also ?Cotillion
@lamar no, you set them in the env variables on the actual server. That was each time you deploy they are there but not in the source code.Eluviation
@Lamar yes you can actually, as a more portable alternative to setting env variables on the server. The important point is to not include the .env file in your version control or deployment process.Ferromagnetic
E
35

Are you guys using npm to start your scripts (env etc) ?

If you use .env files you can include them in your package.json and use npm to source/start them.

Example:

{
  "name": "server",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node test.js",
    "start-dev": "source dev.env; node test.js",
    "start-prod": "source prod.env; node test.js"
  },
  "dependencies": {
    "mysql": "*"
  }
}

then run the npm scripts:

$ npm start-dev

Its described here https://gist.github.com/ericelliott/4152984 All credit to Eric Elliot

Evilminded answered 22/10, 2013 at 23:54 Comment(3)
Can you explain what "source" is? I get source : not found Indifferentism
@Indifferentism source (or simply, .) is a built-in command in Unix shells (Bash, etc.) to read and execute commands from the given file, in the current shell. That is, the commands are not executed in a sub-shell. The effect of that in this example is that the environment variables defined in prod.env are added to the current shell and hence, passed to any child process spawned by this shell. You seem to be using Windows CMD. See this question for more details.Grayish
Worth noting - 12 factor app recommends not creating dev.env and prod.env, but having a single .env file per deploy.Desiderative
M
27

You might also look to node-config which loads configuration file depending on $HOST and $NODE_ENV variable (a little bit like RoR) : documentation.

This can be quite useful for different deployment settings (development, test or production).

Marmoreal answered 23/4, 2012 at 17:54 Comment(0)
T
25

Just do a simple settings.js with exports:

exports.my_password = 'value'

Then, in your script, do a require:

var settings = require('./settings.js');

All your settings now will be availabe via settings variable:

settings.my_password // 'value'
Triplet answered 27/1, 2013 at 16:47 Comment(2)
@backdesk of course you could set up a secret storage system which would encrypt secrets and limit access using ip, some tokens, etc. But in the end of the day it all comes to just reading some files from the disk, be it encrypted or not.Triplet
@backdesk There is no problem with the example. Is just that: an example for explaining something concrete.Rah
Q
17

Convict is another option that adds a schema for validation. Like nconf, it supports loading settings from any combination of environment variables, arguments, files, and json objects.

Example from the README:

var convict = require('convict');
var conf = convict({
  env: {
    doc: "The applicaton environment.",
    format: ["production", "development", "test"],
    default: "development",
    env: "NODE_ENV"
  },
  ip: {
    doc: "The IP address to bind.",
    format: "ipaddress",
    default: "127.0.0.1",
    env: "IP_ADDRESS",
  },
  port: {
    doc: "The port to bind.",
    format: "port",
    default: 0,
    env: "PORT"
  }
});

Getting started article: Taming Configurations with node-convict

Quicken answered 5/8, 2013 at 19:0 Comment(0)
W
16

I'm going to throw my hat into the ring here because none of these answers address all the critical components that pretty much any system needs. Considerations:

  • Public configuration (that can be seen by the frontend) vs private configuration (guy mograbi got this one right). And ensuring these are kept separate.
  • Secrets like keys
  • Defaults vs environment-specific overrides
  • Frontend bundles

Here's how I do my configuration:

  • config.default.private.js - In version control, these are default configuration options that can only be seen by your backend.
  • config.default.public.js - In version control, these are default configuration options that can be seen by backend and frontend
  • config.dev.private.js - If you need different private defaults for dev.
  • config.dev.public.js - If you need different public defaults for dev.
  • config.private.js - Not in version control, these are environment specific options that override config.default.private.js
  • config.public.js - Not in version control, these are environment specific options that override config.default.public.js
  • keys/ - A folder where each file stores a different secret of some kind. This is also not under version control (keys should never be under version control).

I use plain-old javascript files for configuration so I have the full power of the javascript langauge (including comments and the ability to do things like load the default config file in the environment-specific file so they can then be overridden). If you want to use environment variables, you can load them inside those config files (tho I recommend against using env vars for the same reason I don't recommend using json files - you don't have the power of a programming language to construct your config).

The reason each key is in a separate file is for installer use. This allows you to have an installer that creates keys on-machine and stores them in the keys folder. Without this, your installer might fail when you load your configuration file that can't access your keys. This way you can traverse the directory and load any key files that are in that folder without having to worry about what exists and what doesn't in any given version of your code.

Since you probably have keys loaded in your private configuration, you definitely don't want to load your private config in any frontend code. While its probably strictly more ideal to completely separate your frontend codebase from your backend, a lot of times that PITA is a big enough barrier to prevent people from doing it, thus private vs public config. But there's two things I do to prevent private config being loaded in the frontend:

  1. I have a unit test that ensures my frontend bundles don't contain one of the secret keys I have in the private config.
  2. I have my frontend code in a different folder than my backend code, and I have two different files named "config.js" - one for each end. For backend, config.js loads the private config, for frontend, it loads the public config. Then you always just require('config') and don't worry about where it comes from.

One last thing: your configuration should be loaded into the browser via a completely separate file than any of your other frontend code. If you bundle your frontend code, the public configuration should be built as a completely separate bundle. Otherwise, your config isn't really config anymore - its just part of your code. Config needs to be able to be different on different machines.

Womanly answered 2/3, 2017 at 1:59 Comment(3)
I like this answer but I still haven't found anyone who has had my problem. I have an app that uses the Google Calendar API, I am using Travis CI and the tests need to also test the calendar's functionality. To use it, however, I need to have a credentials.json file in my project, this is definitely not in VC. So my question, how do I provide this file to Travis' build process and let it persist to production?Entophyte
@DevYego use environment variables, that your ci stores securely.Tatiana
I'd suggest putting any credentials in any format in the keys/ directory I suggested.Womanly
S
12

You can use Konfig for environment specific config files. It loads json or yaml config files automatically, it has default value and dynamic configuration features.

An example from Konfig repo:

File: config/app.json
----------------------------
{
    "default": {
        "port": 3000,
        "cache_assets": true,
        "secret_key": "7EHDWHD9W9UW9FBFB949394BWYFG8WE78F"
    },

    "development": {
        "cache_assets": false
    },

    "test": {
        "port": 3001
    },

    "staging": {
        "port": #{process.env.PORT},
        "secret_key": "3F8RRJR30UHERGUH8UERHGIUERHG3987GH8"
    },

    "production": {
        "port": #{process.env.PORT},
        "secret_key": "3F8RRJR30UHERGUH8UERHGIUERHG3987GH8"
    }
}

In development:

> config.app.port
3000

In production, assume we start application with $ NODE_ENV=production PORT=4567 node app.js

> config.app.port
4567

More details : https://github.com/vngrs/konfig

Sorgo answered 3/2, 2014 at 12:20 Comment(0)
A
10

I will create a folder as config a file naming as config.js and later I will use this file wherever required as below

Example of config.js

module.exports = {
    proxyURL: 'http://url:port',
    TWITTER: {
        consumerkey: 'yourconsumerkey',
        consumerSecrete: 'yourconsumersecrete'
    },
    GOOGLE: {
        consumerkey: 'yourconsumerkey',
        consumerSecrete: 'yourconsumersecrete'
    },
    FACEBOOK: {
        consumerkey: 'yourconsumerkey',
        consumerSecrete: 'yourconsumersecrete'
    }
}

Then if i want to use this config file somewhere

I will first import as below

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

and I can access the values as below

const oauth = OAuth({
    consumer: {
        key: config.TWITTER.consumerkey,
        secret: config.TWITTER.consumerSecrete
    },
    signature_method: 'HMAC-SHA1',
    hash_function(base_string, key) {
        return crypto.createHmac('sha1', key).update(base_string).digest('base64');
    }
});
Assortment answered 25/1, 2018 at 4:12 Comment(0)
G
9

Just use npm module config (more than 300000 downloads)

https://www.npmjs.com/package/config

Node-config organizes hierarchical configurations for your app deployments.

It lets you define a set of default parameters, and extend them for different deployment environments (development, qa, staging, production, etc.).

$ npm install config
$ mkdir config
$ vi config/default.json


{
      // Customer module configs
      "Customer": {
        "dbConfig": {
          "host": "localhost",
          "port": 5984,
          "dbName": "customers"
        },
        "credit": {
          "initialLimit": 100,
          // Set low for development
          "initialDays": 1
        }
      }
}



$ vi config/production.json

{
  "Customer": {
    "dbConfig": {
      "host": "prod-db-server"
    },
    "credit": {
      "initialDays": 30
    }
  }
}



$ vi index.js

var config = require('config');
//...
var dbConfig = config.get('Customer.dbConfig');
db.connect(dbConfig, ...);

if (config.has('optionalFeature.detail')) {
  var detail = config.get('optionalFeature.detail');
  //...
}


$ export NODE_ENV=production
$ node index.js
Gauzy answered 30/8, 2018 at 9:27 Comment(0)
C
8

A bit late (just 10 year) but I use a config.js structured like this:

const env = process.env.NODE_ENV || 'development';

var config_temp = {
    default:{
        port: 3000,
        mysql_host: "localhost",
        logging_level: 5,
        secret_api_key: process.env.SECRET_API_KEY
    },
    development: {
        logging_level: 10
    },
    production: {
        port: 3001,
        mysql_host: "not-localhost"
    }
};
var config = {
    ...config_temp.default, 
    ...config_temp[env]
}
module.exports = config;

and I load the config with:

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

In this way:

  • The reading of the env variable is included in the config.js file so I can avoid this ugliness: require('./config')[process.env.NODE_ENV || 'development'].
  • The file config.js can be uploaded in the code 's repo because sensitive variables continue to be handled with process.env.
  • If the same element is contained in both default:{ and custom_env:{ only the second is kept.
  • There are no dedicated folders and multiple files (like in config)
Childish answered 22/3, 2021 at 16:18 Comment(3)
In this approach, we have to restart the server every time we change config or add values in config. Is there an approach where we do not have to restart the server?Obsequious
@ADITYAKUMAR save to db, load from an api etc. properties are meant to be loaded on startupHypochondria
That would be slowObsequious
T
4

I know this is a really old post. But I want to share my module for configuring environment variables, I think it is very flexible solution. Here is the module json-configurator

var configJson = {
  'baseUrl': 'http://test.com',
  '$prod_baseUrl': 'https://prod.com',
  'endpoints': {
    'users': '<%= baseUrl %>/users',
    'accounts': '<%= baseUrl %>/accounts'
    },
  foo: 'bar',
  foobar: 'foobar',
  $prod_foo: 'foo in prod',
  $test_foo: 'foo in test',
  deep:{
    veryDeep: {
      publicKey: 'abc',
      secret: 'secret',
      $prod_secret: 'super secret'
    }
  }
};

var config = require('json-configurator')(configJson, 'prod');

console.log(config.deep.veryDeep.secret) 
// super secret 

console.log(config.endpoints.users)
// https://prod.com/users 

Then you can use process.env.NODE_ENV to get all the variables for your environment.

Trichomoniasis answered 12/11, 2015 at 9:22 Comment(0)
S
4

It's better to separate 'development' and 'production' configs.

I use following way: Here is my config/index.js file:

const config = {
    dev : {
        ip_address : '0.0.0.0',
        port : 8080,
        mongo :{
            url : "mongodb://localhost:27017/story_box_dev",
            options : ""
        }
    },
    prod : {
        ip_address : '0.0.0.0',
        port : 3000,
        mongo :{
            url : "mongodb://localhost:27017/story_box_prod",
            options : ""
        }
    }
} 

For require the config use following:

const config = require('../config')[process.env.NODE_ENV];

Than you can use your config object:

const ip_address = config.ip_address;
const port = config.port;
Stitching answered 18/8, 2018 at 10:24 Comment(1)
also you can user module.exports = config; at the end of the config/index.js fileRouault
Z
3

I am a bit late in the game, but I couldn't find what I needed here- or anywhere else - so I wrote something myself.

My requirements for a configuration mechanism are the following:

  1. Support front-end. What is the point if the front-end cannot use the configuration?
  2. Support settings-overrides.js - which looks the same but allows overrides of configuration at settings.js. The idea here is to modify configuration easily without changing the code. I find it useful for saas.

Even though I care less about supporting environments - the will explain how to add it easily to my solution

var publicConfiguration = {
    "title" : "Hello World"
    "demoAuthToken" : undefined, 
    "demoUserId" : undefined, 
    "errorEmail" : null // if null we will not send emails on errors. 

};

var privateConfiguration = {
    "port":9040,
    "adminAuthToken":undefined,
    "adminUserId":undefined
}

var meConf = null;
try{
    meConf = require("../conf/dev/meConf");
}catch( e ) { console.log("meConf does not exist. ignoring.. ")}




var publicConfigurationInitialized = false;
var privateConfigurationInitialized = false;

function getPublicConfiguration(){
    if (!publicConfigurationInitialized) {
        publicConfigurationInitialized = true;
        if (meConf != null) {
            for (var i in publicConfiguration) {
                if (meConf.hasOwnProperty(i)) {
                    publicConfiguration[i] = meConf[i];
                }
            }
        }
    }
    return publicConfiguration;
}


function getPrivateConfiguration(){
    if ( !privateConfigurationInitialized ) {
        privateConfigurationInitialized = true;

        var pubConf = getPublicConfiguration();

        if ( pubConf != null ){
            for ( var j in pubConf ){
                privateConfiguration[j] = pubConf[j];
            }
        }
        if ( meConf != null ){
              for ( var i in meConf ){
                  privateConfiguration[i] = meConf[i];
              }
        }
    }
    return privateConfiguration;

}


exports.sendPublicConfiguration = function( req, res ){
    var name = req.param("name") || "conf";

    res.send( "window." + name + " = " + JSON.stringify(getPublicConfiguration()) + ";");
};


var prConf = getPrivateConfiguration();
if ( prConf != null ){
    for ( var i in prConf ){
        if ( prConf[i] === undefined ){

            throw new Error("undefined configuration [" + i + "]");
        }
        exports[i] = prConf[i];
    }
}


return exports;

Explanation

  • undefined means this property is required
  • null means it is optional
  • meConf - currently the code is target to a file under app. meConf is the overrides files which is targeted to conf/dev - which is ignored by my vcs.
  • publicConfiguration - will be visible from front-end and back-end.
  • privateConfiguration - will be visible from back-end only.
  • sendPublicConfiguration - a route that will expose the public configuration and assign it to a global variable. For example the code below will expose the public configuration as global variable myConf in the front-end. By default it will use the global variable name conf.

    app.get("/backend/conf", require("conf").sendPublicConfiguration);

Logic of overrides

  • privateConfiguration is merged with publicConfiguration and then meConf.
  • publicConfiguration checks each key if it has an override, and uses that override. This way we are not exposing anything private.

Adding environment support

Even though I don't find an "environment support" useful, maybe someone will.

To add environment support you need to change the meConf require statement to something like this (pseudocode)

if ( environment == "production" ) { meConf = require("../conf/dev/meConf").production; }

if ( environment == "development" ) { meConf = require("../conf/dev/meConf").development; }

Similarly you can have a file per environment

 meConf.development.js
 meConf.production.js

and import the right one. The rest of the logic stays the same.

Zhang answered 1/10, 2013 at 9:20 Comment(2)
not terribly obvious that undefined really means 'required' and null means 'optional'. so the yellow bin is for plastics and the blue for scrap paper? fine, but had to read the manual before tossing that litter.Darien
You don't have to use this convention. I find it useful and I instruct my team to use it, but you can obviously remove this feature.Zhang
S
3

an alt example I just used because I wanted more flexibility than a typical .json file but didn't want it abstracted away into a library which would require a dependency is something like this. Basically, exporting a function invoked immediately which returned an object with values I wanted set. Gives a lot of flexibility.

     module.exports = function(){
       switch(node_env){
         case 'dev':
           return
           { var1 = 'development'};
         }
    }();

There is a much better explanation with full example here. Using Config Files in Node.js

Sorcery answered 20/10, 2014 at 5:22 Comment(0)
M
3

Here is a neat approach inspired by this article. It does not require any additional packages except the ubiquitous lodash package. Moreover, it lets you manage nested defaults with environment-specific overwrites.

First, create a config folder in the package root path that looks like this

package
  |_config
      |_ index.js
      |_ defaults.json
      |_ development.json
      |_ test.json
      |_ production.json

here is the index.js file

const _ = require("lodash");
const defaults = require("./defaults.json");
const envConf = require("./" + (process.env.NODE_ENV || "development") + ".json" );
module.exports = _.defaultsDeep(envConf, defaults);

Now let's assume we have a defaults.json like so

{
  "confKey1": "value1",
  "confKey2": {
    "confKey3": "value3",
    "confKey4": "value4"
  }
}

and development.json like so

{
  "confKey2": {
    "confKey3": "value10",
  }
}

if you do config = require('./config') here is what you will get

{
  "confKey1": "value1",
  "confKey2": {
    "confKey3": "value10",
    "confKey4": "value4"
  }
}

Notice that you get all the default values except for those defined in environment-specific files. So you can manage a config hierarchy. Using defaultsDeep makes sure that you can even have nested defaults.

Marchant answered 13/2, 2020 at 14:7 Comment(0)
K
3
npm i config

In config/default.json
{
    "app": {
        "port": 3000
    },
    "db": {
        "port": 27017,
        "name": "dev_db_name"
    }
}

In config/production.json
{
    "app": {
        "port": 4000
    },
    "db": {
        "port": 27000,
        "name": "prod_db_name"
    }
}

In index.js

const config = require('config');

let appPort = config.get('app.port');
console.log(`Application port: ${appPort}`);

let dbPort = config.get('db.port');
console.log(`Database port: ${dbPort}`);

let dbName = config.get('db.name');
console.log(`Database name: ${dbName}`);

console.log('NODE_ENV: ' + config.util.getEnv('NODE_ENV'));

$ node index.js
Application port: 3000
Database port: 27017
Database name: dev_db_name
NODE_ENV: development

For production
$ set NODE_ENV=production
$ node index.js
Application port: 4000
Database port: 27000
Database name: prod_db_name
NODE_ENV: production
Kati answered 20/8, 2020 at 8:37 Comment(2)
And if I want to change database from ENV_VAR as per 12factorapp requirements, how can I do that?Trembly
I mean "database port" for exampleTrembly
D
2

In addition to the nconf module mentioned in this answer, and node-config mentioned in this answer, there are also node-iniparser and IniReader, which appear to be simpler .ini configuration file parsers.

Derris answered 9/4, 2013 at 0:56 Comment(1)
no way to go back to win-ini files... that iniparser proudly stresses the fact they know how to parse sections in the config ... in 2013 ... if you need deeper nesting do you say [foo/bar]? [foo\bar]? bar.baz=42? bar/baz=42? bar\baz=42? bar:baz=42? how do you tell 42 is a number? it could be an all-digits text!—toss XML, toss YAML, toss WIN.INI, embrace JSON, worries gone.Darien
L
1

I just recently released a small module to load any type of configuration files. It's pretty straight-forward, you can check it at https://github.com/flesler/config-node

Leeland answered 26/3, 2014 at 19:32 Comment(0)
W
1

You can use pconf: https://www.npmjs.com/package/pconf

Example:

var Config = require("pconf");
var testConfig = new Config("testConfig");
testConfig.onload = function(){

  testConfig.setValue("test", 1);
  testConfig.getValue("test");
  //testConfig.saveConfig(); Not needed

}
Warnock answered 16/10, 2016 at 9:12 Comment(0)
M
1

I used Dotenv-Flow for configuration management.

This is working as expected. It's very often that you have multiple environments like local, dev, staging, and production. Just flow these steps to create your own environments.

1. npm i dotenv-flow.

2. Create files like .env | .env.dev | .env.prod.

For testing purposes copy this content

.env

DATABASE_HOST=global
DATABASE_PORT=global
DATABASE_USER=global
DATABASE_PASS=global
DATABASE_NAME=global

.env.dev

DATABASE_NAME=dev
DATABASE_PASS=dev

.env.prod

DATABASE_NAME=prod
DATABASE_PASS=prod

Now create a test file use these environment variables.

test.js

console.log('database host:', process.env.DATABASE_HOST);
console.log('database port:', process.env.DATABASE_PORT);
console.log('database user:', process.env.DATABASE_USER);
console.log('database pass:', process.env.DATABASE_PASS);
console.log('database name:', process.env.DATABASE_NAME);

Now use these commands to run your script.

node -r dotenv-flow/config test.js
node -r dotenv-flow/config test.js --node-env=dev
node -r dotenv-flow/config test.js --node-env=prod

If you create these environment variables files in a specific folder like in my case I have created these files in envs folder then use the below command.

node -r dotenv-flow/config test.js --dotenv-flow-path=./envs
node -r dotenv-flow/config test.js --dotenv-flow-path=./envs --node-env=dev
node -r dotenv-flow/config test.js --dotenv-flow-path=./envs --node-env=prod
Mathura answered 25/9, 2021 at 23:47 Comment(0)
A
0

For those who are visiting this old thread here is a package I find to be good.

https://www.npmjs.org/package/config

Autism answered 14/10, 2014 at 13:1 Comment(0)
E
0

I tryied some of suggested solutions here, but was not sattisfied with them, so I created my own module. It is called mikro-config and the main difference is that it honors convention over configuration, so you can just require the module and start using it.

You store your configuration in either plain js, or json files from /config folder. First it loads default.js file, then all other files from /config directory, then it loads environment specific configuration based on $NODE_ENV variable.

It also allows to override this configuration for local development with local.js or environment specific /config/env/$NODE_ENV.local.js.

You can take at look at it here:

https://www.npmjs.com/package/mikro-config

https://github.com/B4nan/mikro-config

Erzurum answered 17/4, 2017 at 10:19 Comment(0)
F
0

For long, I used to use the approach mentioned in the solution here. There is a concern however, about security of the secrets in clear text. You can use another package on top of config so that the security bits are taken care of.

Check this out: https://www.attosol.com/secure-application-secrets-using-masterkey-in-azure-key-vault/

Flavorous answered 13/6, 2017 at 10:43 Comment(2)
Why should I even subscribe to Azure to pay for this service? Why not using ansible-vault? Another thing: I think no one will post a config file with clear text credentials on source repository. Either use environment variables or put it your secret data on a file with read only permission.Carbonyl
If you can read it from some 3rd party location and decode it and have your service use that top secret data, it's going to be possible for a hacker to do exactly the same if they gain access to your computer. It's more work (takes longer) but in the end it does not protect you. If your server is penetrated, imagine that anything you have on it is now public.Macassar
C
0

How we do it with TypeScript.

export const loadConfig = () => {
    const configLoadeded = configLoader.util.toObject() as any
    Config = configLoadeded
}

export interface ConfigI {
    productName: string;
    productId: string;
    googleCloudApiKey: string;
}
Chlorophyll answered 19/3, 2021 at 13:29 Comment(0)
G
0

These days, when working with databases it is the easiest not to deal with configuration files at all, because deployment environments are easier to set up with just a single environment variable, call it DB_CONNECTION, for example, and pass it any additional configuration data as required.

configuration data example:

const config = {
    userIds: [1, 2, 3],
    serviceLimit: 100,
    // etc., configuration data of any complexity    
};
// or you can read it from a config file

Create a connection string, with extra parameters that the database driver doesn't care about:

import {ConnectionString} from 'connection-string';

const cs = new ConnectionString('postgres://localhost@dbname', {
    user: 'user-name',
    password: 'my-password',
    params: {
        config
    }  ​
});

Then we can generate the resulting string for storing it in the environment:

cs.toString();
//=>postgres://localhost:my-password@dbname?config=%7B%22userIds%22%3A%5B1%2C2%2C3%5D%2C%22serviceLimit%22%3A100%7D

So you store this in your environment, let's say, DB_CONNECTION, and within the client process you can just read it via process.env.DB_CONNECTION:

const cs = new ConnectionString(process.env.DB_CONNECTION);

const config = JSON.parse(cs.params?.config); // parse extra configuration
//=> { userIds: [ 1, 2, 3 ], serviceLimit: 100 }

This way you will have both the connection, and all the extra configuration needed, all within a single environment variable, no need messing with configuration files.

Grindlay answered 22/5, 2021 at 16:2 Comment(0)
M
0

Try with properties-gen https://www.npmjs.com/package/properties-gen

Pretty similar to node-config which loads a configuration base file and extends it with an ext file depending on the environment you set, but this is a cli that runs on demand and is fully configurable.

npx properties-gen init

To create the cli configuration, then install it in your project

npm install properties-gen --save-dev

Define your configuration files (base and extensions) and run the generate command before build process or before starting dev server in your project.

{
  "name": "myApp",
  "scripts": {
    "config": "properties-gen generate",
    "dev": "npm run config && next dev",
    "build": "npm run config && next build",
    "start": "next start"
  }
}

One cool thing is you can define multiple configuration groups, in case you need to generate multiple outputs for example a client and server specific files.

Mise answered 30/11, 2022 at 6:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.