Descriptive errors through metadata
Typical strategies of parsing errors are fragile and couple code to the error messages. By defining error objects consistently, working with errors becomes predictable and efficient.
- arbitrary error metadata
- templated error messages
- stack traces
- JSend format errors
var erroz = require("erroz");
var DuplicateError = erroz({
name: "Duplicate",
code: "duplicate",
statusCode: 409,
template: "Resource %resource (%id) already exists"
});
// ...
throw new DuplicateError({ resource: "Unicorn", id: 1 });
/*
throw new DuplicateError();
^
Duplicate: Resource Unicorn (1) already exists
at Object.<anonymous> (/erroz/examples/staticErrorMessage.js:14:7)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:902:3
*/
npm install --save erroz
var errorDefinition = {
name: "NotFound",
template: "%resource (%id) not found"
};
var NotFoundError = erroz(errorDefinition);
Arbitrary data structure for metadata which will be available on every error instance. Some attributes have a special meaning which is why they are described below:
The name displayed when the error is thrown.
A static error message.
A dynamic error message. Variable substitution from the data object is done with %<variable name>
.
var data = { resource: "Unicorn", id: 1 };
throw new NotFoundError(data);
// Duplicate: Resource Unicorn (1) already exists
A set of data to be used with the errorDefinition.template
property.
var overrideMessage = "You are not authorized to eat my cookies";
throw new ForbiddenError(overrideMessage);
// Forbidden: You are not authorized to eat my cookies
A message to override errorDefinition.message
or errorDefinition.template
. Use of this option will set error.data
to an empty object.
Errors can be converted to JSON with JSON.stringify()
.
var err = new DuplicateError({ resource: "Unicorn", id: 1 });
console.log(JSON.stringify(err));
/*
{
"name": "Duplicate",
"code": "duplicate",
"status": "fail",
"statusCode": 409,
"template": "Resource %resource (%id) already exists",
"data": {
"resource": "Unicorn",
"id": 1
},
"message": "Resource Unicorn (1) already exists"
}
*/
Custom JSON format
The AbstractError.toJSON
method can be defined to customize the JSON format.
// Set a custom `toJSON` method for all errors
erroz.AbstractError.prototype.toJSON = function() {
return {
name: this.name,
code: this.code
};
};
console.log(JSON.stringify(err));
/*
{
"name": "Duplicate",
"code": "duplicate"
}
*/
Converts the error to a JSend-style object.
The JSend status
attribute is derived from the statusCode if not passed explicitly. Valid codes are 4xx and 5xx.
In case of an invalid statusCode, .toJSend()
will throw an error.
var err = new DuplicateError({ resource: "Unicorn", id: 1, status: 409 });
err.toJSend();
/*
{
"status": "fail",
"code": "duplicate",
"message": "Resource Unicorn (1) already exists",
"data": {
"resource": "Unicorn",
"id": 1,
"stack": "Duplicate: Resource Unicorn (1) already exists\n at Object.<anonymous> (/erroz/examples/ toJson.js:13:11)\n at Module._compile (module.js: 456:26)\n at Object.Module._extensions..js (module.js:474:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n at Function.Module.runMain (module.js:497:10)\n at startup (node.js:119:16)\n at node.js: 906:3"
}
}
*/
Define a custom error renderer.
erroz.options.renderMessage = function(data, template) {
return "Ooops";
}
Whether the stack should be included in errors. Default is true.
erroz.options.includeStack = false;
Consider turning this off in production and sending it to a logger instead.
Define a global error handler which calls toJSend()
if the error is an instance of erroz.AbstractError
.
why do this? So you can simply next
all your errors in your route-handlers.
function myAwesomeRoute(req, res, next) {
if (!req.awesome) {
next(new NotAwesomeError());
return;
}
next();
}
app.use(function errozHandler(err, req, res, next) {
if (err instanceof erroz.AbstractError) {
res.status(err.statusCode).send(err.toJSend());
return;
}
// Pass on all non-erroz errors
next(err);
});
MIT