Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope of middleware functions in express.js

I'm learning express.js / node.js and have a good but not excellent understanding of the javascript prototype model. Hence, I'm a bit confused on the way middleware can be stacked in express.js's routing mechanisms.

Say we have this code

function andRestrictTo(role) {
    return function(req, res, next) {
       req.authenticatedUser.role == role
           ? next() : next(new Error('Unauthorized'));
   }
}

app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
    res.send('Deleted user ' + req.user.name);
});

Since andRestrictTo(role) returns a middleware, it get's executed in the routing chain - I got that. However:

  1. Where does the req, res, next parameters come from in the returned function? I guess that the "chain" is somehow queuing it and assings the parameters, but this is a bit too vague for a deeper understanding ...

  2. What is happening with the Error that is raised as next's parameter? Does an error simply break the middleware chain?

  3. If I would want to package the restriction mechanism to a separate file / module (like a security framework), how would that be done?

It would be cool if someone could point out the basic idea :)

like image 631
shredding Avatar asked Jun 30 '12 07:06

shredding


2 Answers

1) req and res come from the very source of Express JS, i.e. Node.JS http.createServer handler (both variables are modified a bit before actually hitting Express' handler). At that point Express holds the array of all routes and applies req, res and next function to each route. next function knows at which middleware we are at the moment (due to some scoping tricks) and calling it like this: next() takes you to the next handler.

2) When the error is raised (actually not raised, but passed to), the next function takes you to the error handler, which you can define using error method of app, for example (taken from Express documentation):

app.error(function(err, req, res, next){
    if (err instanceof NotFound) {
        res.render('404.jade');
    } else {
        next(err);
    }
});

Raising error breakes a chain of middlewares and takes you to the chain of error handlers (as you can see, you use next in error handlers as well).

3) Not difficult at all:

security.js

module.exports = function(req, res, next) {
    console.log('Security middleware!');
    next();
}

app.js

app.get('/', require('./security'), ...);
like image 106
freakish Avatar answered Oct 12 '22 01:10

freakish


1) Express's (actually Connect's) routing code takes an HTTP request and begins passing it along to all the middleware functions, calling each of them with the req and res that came along with the request, and adding a next that invokes the part of the routing code that passes control along to the next middleware function in the chain.

2) No error has been raised (only a throw statement can actually raise an error). The routing code recognizes that a middleware function has returned an Error object, and deals with that appropriately.

3) You'd just put it into a module that exports andRestrict (if it were the only externally-usable function in the module, you'd set exports=andRestrict and then invoke it with require('mymodule')); otherwise you'd set exports.andRestrict=<body of your function> and invoke it in two steps: mymodule=require('mymodule') early on, and mymodule.andRestrict later on (e.g. when passing it as middleware)).

like image 23
ebohlman Avatar answered Oct 12 '22 01:10

ebohlman