Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restify Controllers With Node 7 Async/Await

Tags:

node.js

Lets look at the below modified Restify example which now includes Node 7/8s async/await support.

I have slight concerns on the proper implementation of this into express/restify/etc. My concern is with the promise hanging in the event loop for longer than it needs to... I know that this isn't exactly a promise, however should I be concerned about this implementation? I have yet to notice any issues.

'use strict';

const restify = require('restify');
const User = require('./models/User');

const server = restify.createServer({
  name: 'myapp',
  version: '1.0.0'
});

server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());

server.get('/echo/:name', async function (req, res, next) {
  try {
    const user = await User.findOne({
      name: req.params.name;
    });
    res.send(user.get({plain: true}));
  } catch (error) {
    console.error(error);
    res.send(500);
  }
  return next();
});

server.listen(8080, function () {
  console.log('%s listening at %s', server.name, server.url);
});
like image 226
kevingilbert100 Avatar asked Apr 19 '17 15:04

kevingilbert100


1 Answers

There is a problem with using async function instead of regular functions accepting callbacks as the errors are handled differently.

In callback functions (aka "err-backs") the callback must be called regardless if the execution was successful. The first parameter is to be an error object.

The async function simply returns a rejected promise in case of any error (synchronous or asynchronous).

So by default, the Express.js/Restify expect a regular err-back. And if you pass the async function instead and it fails the Express.js/Restify will keep waiting for a callback to be called ignoring the rejected promise. It's simply not aware of a returned promise and not handling it. Finally, the callback won't be called at all and the endpoint will timeout.

So you won't be able to handle the error properly.

You can try it out:

server.get('/echo/:name', async function (req, res, next) {
    throw new Error();
});

So as a rule of thumb, I'd recommend not to mix the concepts and never pass callbacks into async functions. This is a red flag.

In order to fix this you need to use a wrapper like this for example:

const wrap = function(fn) {
    return function(req, res, next) {
        return fn(req, res, next).catch(function(err) {
            return next(err);
        });
    };
};
server.get('/echo/:name', wrap(async function (req, res, next) {
    throw new Error();
}));

You will get a proper status code and there will be no timeout anymore.

There are also a couple of modules you can use if you don't want to wrap it yourself:

  • Express.js: express-async-wrap
  • Restify: restify-async-wrap
like image 54
Antonio Narkevich Avatar answered Sep 28 '22 12:09

Antonio Narkevich