2

I'm not at my first express.js application, although I have still to figure out the most robust way to handle errors.

Since io.js is reality from a couple months I'm using native Promises to help myself with asyncronicity, the following code reflects that.

My error-handling middleware is as following:

router.use(function (err, req, res, next) {
  // in case err.custom is present, means is an "handled" Error, created by developers
  if (!!err.custom) {
    return res.status(err.code).json(err.message);
  }

  if (err instanceof Error) {
    console.error(err.stack);
    return res.status(500).send('Runtime Error'); // should be reported!
  }
  // last but not least, validation error
  res.status(400).send(err);
});

An example controller is written like this:

function callService1 (param1) {
  return new Promise(function (resolve, reject) {
    service.call(param1, function (err, data) {
      if (!!err) return reject(err); // this is an Error object?? not sure!

      if (!!data.handledError) { // this is an handled Error to the user, not 500
        return reject({ custom: true, status: 403, message: 'service1 tells you that myCoolParam is not valid' });
      }
      resolve(data);
    });
  };
}

function callService2 (dataFromParam1) {
  return new Promise(function (resolve, reject) {
    // something here
  });
}

// this is the API "controller"
module.exports = function (req, res, next) {
  callService1(req.body.myCoolParam)
  .then(callService2)
  .then(function (service2Output) {
    res.status(200).json({ message: 'everything went smooth!' });
  })
  .catch(next); // here is the catch-all errors
};

As you can see the express middleware looks quite tidy and elegant.
I usually handle all the interesting errors to the user in the rejects(), some of them are called with an object where I tell the errorhandling middleware.

The problem is service from the example is a 3rd-party library. Those libraries some times returns a string, sometime an object (from an external API), sometimes a javascript Error.

Currently I cannot handle custom javascript Objects, moreover if I want to throw an error 500 to the user I have to do reject(new Error(err)); but sometimes this err is an object, resulting in:

Error: [object Object]
    at errorHandler (awesomeapi\postsomething.js:123:16)
    at IncomingMessage.<anonymous> (node_modules\mandrill-api\mandrill.js:83:24)
    at emitNone (events.js:72:20)
    at IncomingMessage.emit (events.js:163:7)
    at _stream_readable.js:891:16
    at process._tickCallback (node.js:337:11)

This is not cool at all, and I would really like to figure out a way to handle gracefully those errors, without adding code (if possible), since I find this syntax quite elegant and concise.

Archie Reyes
  • 527
  • 2
  • 14
Valerio
  • 2,390
  • 3
  • 24
  • 34

1 Answers1

1

I've thought a lot of about this problem, and I ended up creating/using this https://github.com/yzarubin/x-error/blob/master/lib/x-error.js It is a custom server side error object that inherits Error, and extends with functionality to handle http codes and responses.

To apply it in your case, I would do somehing like this:

function callService1 (param1) {
  return new Promise(function (resolve, reject) {
    service.call(param1, function (err, data) {
      if (!!err) return reject(new xError().extend(err).setHttpCode(500)); // This will inherit the original stack & message

      if (!!data.handledError) { 
        return reject(new xError('this is an handled Error to the user, not 500').setHttpCode(403));
      }
      resolve(data);
    });
  };
}

Then in your controller, you could check instanceof xError === true and handle it, else do some sort of default response. But I've also done something like this in an app where it was certain that every promise will eventually either resolve or reject an instance of xError:

router.use(function (err, req, res, next) {
  res.status(err.httpCode || 500).send(err.message || 'Internal error');
});
Yuri Zarubin
  • 11,439
  • 4
  • 30
  • 33
  • really interesting. Some documentation could make this library quite useful and used (I have googled extensively before posting on SO). I'll try and use it – Valerio Feb 26 '15 at 12:56
  • I meant to update its functionality recently, I'll see if I can provide some docs. – Yuri Zarubin Feb 26 '15 at 16:48