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 sin
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.
For maintainable routing organisation you can check out this article about the express-routescan node module and try it. This is the best solution for me.
I have my apps build on top of the express-generator tool. You can install it by running npm install express-generator -g
and run it using express <APP_NAME>
.
To give you a perspective, one of my smaller application's structure looked like this:
~/
|~bin
| |-www
|
|~config
| |-config.json
|
|~database
| |-database.js
|
|~middlewares
| |-authentication.js
| |-logger.js
|
|~models
| |-Bank.js
| |-User.js
|
|~routes
| |-index.js
| |-banks.js
| |-users.js
|
|~utilities
| |-fiat-converersion.js
|
|-app.js
|-package.json
|-package-lock.json
One cool thing I like about this structure I end up adopting for any express application I develop is the way the routes are organized. I did not like having to require each route files into the app.js and app.use()
each route, especially as the file gets bigger. As such, I found it helpful to group and centralize all my app.use()
on a ./routes/index.js file.
In the end, my app.js will look something like this:
...
const express = require('express');
const app = express();
...
require('./routes/index')(app);
and my ./routes/index.js will look something like this:
module.exports = (app) => {
app.use('/users', require('./users'));
app.use('/banks', require('./banks'));
};
I am able to simply require(./users)
because I wrote the users route using express.Router() which allows me to "group" multiple routes and then export them at once, with the goal of making the application more modular.
This is an example of what you would fine on my ./routers/users.js route:
const router = require('express').Router();
router.post('/signup', async (req, res) => {
// Signup code here
});
module.exports = router;
Hopefully this helped answer your question! Best of luck!
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.