103

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?

nbro
  • 15,395
  • 32
  • 113
  • 196
Eric the Red
  • 5,364
  • 11
  • 49
  • 63
  • 3
    There have been people breaking them up into routes. Also you can take a look at express-resources. – BRampersad Oct 11 '11 at 20:52
  • @Brandon_R have you tried resources? I glanced at it and thought it looked neat, just haven't kicked the tires yet. – Chance Oct 11 '11 at 21:29
  • 1
    A little late, but I recently open sourced a router for express which allows you to break up the app.js nicely intro controllers+views etc. See: https://github.com/kishorenc/road – jeffreyveon Nov 29 '11 at 16:44

4 Answers4

83

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)


EDIT 4

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).


EDIT 3

If you are a fan of CoffeeScript (I am not) and reeeeaaaaaally want the L&F of Rails, there is also Tower.js.


EDIT 2

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.


EDIT 1

nodejs-express-mongoose-demo is very similar to how I have mine structured. Check it out.

Community
  • 1
  • 1
Chance
  • 11,043
  • 8
  • 61
  • 84
  • 2
    Where does business logic go? Do you ever use helpers for things like authentication? – Eric the Red Oct 11 '11 at 21:31
  • @ErictheRed if you are familiar with the MVC Pattern (rails, Asp.Net mvc, etc) then I consider my Routes to be my controllers and everything kind of falls into place after that. Business logic goes in the models (although I am having difficulties with validation and mongoose). For helpers, I use Exports on a simple internal util library I'm putting together for myself for things I reuse. – Chance Oct 11 '11 at 22:17
  • It would be cool to upload a sample setup to github for us to look at. What goes in Routes folder/files? – chovy Oct 11 '11 at 23:06
  • 1
    @chovy I added a link to https://github.com/qed42/nodejs-express-mongoose-demo that has a very similar structure – Chance Oct 12 '11 at 03:01
  • I recommend avoiding any bloated frameworks build on top of express – Raynos Apr 08 '12 at 15:36
  • @Raynos I don't think Locomotive is bloated. In fact, it does very little. The only hesitation I have with it is I'm just not certain the pattern is the best approach for Node. We are working on an internal framework, built on Express, that we are considering releasing sometime in the near future after we have all the kinks worked out. – Chance Apr 08 '12 at 16:49
  • The problem is express is already a bloated framework. Express has too much complexity, adding anything on top is madness. And for the love of god don't recommend tower, it's a piece of crap coffeescript framework – Raynos Apr 08 '12 at 16:57
  • I disagree with you that Express is bloated. I personally think that expecting any sort of structure in a web framework is not conducive to growth. Not everyone wants to rewrite from the ground up. – Chance Apr 08 '12 at 20:18
  • @Chance What are your thoughts on a service layer? – theblang Oct 01 '13 at 18:05
9

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

  • I have a chunk that sets up all my remote OAuth authentication using everyauth.
  • I have a chunk that configures my application (basically calling app.configure)
  • I have a little bit of code that punches less so it re-compiles any of my less into css at run time.
  • I have code that sets up all my routes
  • I call this small nko module
  • Finally I start the server by listening to a port.

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.

Raynos
  • 166,823
  • 56
  • 351
  • 396
1

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.

1

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!

JKleinne
  • 1,270
  • 7
  • 14