Node.js / Express.js - How does app.router work?
Asked Answered
F

4

306

Before I ask about app.router I think I should explain at least what I think happens when working with middleware. To use middleware, the function to use is app.use(). When the middleware is being executed, it will either call the next middleware by using next() or make it so no more middleware get called. That means that the order in which I place my middleware calls is important, because some middleware depends on other middleware, and some middleware near the end might not even be called.

Today I was working on my application and had my server running in the background. I wanted to make some changes and refresh my page and see the changes immediately. Specifically, I was making changes to my layout. I couldn't get it to work so I searched Stack Overflow for the answer and found this question. It says to make sure that express.static() is beneath require('stylus'). But when I was looking at that OP's code, I saw that he had his app.router call at the very end of his middleware calls, and I tried to figure out why that was.

When I made my Express.js application (version 3.0.0rc4), I used the command express app --sessions --css stylus and in my app.js file the code came setup with my app.router above both the express.static() and require('stylus') calls. So it seems like, if it comes already setup that way, then it should stay that way.

After re-arranging my code so I could see my Stylus changes, it looks like this:

app.configure(function(){
  //app.set() calls
  //app.use() calls
  //...
  app.use(app.router);
  app.use(require('stylus').middleware(__dirname + '/public'));
  app.use(express.static(__dirname + '/public', {maxAge: 31557600000}));
});

app.get('/', routes.index);

app.get('/test', function(req, res){
  res.send('Test');
});

So I decided that the first step would be to find out why it is important to even have app.router in my code. So I commented it out, started my app and navigated to /. It displayed my index page just fine. Hmm, maybe it worked because I was exporting the routing from my routes file (routes.index). So next I navigated to /test and it displayed Test on the screen. Haha, OK, I have no idea what app.router does. Whether it is included in my code or not, my routing is fine. So I am definitely missing something.

So Here Is My Question:

Could somebody please explain what app.router does, the importance of it, and where I should place it in my middleware calls? It would also be nice if I got a brief explanation about express.static(). As far as I can tell, express.static() is a cache of my information, and if the application can't find the requested page, it will check the cache to see if it exists.

Fuller answered 2/10, 2012 at 17:53 Comment(2)
Thank you for asking this question. I have been googling around to find this answer (and the question to prompt it).Earnestineearnings
That was a really well written question, I was googling the same thing.Cortez
H
332

Note: This describes how Express worked in versions 2 and 3. See the end of this post for information about Express 4.


static simply serves files (static resources) from disk. You give it a path (sometimes called the mount point), and it serves the files in that folder.

For example, express.static('/var/www') would serve the files in that folder. So a request to your Node server for http://server/file.html would serve /var/www/file.html.

router is code that runs your routes. When you do app.get('/user', function(req, res) { ... });, it is the router that actually invokes the callback function to process the request.

The order that you pass things to app.use determines the order in which each middleware is given the opportunity to process a request. For example, if you have a file called test.html in your static folder and a route:

app.get('/test.html', function(req, res) {
    res.send('Hello from route handler');
});

Which one gets sent to a client requesting http://server/test.html? Whichever middleware is given to use first.

If you do this:

app.use(express.static(__dirname + '/public'));
app.use(app.router);

Then the file on disk is served.

If you do it the other way,

app.use(app.router);
app.use(express.static(__dirname + '/public'));

Then the route handler gets the request, and "Hello from route handler" gets sent to the browser.

Usually, you want to put the router above the static middleware so that a accidentally-named file can't override one of your routes.

Note that if you don't explicitly use the router, it is implicitly added by Express at the point you define a route (which is why your routes still worked even though you commented out app.use(app.router)).


A commenter has brought up another point about the order of static and router that I hadn't addressed: the impact on your app's overall performance.

Another reason to use router above static is to optimize performance. If you put static first, then you'll hit the hard drive on every single request to see whether or not a file exists. In a quick test, I found that this overhead amounted to ~1ms on an unloaded server. (That number is much likely to be higher under load, where requests will compete for disk access.)

With router first, a request matching a route never has to hit the disk, saving precious milliseconds.

Of course, there are ways to mitigate static's overhead.

The best option is to put all of your static resources under a specific folder. (IE /static) You can then mount static to that path so that it only runs when the path starts with /static:

app.use('/static', express.static(__dirname + '/static'));

In this situation, you'd put this above router. This avoids processing other middleware/the router if a file is present, but to be honest, I doubt you'll gain that much.

You could also use staticCache, which caches static resources in-memory so that you don't have to hit the disk for commonly requested files. (Warning: staticCache will apparently be removed in the future.)

However, I don't think staticCache caches negative answers (when a file does not exist), so it doesn't help if you've put staticCache above router without mounting it to a path.

As with all questions about performance, measure and benchmark your real-world app (under load) to see where the bottlenecks really are.


Express 4

Express 4.0 removes app.router. All middleware (app.use) and routes (app.get et al) are now processed in precisely the order in which they are added.

In other words:

All routing methods will be added in the order in which they appear. You should not do app.use(app.router). This eliminates the most common issue with Express.

In other words, mixing app.use() and app[VERB]() will work exactly in the order in which they are called.

app.get('/', home);
app.use('/public', require('st')(process.cwd()));
app.get('/users', users.list);
app.post('/users', users.create);

Read more about changes in Express 4.

Heliograph answered 2/10, 2012 at 18:9 Comment(16)
@ebohlman - So is it possible to have app.router calls in multiple places in the stack? For example, if I defined routes before and after configuring, what would happen?Fuller
@Heliograph - What is the difference between express.router and app.router?Fuller
The router goes in one place. If, the first time you call app.get (or post or others), you haven't yet used app.router, Express adds it for you.Heliograph
And at that point, all routes defined will be accessible regardless of when they are defined?Fuller
Right. All routes are added to a single router object.Heliograph
so, router is a public property create by express that points to an instance of express.router ? if I create var myApp = express(), then express will return an object to myApp variable, already containing this property ? then we can access myApp.routerPannell
@JoneJohnPolvora: Yes. myApp.router would return a function.Heliograph
@Heliograph is it more efficient to have express.static above app.router to avoid all routes being "processed" for each static file? The trade off being you must be careful in naming your public static files. What about all of the other app.use() config? Do you really need to run the methodOverride() and cookieParser() middlewares before serving static files?Halogenate
@MikeCauser: No, because the overhead of disk access (to see whether or not a file exists) is larger than the overhead of function invocation. In my test, that overhead amounted to 1ms on an unloaded server. That's much likely to be higher under load, where requests will compete for disk access. With static after router, the question about the other middleware becomes irrelevant since it must be above the router.Heliograph
...of course, if you can restrict all static files to a specific path, you could mount the static middleware to that path and use it first -- app.use('/static', express.static(...)). This avoids processing other middleware/the router if a file is present, but to be honest, I doubt you'll gain that much. As with all questions about performance, measure and benchmark your real-world app (under load) to see where the bottlenecks really are.Heliograph
app.router is removed in the current master branch, which will be express-4.0. Each route becomes a separate middleware.Recension
as of express 4, app.use(app.router) is removed. please see the docs github.com/visionmedia/express/wiki/New-features-in-4.x – Jonathan OngYuriyuria
The Express 4.2 command line generates code that uses express.Router() (with a capital R) and does NOT call app.use() for routes. So there is still a router of sorts there, and the default framework has us using it. Apparently it allows multiple instances of an application to be installed under different roots. My search for an explanation landed me at this tutorial: scotch.io/tutorials/javascript/…Nel
Clarification: The new express 4 router allows multiple differently configured routers to be installed under different root paths. It also allows root paths to be factored out of the routes. Per my link above.Nel
One more clarification, as I work with this. In express 4, multiple routes can be assigned to a router, and then to use the router, the router is given a root path and placed in the "middleware" stack via app.use(path, router). This allows related routes to each use their own router and to be assigned a base path as a unit. If I understood it better, I'd offer to post another answer. Again, I'm getting this from scotch.io/tutorials/javascript/…Nel
yes, but app.params will be invoked before app.get, app.post, etcTessera
V
3

Routing means determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on). Each route can have one or more handler functions, which are executed when the route is matched.

In Express 4.0 Router, we are given more flexibility than ever before in defining our routes.

express.Router() is use multiple times to define groups of routes.

route used as middleware to process requests.

route used as middleware to validate parameters using ".param()".

app.route() used as a shortcut to the Router to define multiple requests on a route

when we are using app.route(), we are attaching our app with that router.

var express = require('express'); //used as middleware
var app = express(); //instance of express.
app.use(app.router);
app.use(express.static(__dirname + '/public')); //All Static like [css,js,images] files are coming from public folder
app.set('views',__dirname + '/views'); //To set Views
app.set('view engine', 'ejs'); //sets View-Engine as ejs
app.engine('html', require('ejs').renderFile); //actually rendering HTML files through EJS. 
app.get('/', function (req, res) {
  res.render('index');  
})
app.get('/test', function (req, res) {
  res.send('test')
})
Verbiage answered 4/11, 2016 at 17:21 Comment(0)
B
2

In express Version 4 we can easily define routes in the following manner:

server.js:

const express = require('express');
const app = express();
const route = require('./route');

app.use('/route', route);
// here we pass in the imported route object

app.listen(3000, () => console.log('Example app listening on port 3000!'));

route.js:

const express = require('express');
const router = express.Router();

router.get('/specialRoute', function (req, res, next) {
     // route is now http://localhost:3000/route/specialRoute
});

router.get('/', function (req, res, next) {
    // route is now http://localhost:3000/route
});

module.exports = router;

In server.js we imported the router object of the route.js file and apply it in the following manner in server.js:

app.use('/route', route);

Now all of the routes in the route.js have the following base URL:

http://localhost:3000/route

Why this approach:

The main advantage of taking this approach is that now our app is more modular. All the route handlers for a certain route now can be put into different files which makes everything more maintainable and easier to find.

Bifilar answered 12/9, 2018 at 7:54 Comment(0)
C
0

An article by @kelyvinn from 2016, with the intent to demonstrate modularity, includes this code:

// controllers/apis/dogs/index.js
const
    express = require('express'),
    dogService = require('../../../services/dogs');

let router = express.Router();

router.get('/', dogService.getDogs);
router.get('/:id', dogService.getDogWithId);

module.exports = router;
Computer answered 17/8, 2020 at 16:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.