node-errors
v0.2.0
Published
Helper module for handling errors
Downloads
32
Readme
node-errors
Helper module for handling errors
Motivation
I like to translate system errors (such as filesystem errors or network errors) into custom errors with meaningful messages and also generate differents types of errors for different situations such as something was forbidden or something was not found. This way when developing an REST API it is easy for example to generate a good HTTP status code and response message.
Also I hate having to write if (err)
all the time morover when I only need to translate the error and pass it through. Many times are errors difficult to test (e.g. a networking error) and if the body of that if
finally happens to be run in production and it was not tested you can be screwed.
How it works
var errors = require('node-errors')
function findUser(id, callback) {
db.query('SELECT * FROM users WHERE id=?', [id], function(err, results) {
errors.with(callback)
.on(err)
.internal('Error while trying to find user with id = `%s`', id)
.when(results.length === 0)
.notFound('User not found with id = `%s`', id)
.success(function() {
callback(null, results[0])
})
})
}
A similar code without node-errors
would be:
var errors = require('./custom-errors')
var InternalError = errors.InternalError
var NotFoundError = errors.NotFoundError
function findUser(id, callback) {
db.query('SELECT * FROM users WHERE id=?', [id], function(err, results) {
if (err) {
return callback(new InternalError(err, 'Error while trying to find user with id = `%s`', id))
} else if (results.length === 0) {
return callback(new NotFoundError('User not found with id = `%s`', id))
} else {
return callback(null, results[0])
}
}
}
The amount of code is more or less the same but with node-errors
you don't have to implement those custome errors, you can also create new custom errors easily (see below) and you will have 100% of test coverage easily and you don't need to be afraid of untested code. For instance: how do you test the case in which db.query
returns an error? And thus, how do you test that the code inside that if
won't crash your Node.js process?
Usage
You always start with errors.with(callback)
. That callback
is the function that will be called in case of any error or unmet condition.
Then you use on(err)
if you want to check against an existing error or when(condition)
for any other condition, followed by a call to any of the following:
internal(message, [...])
For internal errorsforbidden(message, [...])
If there are not enough permissions to do somethingrequest(message, [...])
For example when invalid parameters are passed in an HTTP requestnotFound(message, [...])
When trying to read information of an unknown resource
Finally you will call success
passing a callback function where you will put the code to be run when everything is ok.
You can check if an error has been generated by node-errors
with errors.isCustomError(err)
. You can check for a specific error type with isXXX()
(e.g. err.isInternal()
) and you can also access the type
field that will give you the error type in a string (internal
, forbidden
, request
, notFound
).
If a custom error was generated using on(err)
then the custom error will have a root
property defined referencing the initial error.
Custom errors
You can define custom errors easily. Just use errors.defineErrorType(type)
The following is a full example of how to define custom errors and how to use the
errors.defineErrorType('external')
function downloadPicture(id, callback) {
http.downloadFile('.../'+id+'jpg', function(err, buffer) {
errors.with(callback)
.on(err)
.external('There was an external error downloading picture with id = `%s`', id)
.success(function() {
callback(null, buffer)
})
})
}
downloadPicture('1234', function(err, buffer) {
if (err) {
errors.isCustomError(err) // true
err.isExternal() // true
err.type // 'external'
err.root // the root error that generated this error
return
}
})
Creating custom errors directly
You can also create custom errors directly. Example:
var err = errors.notFound('User not found `%s`', email)
If the last argument is an error object it is not used to format the error message and it is set to the err.root
field.
var err = errors.internal('Internal error', error)
err.root === error // true
Listening to errors
Another thing you can do is to easily listen to errors. The node-errors
module is an EventEmitter
that emits events when errors are generated. So you can do:
errors.on('internal', function(err) {
// log this internal error
})
You could for example generate a custom critical
error type and everytime that one critical error is generated you could send an SMS to somebody. For example if a payment gateway fails, or whatever.
Using the utility nook function
I find myself writing many times code like this:
somethingAsync(function(err, arg) {
if (err) return callback(err)
callback(null, arg+something)
})
If you don't want to translate any error, just pass it through you can use the nook
function:
var nook = errors.nook
somethingAsync(nook(callback,
function(arg) {
callback(null, arg+something)
})
)
And if you just want to pass an already known argument or arguments in case of success you can do:
somethingAsync(nook(callback, something))
In case of error the callback
function will be called with that error. In case of success the callback
function will be called with callback(null, something)