Question

I have a node.js app Using express and 3 layers (controllers, api and models).

The controller asks the API layer to post, edit or add objects (promise styled). The API validates and executes the request (using the model layer (mongoose)). the controller must tells the client whether his request has been accepted or not by rendering error page or flash messages. Here is a small chart showing the main structure.

app structure chart

Here is a piece of example:

Controller:

  exports.doEdit = function(req, res) {
    var args;
    args = req.body;
    return app.api.mods.edit(req.getUserId(), req.params.id, args.name, args.value).then(function(status) {
      return res.send(200, "Saved!");
    }).fail(function(err) {
      return res.send(400, err.message);
    });
  };

API Layer:

/*
  Edit a mod
  @param userid the current logged user id 
  @param slug the slug of the mod
  @param field the field to be edited
  @param value the new value
  @permission mod:edit
  */


  exports.edit = (function(userid, slug, field, value, callback) {
    return canThis(userid, "mod", "browse").then(function(can) {
      var Mod;
      Mod = mongoose.model("Mod");
      return Mod.findOne({
        slug: slug
      }, function(err, mod) {
        if (can === false && mod._id !== userid) {
          return callback(new Error("unauthorized"));
        }
        if (err || !mod) {
          if (err) {
            console.log(err);
          }
          return callback(new Error("Please try again"));
        }
        mod[field] = value;
        mod.save();
        return callback("ok");
      });
    });
  }).toPromise(this);

How to makes easy, flexible and clean communication for the errors ?

I thought of adding a JSON object like that

{
    status: "error",
    code: "404",
    id: "not_found",
    message: "Not found"
}

But how to add it to the error that takes only a message? And Is it flexile and "clean"?

The whole project can be seen here : https://github.com/CraftYourModCorporation/OpenCubes

Was it helpful?

Solution

If i understand you right, you're asking about how to propagate the error to the client with enough information.

first of all, javascript is dynamic, so you could just add the properties you need to your Error instance before passing it to the callback/response.

    if (can === false && mod._id !== userid) {
      var error = new Error("unauthorized")
      error.code = 401
      error.id = "not_authorized"
      return callback(error);
    }

Don't forget that in the matter of separation of concerns, every layer has it's own semantics and it has to encapsulate the lower layers. This is also true for error handling. Sometimes it is suitable to let the error just bubble up, but in most of the time this is bad, or at least bad user experience. So at least in the Controller you should transform any error bubbling up here to user/client-friendly http error codes. For this you have to think about what does an error mean to the upper next layer.

so the code like res.send(404, err.message) is easy, but not very helpful. If you really want to do a good error handling then you have to do a step further and do some matching here. In static typed languages like Java you can do matching on Type. For JS this is possible too (with instanceof), but this is not very reliable. But you could use every other property or introduce own property with well defined error types.

    var error = new Error("Some lengthy message for humans, who are reading log files")
    error.errType = require('./errorTypes').NOT_AUTORIZED
    callback(error)

and in the api user code

    if(err){
        if(err.errType === errorTypes.NOT_AUTHORIZED)
           return res.send(401, "Wrong credentials")        
    }

you could, to reduce code duplicates, extract the error matching into an own module or a simple function, which maps internal types to external.

so this is simple (not the same as easy, easy depends on your context), and it is clean in the sense of callback style code. same is valid for promise based apis, only there you have error propagation and separation of resolve and reject control flows.

What i do not cover are things like Node.js domains (since they are not easy to use with express) and Zone.js stuff. This things might bring another tool-chains for error handling, but in the essence it's still the same. You have to transform errors somewhere.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top