Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/gimenete/node-errors

Helper module for handling errors
https://github.com/gimenete/node-errors

Last synced: about 1 month ago
JSON representation

Helper module for handling errors

Awesome Lists containing this project

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

```javascript
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:

```javascript
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

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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)`