@namecheap/error-extender
Simplifies creation of custom Error
classes for Node.js and Browser!
...which then produces stack
with appended stacks of supplied cause
(very much like in Java)!
const extendError = require('@namecheap/error-extender');
const CustomError = extendError('CustomError');
const rootCause = new Error('the root cause');
console.log(new CustomError({ message: 'An error has occurred.', cause: rootCause }));
Shall output:
CustomError: An error has occurred.
at Object.<anonymous> (/opt/app/index.js:7:13)
at Module._compile (internal/modules/cjs/loader.js:702:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
at Module.load (internal/modules/cjs/loader.js:612:32)
at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
at Function.Module._load (internal/modules/cjs/loader.js:543:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:744:10)
at startup (internal/bootstrap/node.js:240:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)
Caused by: Error: the root cause
at Object.<anonymous> (/opt/app/index.js:5:19)
at Module._compile (internal/modules/cjs/loader.js:702:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
at Module.load (internal/modules/cjs/loader.js:612:32)
at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
at Function.Module._load (internal/modules/cjs/loader.js:543:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:744:10)
at startup (internal/bootstrap/node.js:240:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)
100% Code Coverage
Oh, by the way, 100% test coverage. See for yourself (via npm test
)!
Features
"Extending" Errors
It's quite simple! See below:
const extendError = require('@namecheap/error-extender');
const AppError = extendError('AppError');
Or... A bit more complex using the second argument (options):
const extendError = require('@namecheap/error-extender');
const AppError = extendError('AppError', {
defaultMessage: 'An unhandled error has occurred.',
defaultData: { status: 503, message: 'An unhandled error has occurred.' }
});
const ServiceError = extendError('ServiceError', {
parent: AppError,
defaultMessage: 'A service error has occurred.',
defaultData: { status: 500, message: 'A service error has occurred.' }
});
const DatabaseError = extendError('DatabaseError', {
parent: ServiceError,
defaultMessage: 'A service database error has occurred.',
defaultData: { message: 'A service database error has occurred.' }
});
require('assert').deepStrictEqual(
DatabaseError.defaultData, {
status: 500,
message: 'A service database error has occurred.'
});
Yes, defaultData
merges!
error-extender
Arguments
error-extender
accepts a single object literal as second argument.
The options (object literal keys) are as follows:
key | expected type |
---|
parent | Error.prototype or one that extends it |
defaultMessage | string |
defaultData | any |
"Extended Errors"
- Creates prototype-based
Error
classes (child/subclass) : "Extended Errors". - Those "Extended Errors", accepts
cause
(Error
); very much like how it is with Java Exception
. - Appends stack of
cause
to the bottom of instantiated "Extended Errors" stack. - "Extended Errors" constructor & argument (w/ optional
new
):
new ExtendedError(options)
ExtendedError(options)
Yes, much like JavaScript's native Error
, "Extended Errors" can be written/used "factory-like" (without the new
keyword).
"Extended Errors" Arguments (constructor)
"Extended Errors" accepts a single object literal as argument:
const extendError = require('@namecheap/error-extender');
const ServiceError = extendError('ServiceError');
try {
} catch (error) {
throw new ServiceError({
message: 'An error has occurred',
data: { ref: '7e9f876ca116' },
cause: error
});
}
The options (object literal keys) are as follows:
key | alias | expected type |
---|
message | m | string |
data | d | any |
cause | c | instancedof Error |
Given the alias, you may construct extended errors by:
const extendError = require('@namecheap/error-extender');
const ServiceError = extendError('ServiceError');
try {
} catch (error) {
throw new ServiceError({
m: 'An error has occurred',
d: { ref: '7e9f876ca116' },
c: error
});
}
Note: Aliases are evaluated first; hence if you have both m
and message
, if m
's value is truthy, then m
's value will be used.
Instance Properties
As with Error
, "Extended Errors" would have the following properties:
... "Extended Errors" shall have the following additiona properties:
data
- (as set in constructor args)cause
- (as set in constructor args)
data
merging w/ defaultData
Yes, you heard right, instance data
merges with defaultData
!!!
See example below:
const extendError = require('@namecheap/error-extender');
const AppError = extendError('ServiceError', {
defaultData: { status: 503, message: 'An unhandled error has occurred.' }
});
const appError = new AppError({ d: { status: 401 } });
require('assert').deepStrictEqual(
appError.data, {
status: 401,
message: 'An unhandled error has occurred.'
});
The inspiration (thanks bluebird
!):
const Promise = require('bluebird');
const extendError = require('@namecheap/error-extender');
const ServiceError = extendError('ServiceError');
const ServiceStateError = extendError(
'ServiceStateError',
{ parent: ServiceError });
function aServiceFunction() {
return new Promise(
function (resolve, reject) {
})
.catch(ServiceStateError, function (error) {
})
.catch(ServiceError, function (error) {
})
.catch(function (error) {
});
}
With JavaScript, I felt quite stifled when I was limited to:
- Do selective/custom handling based on matching messages from
throw new Error('..')
. - Return/propagate JSend-like responses to function "callers"/"users".
- ... or whatever error possible passing/handling could be done, throughout functions and callers/users.
With error-extender
with help from syntactic-sugar from bluebird
, you can improve (or even standardize) your way of propagating/handling errors throughout your application.
callers.