@aqo/exception
v1.0.2
Published
error extension for graceful exception handling
Downloads
12
Readme
exception
Error extension for graceful exception handling.
Usage
function Exception(messageOrError, assign = {})
messageOrError
: can be String or Error or Exception
assign
: optional, if provided must be Object
- if messageOrError is String, sets the message, and stack is set from point code was called
- if messageOrError is Error or Exception, sets the message and stack from that Error/Exception
- if assign is provided, all of its keys are assigned to the Exception being created
bonus "hidden" global configuration options that affect all Exceptions:
Exception.toRemoveNodeInternals = true;
If you set this to true, all Exceptions will remove Node.js internals from stack traces. This makes stacks a lot more compact and easier to read, at the price of potentially missing an actual error in Node.js's implementation if one happens.
Exception.trimPath = require('path').resolve(__dirname, '..');
If you set trimPath to a non-empty string, this path will be stripped from all paths in both the trace and the error message, making it more compact and easier to read, at the price of potentially missing relevant information.
Examples
const Exception = require('@aqo/exception');
// throw an error (this is native JavaScript, no external modules)
try {
throw Error('message');
} catch(error) {
console.log(
'\nerror instanceof Error:', error instanceof Error, // true
'\nerror instanceof Exception:', error instanceof Exception, // false
);
}
// throw an exception (this is using this package)
try {
throw Exception('message');
} catch(error) {
console.log(
'\nerror instanceof Error:', error instanceof Error, // true
'\nerror instanceof Exception:', error instanceof Exception, // true
);
}
// throw an exception with extra properties
try {
throw Exception('human friendly message as long as needed for debugging', { // message is unreliable for testing, i.e. can be dynamically generated, etc
code: 'ERR_MACHINE_FRIENDLY_VALUE', // reliable for testing, should be final once decided on
data: 'blob of information that can be useful for debugging', // actual data depends on code
});
} catch(error) {
switch(error.code) {
case 'ERR_MACHINE_FRIENDLY_VALUE':
console.error('known exception type happened:', error.message, error.data);
break;
default:
console.error('unknown error happened:', error);
}
}
// convert errors into exceptions
try {
JSON.parse('not-json-data');
} catch(error) {
console.error(Exception(error, { // keeps error's original message, stack, and other keys
code: 'ERR_JSON_PARSE', // if error already had a .code key, this overwrites it
}));
}
// also works with existing exceptions
console.log(Exception(Exception('multi-level error handling', {
foo: 'inner level',
bar: 'inner level',
}), {
foo: 'outer level',
})); // should have foo: 'outer level', bar: 'inner level'
// you can also use `new` to create Exceptions, but it's recommended to avoid it
const myException = new Exception('foo');
// same result as const myException = Exception('foo');
Why
Errors and exceptions are not the same thing.
Errors are something unexpected, that you should fix whenever you encounter. It makes sense for those to be thrown by your environment.
Exceptions are something you expect, that you can capture and handle. It makes sense for those to be thrown by users.
In JavaScript, there is only an Error object, which only lets you put a message, and generates a stack trace. This is enough to debug an actual error, but not enough to have exceptions that you handle in runtime.
Very often a piece of code can have one happy path and then multiple failure points, and you want to give the caller a graceful way to handle different failure points separately. Exactly how to do it is opinionated.
This Exception constructor gives you an unopinionated way to extend an Error with any keys you wish, while keeping the original stack trace, and being compatible with instanceof tests. Each project can choose its own style of Error extension.
The recommended style this package was created for is to add a "code" key with a string value in the format of "ERR_SHORT_ERROR_DESCRIPTION", and any arbitrary other keys that depend on the code. This allows you to provide callers with an easy way to choose which error types they want to handle (test .code's value) while being transparent about the error type and not using any ambiguous magic values that require checking docs to use. The "ERR_" prefix is also search-friendly allowing you to quickly find all error codes in a large chunk of code.
However you may use this any way that you see fit.
License
ISC