I have a public git[hub] project, and am now ready to switch it from development to production. We are in the research field, so we like to share our code too!
I have a server.js file that we start with node server.js like most tutorials.
In it, there is connection information for the SQL server, and the location of the HTTPS certificates. It looks something like this:
var https = require('https');
var express = require('express');
var ... = require('...');
var fs = require('fs');
var app = express();
var Sequelize = require('sequelize'),
// ('database', 'username', 'password');
sequelize = new Sequelize('db', 'uname', 'pwd', {
logging: function () {},
dialect: 'mysql',
…
});
…
var secureServer = https.createServer({
key: fs.readFileSync('./location/to/server.key'),
cert: fs.readFileSync('./location/to/server.crt'),
ca: fs.readFileSync('./location/to/ca.crt'),
requestCert: true,
rejectUnauthorized: false
}, app).listen('8443', function() {
var port = secureServer.address().port;
console.log('Secure Express server listening at localhost:%s', port);
});
In PHP you can have the connection information in another file, then import the files (and therefore variables) into scope to use. Is this possible for the SQL connection (db, uname, pwd) and the file locations of the certs (just to be safe) so that we can commit the server.js file to git and ignore/not follow the secret file?
You can do this in a lot of different ways. One would be to use environment variables like MYSQL_USER=foo MYSQL_PASSWD=bar node server.js and then use process.env.MYSQL_USER in the code.
You can also read from files as you have suggested. You can do require("config.json") and node will automatically parse and import the JSON as JavaScript constructs. You can then .gitignore config.json and perhaps provide an example.config.json.
If you want to support both of these at once there is at least one library that allows you to do this simply: nconf.
You can always just store the configuration information in a JSON file. Node natively supports JSON files. You can simply require it:
var conf = require('myconfig.json');
var key = fs.readFileSync(conf.ssl_keyfile);
There are also 3rd party libraries for managing JSON config files that add various features. I personally like config.json because it allows you to publish a sample config file with empty values then, without modifying the sample config file, you can override those values using a .local.json file. It makes it easier to deal with config files in repos and also makes it easier to publish changes to the config file.
Here is great writeup about how you should organise your deployments
Basically all application critical variables like db password, secret keys, etc., should be accessible via environment variables.
You could do something like this
// config.js
const _ = require('lodash');
const env = process.env.NODE_ENV || 'development';
const config = {
default: {
mysql: {
poolSize: 5,
},
},
development: {
mysql: {
url: 'mysql://localhost/database',
},
},
production: {
mysql: {
url: process.env.DB_URI,
},
},
};
module.exports = _.default(config.default, config[env]);
// app.js
const config = require('./config');
// ....
const sequelize = new Sequelize(config.mysql.url);
Code is not perfect, but should be enough to get the idea.
Related
Looking for elegant and simple solution to have "local configuration override" files.
The idea is to be able to have local configuration that will not ask to be added to git repository every time.
For that I need to include local.config.js if it exists.
I have global app configuration in config.js with configuration like
export const config = {
API_URL="https://some.host",
}
and config.local.js
export const config = {
API_URL="https://other.address",
}
there's .gitignore:
config.local.js
Difficulty:
I do not want to add a node module to project just for this one thing. I believe there should be an elegant way to do this in one or few lines, but have not found any so far.
Things that I tried:
1.
try {
const {
apiUrl: API_URL,
} = require('./config.local.js');
config. API_URL =apiUrl;
} catch (e) {
}
require does not work inside try{} block.
2.
const requireCustomFile = require.context('./', false, /config.local.js$/);
requireCustomFile.keys().forEach(fileName => {
requireCustomFile(fileName);
});
does not work.
3.
export const config = require('./config.local.js') || {default:'config = {...}'}
does not work.
4.
Using .env and settings environment variable: I need to override whole array of configuration values. Not one by one.
This solution uses process.argv. It is native to node as documented here and does not use .env
It inspects the command values used to start the app. Since these should be different between your local and production environments, it's an easy way to switch with no additional modules required.
command prompt to start your node app:
(this might also be in package.json and incurred via npm start if you're using that approach.)
$ node index.js local
index.js of your node app:
var express = require('express');
var config = require('./config');
if (process.argv[2] === 'local') {
// the 3rd argument provided at startup (2nd index) was 'local', so here we are!
config = require('./config_local');
}
var app = express();
// rest of owl…
I'm trying to find out there the best solution about how to store environment vars for Node using Express. Many tutorials suggest to move the data into a .json file, here is an example but the don't config environment vars, they define new vars such as:
{
"test": {
"facebook_app_id": "facebook_dummy_dev_app_id",
"facebook_app_secret": "facebook_dummy_dev_app_secret",
},
"development": {
"facebook_app_id": "facebook_dummy_dev_app_id",
"facebook_app_secret": "facebook_dummy_dev_app_secret",
},
"production": {
"facebook_app_id": "facebook_dummy_prod_app_id",
"facebook_app_secret": "facebook_dummy_prod_app_secret",
}
}
The problem I've find to this approach is that I can't define vars in it, because all elements must be inside double quotes ( " ) so I can't do:
{
process.env.PORT = 3000,
process.env.NODE_APP = XXX
....
}
I've tried to do some tricky things like defining a .js file and store there the vars, something like this:
module.exports = {
env: {
USER:"www-data",
NODE_ENV:"development",
PORT:"3002",
....
LOG_FILE:"error.log"
}
}
In this case, I have not found the way to use those vars inside env, something like:
module.exports = {
env: {
...
process.env.APP_NAME:"application.name",
process.env.NODEJS_DIR:"/var/nodejs",
process.env.APP_DIR: `${this.NODEJS_DIR}/${this.APP_NAME}/current`, //Not working
process.env.APP_DIR:process.env.NODEJS_DIR}/${this.APP_NAME}/current`, //Not working, and verbose, ugly code
....
}
}
Is there a better way to solve this?
Thanks
You can use dotenv module to achieve this.
So in your root directory, create 2 files (depending on how many different environments you might have) named development.env and production.env
In your app, do this:
var env = process.env.NODE_ENV || 'development';
require('dotenv').config({
path: './' + env + '.env'
});
Then you could access all the environment variables you have defined in the respective env file.
Something like this you are looking for.
app.configure('production', function(){
// production env. specific settings
});
As another example of forming the config file
// config file in /config/general.js
var path = require('path'),
rootPath = path.normalize(__dirname + '/..'),
env = process.env.NODE_ENV || 'development';
if (env == 'development') {
process.env.DEBUG = 'http';
}
var config = {
development: {
name : 'development',
root: rootPath,
port: 3000
},
production: {
name : 'prod',
root: rootPath,
port: 3000
}
};
module.exports = config[env];
Think about the dev-ops team, how they will deploy your software? They won't touch the code, and the configuration file should not contain any logic. So the .js file containing configurations is wrong. You have to provide them an interface to configure the execution. That may be a parameter, or if the configuration is complex, then it should be extracted to a configuration file.
My approach is to load app.json by default. If the dev-ops engeneer provides NODE_ENV=customenv, then load app.customenv.json configuration file. This way the .json file won't expose non-relevant development configurations to the users.
APP_NAME is not part of the environment, but part of your software configuration. This parameter should be configured in the .json configuration file.
Directories can be relevant to the software directory, it defined in the __dirname variable.
The app.json file should be loaded by a configuration module. You can use one of the open-source projects available, for example nconf.
How can I export these variables so I can later use them on different js files?
The following example works well with just 1 variable
var app = module.exports = express();
But I want to pass more variables so I did this
var app = express();
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : ''
});
module.exports.app = app;
module.exports.connection = connection;
with no success
This is because on the first, your module is the app. I mean that if you have B like that in the first case:
app = require('A') // = express()
whereas on the second time it is :
app = require('A') // = {app: express(), connection: connection}
The answer is in the Node.js Modules documentation:
You either assign the value that you want to export to the module.exports property, or you assign an object to it.
mymodule.js:
var app = module.exports = express();
Or:
var app = express();
module.exports = {
"app": app,
"otherproperties": "you want to export"
}
To require a module that you made yourself and didn't place node_modules directory, you can either supply an absolute path or a relative path.
Calling module:
var app = require("/home/user/myapp/mymodule.js"); // absolute path
app; // access returned value of express() function, created in *mymodule.js*
Or:
var app = require("./mymodule.js"); // path relative to the calling module
// In this case the calling module is in the same directory as *mymodule.js*
app.app; // access returned value of express() function, created in *mymodule.js*
Addendum: Even though the modules library/module has been locked, I really recommend reading the documentation. It's very much possible to read the entire documentation in two evenings while looking up network terminology that you're not familiar with. It'll save you a lot of time in the short term!
I'm testing my NodeJS application with supertest. At the end of my app.js I expose my app, so I can use it within the test.js.
///////////////////
// https options
var options = {
key: fs.readFileSync("./private/keys/Server.key"),
cert: fs.readFileSync("./private/certs/Server.crt"),
ca: fs.readFileSync("./private/ca/CA.crt"),
requestCert: true,
rejectUnauthorized: false
};
///////////////////
// start https server
var server = https.createServer(options, app).listen(app.get("port"), function(){
console.log('Server listening on port ' + app.get('port'));
});
exports = module.exports = server;
When I require my server in my test.js the path of options will cause an error, since my test.js is in the directory ./test/test.js, whereas app.js is in ./app.js.
I'm struggeling to find a clean solution for this problem.
If you make your options into a module, you can either use it directly or stub it in your test using something like rewire or sandboxed-module
Alternatively you could modify your exports to accept the options as a parameter so that you require it with e.g. require('server')(config)
hi am new to nodejs environment.
am using nodeJs + compoundjs.
am having three database environment development. production and test. my question in when i run the NODE_ENV=production node . command, all url's,port number and other things should get from production.js. when i shift the node environment by giving command NODE_ENV=development node . all things need to run should get from development.js.
any notes for this also helpful for me.
if anybody has any idea please share with me.
You have to set the Environment and then you can configure your app like:
(This is a mongoose db and express, but you can find similar configurations.)
Simply set up three environment configurations
app.configure('development', function () {
mongoose.connect(devConfig.db.path, function onMongooseError(err) {
});
});
app.configure('production', function () {
mongoose.connect(proConfig.db.path, function onMongooseError(err) {
});
});
a configuration example (config.js) :
var config = {};
// Database (MongoDB) configurations
config.db = {
path : 'mongodb://localhost/sampleDatabase'
};
module.exports = config;
I require this file in my app.js by var config = require('config')
You could do the Environment detection in the config file as well.