11

Take the following POST function in express. (I am using express 3.5.1)

app.post('/example', someFunctionOne, someFunctionTwo, function(req, res){
    if(!req.someVar){
        return res.send(400, { message: 'error'});
    } else{
        return res.json(200, { message: 'ok'}); 
    }
});

If I get some result from someFunctionOne which means someFunctionTwo is redundant, is there a way to skip someFunctionTwo and go to the last unnamed function which will send the response?

So I guess in the same way there is the "next()" function where is the "last()" function? If this is not possible why not? It seems like an oversight to me but is there a good reason?

Jonan
  • 2,485
  • 3
  • 24
  • 42
Paulie
  • 1,567
  • 4
  • 16
  • 21

4 Answers4

11

You can do next('route') which will go to the next route entirely. This is not exactly what you need in this case, but it would work if you broke your code up into 2 separate routes.

However, I think there are generally 2 approaches to this kind of conditional logic:

  • make someFunctionOne put some state on the req instance when it gets the special result and make someFunctionTwo smart enough to check for that and when found call next() and bypass itself. This is the most idiomatic express thing to do, and it's how most middleware detect when they have been called more than once on the same request and avoid redoing their work again.
  • In someFunctionOne, when the special case happens, just invoke lastFunction directly. Remember the middleware abstraction isn't the holy grail. If your middleware are so tightly coupled, maybe they should be one middleware and some helper functions. There are lots of other ways to organize code that might feel more natural.
Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • Can you clarify what you mean by next('route'). Do you mean next('/route2'). There doesn't seem to be any documentation on the next route with a parameter. Is it not possible that the parameter could be a function?I am already doing what you suggest in your first bullet. It just seems inefficient/ugly code wise having to attach variables to skip through functions when a 'last()' function would be much neater. I can't do your second suggestion because all the final functions are named functions (which I like because it keeps the function with the request route) – Paulie Jul 21 '14 at 14:27
  • 3
    No I mean literally exactly `next('route');`. 'route' is a magic string connect will recognize. – Peter Lyons Jul 21 '14 at 15:26
6

My instinct is to do something like this:

const funcOne = (req, res, next) => {
  // do something
  if (/* you were successful */) {
    res.locals.shouldSkipFuncTwo = true
  }
  next()
}

const funcTwo = (req, res, next) => {
  if (res.locals.shouldSkipFuncTwo) return next()
  // do whatever stuff
  next()
}

router.get('/', funcOne, funcTwo, (req, res) => {
  res.status(200).send('hello world')
)}

If you haven't used res.locals before, here are the express docs on it. Basically, it's a property in the response object that's there for you to use as a container.

JSilv
  • 1,035
  • 12
  • 26
1

Probably the best way to do it to make some helper or put your own middleware into chain instead of your functions.

So your code will look like:

app.post('/example', oneOf(key, someFunctionOne, someFunctionTwo), function(req, res){
  if(!req[key]){
    return res.send(400, { message: 'error'});
  } else{
    return res.json(200, { message: 'ok'}); 
  }
});

And the helper should be something like that:

function oneOf (key) {
  var fns = Array.prototype.slice.call(arguments, 1);
  var l = fns.length;
  return function (req, res, next) {
    var i = 0;
    function _next () {
      if (req[key] || i === l) return next();
      fns[i](req, res, _next);
      i += 1;
    }
    _next();
  }
}

If you will decide to do it just here the code will looks like:

app.post('/example', functionOneOrTwo, function(req, res){
  if(!req.someVar){
    return res.send(400, { message: 'error'});
  } else{
    return res.json(200, { message: 'ok'}); 
  }
});

function functionOneOrTwo(req, res, next) {
  someFunctionOne(req, res, function () {
    if (req.someVar) return next();
    someFunctionTwo(req, res, next);
  });
}

Simple, but untested ;-)

Alex Yaroshevich
  • 710
  • 8
  • 19
1

Actually I was facing the very same problem. And I just found the express-unless module which does exactly this: https://github.com/jfromaniello/express-unless

user826955
  • 3,137
  • 2
  • 30
  • 71