79

What would be the best way to store DB config (username, password) in an open source app that runs on node.js / Express? Two specific questions:

  1. Shall I put it into a separate config.js file in /lib folder, for example, and never include it into the master repository that is publicly available on GitHub?

  2. To inlcude the config, is it as simple as require('./config.js') from the file that needs it or is there a better way of doing it?

PS sorry if the questions seem a bit simple or not so well formulated, but I'm just starting :)

Blaszard
  • 30,954
  • 51
  • 153
  • 233
Aerodynamika
  • 7,883
  • 16
  • 78
  • 137

7 Answers7

125

Here's how I do it:

Create a config.js which contains objects representing your configs:

var config = {
development: {
    //url to be used in link generation
    url: 'http://my.site.com',
    //mongodb connection settings
    database: {
        host:   '127.0.0.1',
        port:   '27017',
        db:     'site_dev'
    },
    //server details
    server: {
        host: '127.0.0.1',
        port: '3422'
    }
},
production: {
    //url to be used in link generation
    url: 'http://my.site.com',
    //mongodb connection settings
    database: {
        host: '127.0.0.1',
        port: '27017',
        db:     'site'
    },
    //server details
    server: {
        host:   '127.0.0.1',
        port:   '3421'
    }
}
};
module.exports = config;

Then in my index.js (or wherever really),

var env = process.env.NODE_ENV || 'development';
var config = require('./config')[env];

Then process with that object, e.g.

var server = express();
server.listen(config.server.port);
...
Dejan Zelic
  • 311
  • 3
  • 10
Stephen Wright
  • 2,908
  • 4
  • 19
  • 28
  • Awesome... its really help full, it saved a lot of time. – Wcan Aug 27 '16 at 10:02
  • I've one question how can i use this same config file in angular side to generate development / production url ?? – Wcan Aug 27 '16 at 10:05
  • 7
    I like the idea of having several configs available in one file, but development config and production config just do not mix well: They should not be stored in the same file. You do not want to have the production database password stored on your development machine, that's just insane! Also: Never commit this config file to version control. – jlh Aug 26 '19 at 07:15
  • @jlh completely correct - I would never store credentials in this file - I'd either use dotenv or just straight export system variables via shell profile. – Stephen Wright Aug 30 '19 at 07:55
  • 1
    This approach has a problem, if you are using webpack it will bundle this config file into the bundle. Hence you can not replace the values for different environments. You are forced to create different bundles for different environments. – Neeraj Gulia Aug 11 '20 at 07:48
33

For running toy apps where I need to hide db credentials, I use the dotenv module.

Place your sensitive info in a .env file (which is .gitignored), place require('dotenv').config(); in your app; dotenv creates entries in process.env that you can refer to.

.env file:

DATABASE_PASSWORD=mypw
DATABASE_NAME=some_db

To refer to the values:

process.env.DATABASE_PASSWORD
Adam Azad
  • 11,171
  • 5
  • 29
  • 70
Robb Broome
  • 431
  • 4
  • 3
27

Not sure whether this is the best practice, but personally I have a config.json file where I store my db connection information. Then I do the following:

// options.js
var fs = require('fs'),
configPath = './config.json';
var parsed = JSON.parse(fs.readFileSync(configPath, 'UTF-8'));
exports.storageConfig=  parsed;

Then from a different file I do the following:

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

var loginData = {
        host: options.storageConfig.HOST,
        user: options.storageConfig.user,
        password: options.storageConfig.password
};
anvarik
  • 6,417
  • 5
  • 39
  • 53
  • 1
    In case this is helpful to anyone -- I'm storing my config files in a /config directory, and was getting `Error: ENOENT, no such file or directory './config.json'`. This solution worked for me when I changed `configPath = './config.json'` to `configPath = __dirname + '/config.json'` in `options.js`. See @loganfsmyth's comment here: http://stackoverflow.com/questions/13541948/node-js-cant-open-files-error-enoent-stat-path-to-file – ozandlb Aug 11 '14 at 07:55
  • @anvarik why don't simply have that config file to be JSON object and export it as Node module. So that you can simply require it where ever you want ? – Kamalakannan J Oct 15 '16 at 04:44
  • this reply is very old, take @Stepen answer – stackdave Aug 04 '17 at 08:41
  • 12
    Isn't it a big concern to have a file on the server containing secret credentials? If so, then this answer is unhelpful and what else should we do? Some people say we should use environment variables - but some argue that they too are dangerous because any program on the server can read them. – Morris Dec 26 '18 at 05:29
  • 1
    @Vic I had the same concern. I was looking for a NodeJS library that could decrypt an encrypted config file but when I stumbled upon this and didn't see anyone recommending this, I thought I was going crazy. Glad to see someone else was thinking the same thing. – Matthew Zackschewski Sep 18 '19 at 14:50
  • 1
    I find this the way to go, since we are having config file which is having different keys which need different values for different environments. Using webpack we are bundling the solution in single file. This way saved us. Also, we are not storing confidential information in repository - and during deployment the credentials are replaced with actual values in the json file. – Neeraj Gulia Aug 11 '20 at 07:36
3

I do put in args. just like the port of so many node.js example. you most likely forever, pm2, nodemon to run your app. so this variable is not check in as part of your source code. and they are globally available too.

process.env.PORT
process.env.DATABASE_USER
process.env.DATABASE_PASSWORD


PORT=3000 DATABASE_HOST=localhost DATABASE_USER=admin DATABASE_PASSWORD=mypassword node app.js

export PORT=3000
export DATABASE_HOST=localhost
export DATABASE_PORT=27017
export DATABASE_USER=admin
export DATABASE_PASSWORD=mypassword
node app.js

var server = app.listen(process.env.PORT, function() {
});

var mongoClient = new MongoClient(new Server(process.env.DATABASE_HOST, process.env.DATABASE_PORT));
wayne
  • 3,410
  • 19
  • 11
2

To inlcude the config, is it as simple as require('./config.js') from the file that needs it or is there a better way of doing it?

This is the right way to store config files.

The best approach would be to write your entire application like an ordinary node.js module, and write a small start-up file that calls it. This idea also allow you to use different database drivers using dependency injection.

Good, but not perfect solution is the environment. It is shared among all application, so if you have certain data you want to be available to all of them, this is the best bet. But if you have a config for one particular app, not much so.

PS: And please, don't use JSON for this. It's the worst idea possible. :)

alex
  • 11,935
  • 3
  • 30
  • 42
  • why JSON is a bad idea? – Aerodynamika Mar 12 '14 at 23:14
  • 2
    Mostly because it doesn't support comments and trailing commas. It's too long to explain in the comment, but [here](https://github.com/rlidwka/yapm/blob/master/changes/package-yaml.md#a-few-examples-why-you-should-not-use-packagejson) you can find a few "don't do that" kind of examples. – alex Mar 12 '14 at 23:16
1

I found this a nice way to handle my config, considering different environments:

config.coffee

exports.setEnvironment = (env) ->
    switch env
        when "development"
            exports.DEBUG_LOG = true
            exports.DB_PORT = '27017'
            # ...
        when "testing"
            exports.DEBUG_ERROR = true
            exports.DEBUG_CLIENT = true
            # ...
        when "production"
            exports.DEBUG_LOG = false
            # ...
        else console.log "environment #{env} not found"

server.coffee:

config = require('./config')
config.setEnvironment env
flaudre
  • 2,358
  • 1
  • 27
  • 45
1
  1. Using environment variables

You can use export to set environment variables in OSX and Linux. The following is an example of setting a value in the SESSION_SECRET key.

export SESSION_SECRET="keyboard cat"

In Windows, you can use set.

set SESSION_SECRET="keyboard cat"

You can also set environment variables each time you run them.

SESSION_SECRET="keyboard cat" node secret-env.js

Use process.env of node.js to access environmental variables within code.

var express = require('express')
var session = require('express-session')
var app = express()
app.use(session({secret: process.env.SESSION_SECRET}))
  1. Request a argument from the command-line

The best way to protect confidential information is not to store it in a setup file. If the command-line requests configuration information as an argument using the noopt package, the secret information does not need to exist as a file. The following is an example of requesting a session key as an argument using the noopt package.

var nopt = require("nopt")

var longOpts = {
  "sessionSecret": String,
}

var shortOpts = {
  "s": ["--sessionSecret"],
}

var parsed = nopt(longOpts, shortOpts, process.argv, 2)

console.log("session secret is:", parsed.sessionSecret)
node secret-arg.js --sessionSecret "keyboard cat"
node secret-arg.js -s "keyboard cat"

Advantages : It is safer to expose confidential information than to hardcoding or having it as a configuration file.

Disadvantages : There is a hassle of increasing the amount of information to be entered each time the app is launched. If you try to create and solve a script, the problem that the password still exists in the script remains.

JongSun Park
  • 31
  • 1
  • 5