This is how my node.js server is organised
Problem: The API makes requests to my mongodb database via the model (mongoose). So in the API layer I can have:
User.findById(id, function(user) {
if(user._id !== userid) return deferred.reject(new Error()); // ??
if(!user) return deferred.reject(new Error()); // ??
user[field] = value;
user.save(function() {
deferred.resolve(user);
});
});
But what errors should I throw? What way would be more proper and more user-friendly. I could try for example:
if(user._id !== userid) return deferred.reject(new Error(403));
But that would mean no message, just the error code :/
Another solution i tried:
exports.errors = errors =
NOT_FOUND:
id: "NOT_FOUND"
code: 404
message: "Can't found requested object. Please retry"
UNAUTHORIZED:
id: "UNAUTHORIZED"
code: 401
message: "Please login first"
FORBIDDEN:
id: "FORBIDDEN"
code: 403
message: "Access denied."
DATABASE_ERROR:
id: "DATABASE_ERROR"
code: 500
message: "Error with databse. Please retry"
exports.throwError = throwError = (message, id) ->
if typeof message is "string" then err = new Error message else err = message
err.type = id
err.data = errors[id]
return err
exports.handleHttp = (err, req, res, format="text") ->
console.log(" Error - #{err.message} (#{err.type}) on #{req.url}".red)
console.log(err.stack)
if format is "text"
res.send err.data.code, "#{err.data.type}: #{err.message}"
if format is "json"
res.send err.data.code,
type: err.data.type
message: err.message
id: err.type
status: "error"
errors: err.errors
And so I would use it like that:
if(user._id !== userid) return deferred.reject(error.throwError("Forbidden", "UNAUTHORIZED"));
And in the controller (after a promise)
.fail (err)->
errors.handleHttp err, req, res, "json"
But I don't like this solution, this leaves me with a bitter feeling, unsatisfied. Any other idea?
You can define new Error objects that extend the existing Error object. For example, the following would be for a 404 error:
function HttpError(message, code) {
this.message = message || "";
this.code = code || 500;
}
HttpError.prototype = Object.create(Error.prototype);
HttpError.prototype.send = function send(req, res) {
var type = req.accepts("json", "html", "text");
switch(type) {
case "json":
res.send(this.code, JSON.stringify(this));
break;
case "html":
case "text":
res.send(this.code, "An error occurred: " + this.message);
break;
}
};
function NotFoundError(message, code) {
this.name = "NotFound";
this.message = message || "Could not find the object you were requesting";
this.code = code || 404;
}
NotFoundError.prototype = Object.create(HttpError.prototype);
You can then use it like so:
User.findById(id, function(user) {
if(!user) return deferred.reject(new NotFoundError());
user[field] = value;
user.save(function() {
deferred.resolve(user);
});
});
You can place all of the errors (500, 404, 401, etc.) in an errors file and include that. So it would likely be new errors.NotFoundError();
Then, in your controller you can have a fail callback function like so:
getUser().fail(function(err) { err.send(req, res) });
This will only work if err is an instance of the HttpError class.