node-errors

0.2.0 • Public • Published

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 errors
  • forbidden(message, [...]) If there are not enough permissions to do something
  • request(message, [...]) For example when invalid parameters are passed in an HTTP request
  • notFound(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)

Readme

Keywords

Package Sidebar

Install

npm i node-errors

Weekly Downloads

14

Version

0.2.0

License

MIT

Last publish

Collaborators

  • gimenete