Differences between express.Router and app.get?

How they are different

Everyone, including the documentation, tends to refer back to how much they are the same, but not actually reference any differences. Well, they are, in fact, different.

var bigApp = express();
var miniApp = express.Router();

listen()

The most obviously difference is that the bigApp will give listen, which just a rather confusing way to do what would otherwise be simple and obvious the node http or https module:

var server = require('http').createServer(bigApp);

server.listen(8080, function () {
  console.info(server.address());  
});

I consider this an anti-pattern because it abstracts and obscures away something that wasn't complicated or difficult in the first place, and then makes it difficult for people to use websockets and other middleware that require the raw http server.

Internal State

The big difference, which is really important, is that all bigApps have separate internal state.

bigApp.enable('trust proxy');
bigApp.enabled('trust proxy');
// true

var bigApp2 = express();
bigApp2.enabled('trust proxy');
// false

bigApp.use('/bunnies', bigApp2);
// WRONG! '/bunnies' will NOT trust proxies

A miniApp passed to a bigApp, however, will be operated by the bigApp in such a way that its internal state and thisness will be preserved and those routes will behave accordingly.

bigApp.enable('trust proxy');
bigApp.enabled('trust proxy');
// true

var miniApp = express.Router();

bigApp.use('/bunnies', miniApp);
// CORRECT! All state and such are preserved

This can be a big deal because express does a lot of (sometimes trixy) things to the http.ServerRequest and httpServerResponse object - such as modifying (or hijacking) req.url and req.originalUrl and various other properties you've been using without realizing - and you probably don't want that duplicated and separated.

Smaller API

There is a smaller, more well-defined number of functions a Router can use:

  • .use(mount, fn)
  • .all(mount, fn)
  • .options(mount, fn)
  • .head(mount, fn)
  • .get(mount, fn)
  • .post(mount, fn)
  • .patch(mount, fn)
  • .put(mount, fn)
  • .delete(mount, fn)
  • .route(mount).XXXX
  • .param(name, cb).XXXX

There are a few other convenience methods as well, such as basic(), but you won't find set() or enable() or other methods that change the larger app state.


app.js

var express = require('express'),
    dogs    = require('./routes/dogs'),
    cats    = require('./routes/cats'),
    birds   = require('./routes/birds');

var app = express();

app.use('/dogs',  dogs);
app.use('/cats',  cats);
app.use('/birds', birds);

app.listen(3000);

dogs.js

var express = require('express');

var router = express.Router();

router.get('/', function(req, res) {
    res.send('GET handler for /dogs route.');
});

router.post('/', function(req, res) {
    res.send('POST handler for /dogs route.');
});

module.exports = router;

When var app = express() is called, an app object is returned. Think of this as the main app.

When var router = express.Router() is called, a slightly different mini app is returned. The idea behind the mini app is that each route in your app can become quite complicated, and you'd benefit from moving all that code into a separate file. Each file's router becomes a mini app, which has a very similar structure to the main app.

In the example above, the code for the /dogs route has been moved into its own file so it doesn't clutter up the main app. The code for /cats and /birds would be structured similarly in their own files. By separating this code into three mini apps, you can work on the logic for each one in isolation, and not worry about how it will affect the other two.

If you have code (middleware) that pertains to all three routes, you can put it in the main app, before the app.use(...) calls. If you have code (middleware) that pertains to just one of those routes, you can put it in the file for that route only.


Express 4.0 comes with the new Router. As mentioned on the site:

The express.Router class can be used to create modular mountable route handlers. A Router instance is a complete middleware and routing system; for this reason it is often referred to as a “mini-app”.

There is a good article at https://scotch.io/tutorials/learn-to-use-the-new-router-in-expressjs-4 which describes the differences and what can be done with routers.

To summarize

With routers you can modularize your code more easily. You can use routers as:

  1. Basic Routes: Home, About
  2. Route Middleware to log requests to the console
  3. Route with Parameters
  4. Route Middleware for Parameters to validate specific parameters
  5. Validates a parameter passed to a certain route

Note:

The app.router object, which was removed in Express 4, has made a comeback in Express 5. In the new version, it is a just a reference to the base Express router, unlike in Express 3, where an app had to explicitly load it.


app.route('/book')
  .get(function (req, res) {
    res.send('Get a random book')
  })
  .post(function (req, res) {
    res.send('Post a random book')
  })

As in above example, we can add different HTTP request method under a route.