I'm building out a small Node app using Express.js and, in an effort to keep my server.js file as clean as possible, I'd like to build my configuration in an external file. Here's how the server looks:
// server.js
var express = require( 'express' );
var app = express();
app.enable( 'trust proxy' );
// Set application config params
require( './config.js' )( app, express );
// Load routes and start listening...
require( './routes' )( app );
app.listen( app.get( 'port' ) );
My config.js file sets a few defaults and then updates or overrides the config in NODE_ENV-specific configuration functions. Everything would be fine except for that pesky timing.
My routes need access to some of the configuration values. Is there a way to ensure that my routes are loaded and my server starts only starts listening only after the configuration is completely loaded? Is there a better way?
I get the event loop, but I am brand-spanking new to node/express so I'm open to pretty much anything. I'm kind of making this up as I go along by cobbling together what I know I'd like to do based on experience with what I read about in various articles or documentation sources. I don't think I'm too off base here, but maybe that's being overly optimistic.
UPDATE
My config.js.
module.exports = function( app, express ) {
var config = this;
app.configure( function() {
app.set( 'port', 3000 );
app.set( 'datasources', {
'api' : {...},
'mysql' : {...}
});
app.use( express.logger() );
app.use( express.bodyParser() );
app.use( express.cookieParser() );
app.use( express.methodOverride() );
app.use( app.router );
});
// dev-specific config
app.configure( 'development', function() {
console.log( 'Loading development configuration' );
app.use( express.errorHandler({ dumpExceptions: true, showStack: true }) );
// update the mysql config with a connection object
var datasources = app.get( 'datasources' );
var mysqlConnection = require( 'mysql' ).createConnection({...});
datasources.mysql.connection = mysqlConnection;
app.set( 'datasources', datasources );
});
// stg-specific config
app.configure( 'staging', function() {
console.log( 'Loading staging configuration' );
app.use( express.errorHandler() );
// update the mysql config with a connection object
var datasources = app.get( 'datasources' );
var mysqlConnection = require( 'mysql' ).createConnection({...});
datasources.mysql.connection = mysqlConnection;
app.set( 'datasources', datasources );
});
// prd-specific config
app.configure( 'production', function() {
console.log( 'Loading production configuration' );
app.use( express.errorHandler() );
});
console.log( app.get( 'datasources' ) );
console.log( 'Configuration loaded' );
return config;
};
If the code inside your config is all synchronous this should work:
// server.js
var express = require( 'express' );
var app = express();
var config = require( './config.js' ); // load config
app.enable( 'trust proxy' );
// -----------------------------------------------
// If the code inside your config is all synchronous this call
// will block, which is what you want.
config(app, express );
// Load routes and start listening...
require( './routes' )( app );
app.listen( app.get( 'port' ) );
Assign callback to your config.js module, let make it look like
require( './config.js' )( app, express, finish );
var finish = function(){
// Load routes and start listening...
require( './routes' )( app );
app.listen( app.get( 'port' ) );
}
And in your config method, use a module like async and make all your loadings sync at the end and callback the function. Example:
**config.js**
module.exports = function(app,express,finish){
function loadConfig1(cb){
fs.readFile("....", function(){ // Or someother else async function
... if succesfull, cb(null);
... if fail, cb("error!!!!");
});
}
....
function complete(err, results){
if (!err){
finish(); // you guarantee that all the functions are completed succesfully
}
}
}
I'm currently working on an extension to Express which allows pure json external routing configuration. I plan to open source this project in the near future. If anyone is interested I'd be happy to share an early preview of this project via Github and NPM
module.exports = {
/**
* settings
*/
config:{
"controller_directory" : "routes"
},
/**
* defaults to ./routes directory
*/
filters: {
secure: "secure.securityListener",
global:"secure.globalListener"
},
/**
* defaults to ./routes directory
* order matters for filters
* filters should always contain a path which calls next();
* router should be the final destination to a request
*
* possible verbs are [use, all, get, post, delete ...]
* "use" is the default, "mount" path is stripped and is not visible to the middleware function.
* other verbs will not strip out the "mount" path
*/
routes: {
global: {"filters": ["global"]},
index:{path:"/", "controller": "index"},
users:{path:"/users", "controller": "users", "filters": ["secure"]},
prices:{path:"/prices", "controller": "prices"},
software:{path:"/software", "controller": "software"},
auth:{path:"/auth", "controller": "secure.onAuthentication", "verb": "get"}
}
};