2

I have the following simplified middleware function:

router.put('/', function (req, res, next) {

  const data = req.body;
  const q = req.parsedFilterParam;
  const opts = req.parsedQueryOpts;

  ResponseCtrl.update(q, data, opts)
  .then(stdPromiseResp(res))
  .catch(next);
});

I want to add some ability to catch errors to the middleware, just to save some code, something like this:

router.put('/', function (req, res, next) {

  const data = req.body;
  const q = req.parsedFilterParam;
  const opts = req.parsedQueryOpts;

  return ResponseCtrl.update(q, data, opts)
  .then(stdPromiseResp(res));

});

so we now return the promise in the middleware function, and we can forgo the catch block.

so internally, it might look like this now:

nextMiddlewareFn(req,res,next);

just looking to change it to:

const v = nextMiddlewareFn(req,res,next);
if(v && typeof v.catch === 'function'){
   v.catch(next);
});

does anyone know how to do this with Express?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • I suppose we could just switch to Koa, that might solve it. But simply using async/await probably won't solve it, or will it? – Alexander Mills Mar 21 '18 at 19:30
  • Doing what I was looking for in the question is not that safe. It's much safer if all middleware is only called after promise resolution. – Alexander Mills Mar 21 '18 at 19:49
  • this ended up working for me: https://medium.com/@the1mills/hacking-express-to-support-returned-promises-in-middleware-9487251ca124 – Alexander Mills Mar 25 '18 at 05:56

3 Answers3

2

Use express promise router.

var router = new ExpressPromiseRouter();

router.get('/foo', function(req, res) {
  return somethingPromisey();
});
// or...
router.get('/bar', async function(req, res) {
  await somethingPromisey();
});

app.use(router);

PS: There's no need to be afraid of async/await on performance grounds. There is no appreciable difference vs regular functions with promises.

josh3736
  • 139,160
  • 33
  • 216
  • 263
  • Promises are significantly less performant than vanilla callbacks. But you might be right that async/await is not less performant than promises. – Alexander Mills Mar 21 '18 at 20:00
  • That is correct; callbacks are definitely faster, but if you're already using promises, there's no additional penalty for `async`/`await`. – josh3736 Mar 21 '18 at 20:01
  • Accepted the answer, although as an aside I do suspect using generators does increase memory requirements even more than just promises, have to look into it more – Alexander Mills Mar 22 '18 at 15:08
  • you might enjoy this solution lulz https://medium.com/@the1mills/hacking-express-to-support-returned-promises-in-middleware-9487251ca124 – Alexander Mills Mar 25 '18 at 05:56
1

You may also want to try the general purpose solution: asyncback.

For usage in ExpressJS middle-ware:

const asyncback = require('asyncback');
 
app.get('/users', asyncback(async (req, res) => {
    const users = await User.find({ 'banned': false });
    const offer = await Offers.findOne({ 'active': true });
    res.json({ 'users': users, 'offer': offer });
}));

When the async function returns or throws, the next callback passed by express to asyncback is automatically called accordingly.

S.D.
  • 29,290
  • 3
  • 79
  • 130
0

Note to the reader, the following actually doesn't work:

router.put('/', async function (req, res, next) {

  const data = req.body;
  const q = req.parsedFilterParam;
  const opts = req.parsedQueryOpts;

  await ResponseCtrl.update(q, data, opts)
  .then(stdPromiseResp(res));

});

if the promise is rejected, the try/catch that surrounds each piece of middleware won't actually pick it up, if the promise is rejected, it will bubble up to an unhandledRejection handler if there is one.

I would look forward to a good solution though which:

  1. could be typed with TypeScript
  2. doesn't use async/await.

This is the solution that worked best for me as of now:

https://medium.com/@the1mills/hacking-express-to-support-returned-promises-in-middleware-9487251ca124

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • for a simple proof of concept, see: https://gist.github.com/ORESoftware/7debd0d057ee65306524739e2e74a1cf – Alexander Mills Mar 21 '18 at 19:45
  • 1
    I would be surprised if this actually works. [The `try`/`catch` that express wraps around router handler calls is inside a regular function.](https://github.com/expressjs/express/blob/4.16.3/lib/router/layer.js#L86-L99) `try` only catches rejected promises if you `await` them; in the express `try` block, the returned promise is completely ignored. – josh3736 Mar 21 '18 at 20:04
  • it's literally the same thing as your answer Josh, and my answer came first – Alexander Mills Mar 21 '18 at 20:13
  • 1
    No, `express.Router` does not support promises. You have to use a module that adds promise support, which is what my answer suggests. – josh3736 Mar 21 '18 at 20:19
  • @josh3736 you're right, I was using domains on my server and that was catching the error, not the try/catch, I just got confused about why it was being trapped – Alexander Mills Mar 21 '18 at 22:27