node-err
Simplify error handling and logging for Node.
Index
Overview
One Error Handler
With node-err, you always use the same error handler, giving you expected behavior...
nodeErr.repeat(err);
const nodeErr = require('node-err');
return fetch()
.catch(err => {
return nodeErr.repeat(err);
});
Bubbling-up Errors
With consistent error handling, bubbling-up errors is super easy...
return fetch()
.catch(err => {
return nodeErr.repeat(err);
})
.catch(err => {
return nodeErr.repeat(err);
})
.catch(err => {
return nodeErr.repeat(err);
})
.catch(err => {
return next(err);
});
Automatic logging
And logging/reporting is a breeze, it happens automatically...
nodeErr.repeat(err, details);
return fetchUsers()
.catch(err => {
let details = {
name: 'FETCH_USERS',
status: 500,
};
return nodeErr.repeat(err, details);
});
Server Logging
Server logging happens out-of-the-box so you can set-up alerts with a service such as Papertrail...
papertrail -f SERVER_ERROR
Custom Logging
But you can set up your only logger too...
nodeErr.setup({ logger: myOwnLogger });
Custom Output Responses
Configure and send response data for outputting to the browser. View the Simple Promises example to see it in action.
nodeErr.setup({ responses: ['user_message','internal_code'] });
return saveAnalytics()
.catch(err => {
return nodeErr.repeat(err, {
status: 500,
responses: {
user_message: 'Oops! Something went wrong.',
internal_code: '2352',
}
});
})
.catch(err => {
let statusCode = nodeErr.getStatus(err);
let outputResponse = nodeErr.getResponse(err);
return res.status(statusCode).send(outputResponse);
});
}
Localized/Silent Error Handling
Sometimes you need to handle some errors a little differently, while still allowing other errors to bubble-up or pass through...
nodeErr.repeat(customVal)(err);
return saveAnalytics()
.then(() => {
// do more stuff with the ability to
// bubble up a different fatal error
return true;
})
.catch(err => {
// if our saveAnalytics() failed, lets
// keep going and just return false
return nodeErr.repeat(false)(err);
})
.then(result => {
// did the analytics save ok?
return (result) ? next() : retry();
});
}
For more details, view the handling sections below.
Async/Await
(async function() {
try {
throw new Error('cubitum irem');
} catch(err) {
await nodeErr.repeat(err, { name: 'Do I know latin?' });
}
})().catch(err => {
// Hey Alexa...
});
Express Middleware
How about some Express middleware to catch/report any un-reported errors?
nodeErr.stop(err);
app.use((err, req, res, next) => {
let details = { req };
nodeErr.stop(err, details);
return next(err);
});
Error Output
What should you do with all of these errors? Whatever you like...
app.use((err, req, res, next) => res.sendStatus( nodeErr.getStatus(err) ));
Technical
Getting Started
Just require node-err
and start using repeat
to automatically log and bubble-up errors...
const nodeErr = require('node-err');
return fetch()
.catch(err => {
return nodeErr.repeat(err);
});
Config Vars (optional)
Call the setup
function in your entry file (index.js or server.js) and pass config vars (optional)...
const nodeErr = require('node-err');
nodeErr.setup({
prefix: 'FIND_THIS_ERROR',
status: 401,
debug: true,
logger: (err) => slackChannel('Doh', err)),
responses: ['user_message'],
overrideResponses: true,
});
Key | Type | Description |
---|---|---|
prefix |
string | Global prefix added to all error logs. |
status |
number | Default HTTP status code error. |
debug |
bool | Output error repetition tracing. |
logger |
func | Custom logging function (accepts Error obj). |
responses |
array | Array of response properties. |
overrideResponses |
bool | Allow automatic overwrite of responses when bubbling up. |
Additional Error Details (optional)
When calling repeat
add some more details so you know what went wrong (optional)...
nodeErr.repeat(err, {
name: 'AWS_IMAGE_NOT_SAVED',
req: req,
status: 400,
context: { imageName: 'budget-report.ppt'},
responses: { user_message: 'Oops! Something went wrong.' },
log: false,
censor: true,
});
Key | Type | Description |
---|---|---|
name |
string | (optional) Custom error name. |
req |
object | (optional) Express request object. |
status |
bool | (optional) Desired http status response code. |
context |
any | (optional) Whatever works. |
responses |
object | (optional) Data to place on the response output. |
log |
bool | (optional) Skip logging at this repeat node (intentional errors). |
censor |
bool | (optional) Skip request body log (persists with bubbling up). |
If req
is provided, you'll also get access to:
- IP Address
- Requested URL
- Request Body
- Request Method
- User Agent
Any responses
data provided, must have its property added to the setup
config.
Basic Block Error Handling/Bubbling
By default, calling repeat
on an error the first time will create an error report and then reject it again with a Promise.reject
.
Calling repeat
again (in subsequent catch block) on an already reported error will just reject the same error again (no further reporting).
return fetch()
.then(() => Promise.reject('level 1 error please'))
.catch(err => {
return nodeErr.repeat(err, { name: 'level 1' }); // report and rejects again
})
.then(() => doSomething()
.catch(err => {
return nodeErr.repeat(err, { name: 'level 2' }); // rejects level 1 error again
})
.then(() => doSomething()
.catch(err => {
return nodeErr.repeat(err, { name: 'level 3' }); // rejects level 1 error again
})
.catch(err => {
return next(err); // returns level 1 error
});
In this way, errors can bubble back up and be handled however you like.
Multi-directional Error Handling
You might want to handle some error differently, while still allowing processes above it to pass/bubble their errors through.
By using repeat
with a custom value, which returns a callback and accepts, you can achieve multi-directional functionality:
- Any previously reported will continue to bubble up and through.
- Any un-reported errors will output with whatever value you set.
In this way, you can have a silent error handler for one process without breaking promise chain for processes above/before it.
nodeErr.repeat('It failed!')(err);
return saveAnalytics()
.then(() => {
return fetchUser();
})
.catch(err => {
// if we didnt have a db conn error, and instead had an error
// from saveAnalytics, the below would just return `false`
return nodeErr.repeat(false)(err); // our db conn error will pass through
})
.then(result => {
return (result) ? next() : retry(); // this is skipped because of our db conn error
})
.catch(err => {
return next(err); // return db conn error
})
}
function fetchUser() {
return fetch()
.then(result => {
throw new Error('remote db conn err'); // fire error
})
.then(() => true) // this is skipped
.catch(err => {
return nodeErr.repeat(err, { name: 'FETCH_USER' }); // this will reject again
})
}
You can also force an error report on a silent/custom error, so it will still show in the logs:
nodeErr.repeat('It failed!', true)(err);
Examples
Viewing the Examples
There are several examples included in the project. To run these...
$ cd examples
$ npm install
$ npm run start
- Navigate to
http://localhost:5000
for a directory of examples.