Is there a common convention for breaking up and modularizing the app.js
file in an Express.js application? Or is it common to keep everything in a single file?
Middleware frameworks, like Express. js, are suitable for small and medium projects. If you are going to develop a large project that will be supported by a large team of developers, Express. js is not the best choice.
It's fast, unopinionated, and has a large community behind it. It is easy to learn and also has a lot of modules and middleware available for use. Express is used by big names like Accenture, IBM, and Uber, which means it's also great in a production environment.
I have mine broken up as follows:
~/app |~controllers | |-monkey.js | |-zoo.js |~models | |-monkey.js | |-zoo.js |~views | |~zoos | |-new.jade | |-_form.jade |~test | |~controllers | |-zoo.js | |~models | |-zoo.js |-index.js
I use Exports to return what's relevant. For instance, in the models I do:
module.exports = mongoose.model('PhoneNumber', PhoneNumberSchema);
and then if I need to create a phone number, it's as simple as:
var PhoneNumber = require('../models/phoneNumber'); var phoneNumber = new PhoneNumber();
if I need to use the schema, then PhoneNumber.schema
(which assumes that we are working from the routes folder and need to go 1 level up and then down to models)
The express wiki has a list of frameworks built on top of it.
Of those, I think Twitter's matador is structured pretty well. We actually used a very similar approach to how they load up parts of the app.
derby.js also looks extremely interesting. It's akin to meteor without all of the hype and actually gives credit where credit is due (notably, node and express).
If you are a fan of CoffeeScript (I am not) and reeeeaaaaaally want the L&F of Rails, there is also Tower.js.
If you are familiar with Rails and don't mind the bleed-over of some concepts there is Locomotive. It is a light-weight framework built on Express. It has a very similar structure as RoR and carries over some of the more rudimentary concepts (such as routing).
It's worth checking out even if you don't plan to use it.
nodejs-express-mongoose-demo is very similar to how I have mine structured. Check it out.
Warning: referencing code I hacked together for node knockout, it kind of works but is far from elegant or polished.
To be more specific about splitting up app.js
I have the following app.js file
var express = require('express'), bootstrap = require('./init/bootstrap.js'), app = module.exports = express.createServer(); bootstrap(app);
This basically means I place all my bootstrapping in a seperate file, then I bootstrap the server.
So what does bootstrap do?
var configure = require("./app-configure.js"), less = require("./watch-less.js"), everyauth = require("./config-everyauth.js"), routes = require("./start-routes.js"), tools = require("buffertools"), nko = require("nko"), sessionStore = new (require("express").session.MemoryStore)() module.exports = function(app) { everyauth(app); configure(app, sessionStore); less(); routes(app, sessionStore); nko('/9Ehs3Dwu0bSByCS'); app.listen(process.env.PORT); console.log("server listening on port xxxx"); };
Well it splits all the server initialization setup in nice chunks. Specifically
app.configure
)Just for example let's look at the routing file
var fs = require("fs"), parseCookie = require('connect').utils.parseCookie; module.exports = function(app, sessionStore) { var modelUrl = __dirname + "/../model/", models = fs.readdirSync(modelUrl), routeUrl = __dirname + "/../route/" routes = fs.readdirSync(routeUrl);
Here I load all my models and routes as arrays of files.
Disclaimer: readdirSync
is only ok when called before you start the http server (before .listen
). Calling synchronious blocking calls at server start time just makes the code more readable (it's basically a hack)
var io = require("socket.io").listen(app); io.set("authorization", function(data, accept) { if (data.headers.cookie) { data.cookie = parseCookie(data.headers.cookie); data.sessionId = data.cookie['express.sid']; sessionStore.get(data.sessionId, function(err, session) { if (err) { return accept(err.message, false); } else if (!(session && session.auth)) { return accept("not authorized", false) } data.session = session; accept(null, true); }); } else { return accept('No cookie', false); } });
Here I punch socket.io to actually use authorization rather then letting any tom and jack to talk to my socket.io server
routes.forEach(function(file) { var route = require(routeUrl + file), model = require(modelUrl + file); route(app, model, io); }); };
Here I start my routes by passing the relevant model into each route object returned from the route file.
Basically the jist is you organize everything into nice little modules and then have some bootstrapping mechanism.
My other project (my blog) has an init file with a similar structure.
Disclaimer: the blog is broken and doesn't build, I'm working on it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With