0.3.1 • Published 7 months ago

berrors v0.3.1

Weekly downloads
-
License
MIT
Repository
-
Last release
7 months ago

berrors

Common error builder utility for Node.js. Contains common error types, and helpers for stack trace tracking to support more detailed error messages.

Usage

Install from NPM

npm

$ npm install --save berrors

yarn

$ yarn add berrors

Require and use the module

import {BErrors} from 'berrors';

new BErrors.NotFound('could not find it');

API

The API is designed to be daisy chained with all of the following base commands that are a part of all of the error types.

BError Commands

new BError(message, options) -> instance

Creates an instance of a BError for the type used.

try {
  // ...
} catch (err) {
  new BError('Some error', {cause: err});
}

Options:

  • cause? - The cause of the error. Can be a string, Error, or BError.
  • statusCode? - The HTTP status code to be used by error handlers.
  • errorCode? - The error code to be used by error handlers.
  • data? - Additional data to be used by error handlers.

.setErrorCode(errorCode) -> instance

Sets an error code to later be used by error handlers.

new BError('Not found').setErrorCode('leaf_error');

.setMessage(message) -> instance

Overrides the error message passed in.

new BError('Not found').setMessage('Unable to find the restaurant.');

.setStatusCode(statusCode) -> instance

Setting the response status code to be sent back down to the client.

new BError('Not found').setStatusCode(404);

.setData(data) -> instance

Sets customizable data that can be used down the error stack chain.

new BError('Not found').setData({foo: 'bar'});

Properties

PropertyDescription
messageThe error message.
nameThe error name.
stackThe error stack trace.
causeThe error cause.
statusCodeThe error status code.
errorCodeThe error code.
dataThe error data.

Error Types

Error TypeStatus CodeError CodeMessage
BadRequest400bad_requestBad Request Erro
Unauthorized401unauthorizedUnauthorized Error
PaymentRequired402payment_requiredPayment Required Error
Forbidden403forbiddenForbidden Error
NotFound404not_foundNot Found Error
MethodNotAllowed405method_not_allowedMethod Not Allowed Error
NotAcceptable406not_acceptableNot Acceptable Error
ProxyAuthenticationRequired407proxy_authentication_requiredProxy Authentication Required Error
RequestTimeout408request_timeoutRequest Timeout Error
Conflict409conflictConflict Error
Gone410goneGone Error
LengthRequired411length_requiredLength Required Error
PreconditionFailed412precondition_failedPrecondition Failed Error
PayloadTooLarge413payload_too_largePayload Too Large Error
URITooLong414uri_too_longURI Too Long Error
UnsupportedMediaType415unsupported_media_typeUnsupported Media Type Error
RangeNotSatisfiable416range_not_satisfiableRange Not Satisfiable Error
ExpectationFailed417expectation_failedExpectation Failed Error
ImATeapot418im_a_teapotI'm a teapot Error
MisdirectedRequest421misdirected_requestMisdirected Request Error
UnprocessableEntity422unprocessable_entityUnprocessable Entity Error
Locked423lockedLocked Error
FailedDependency424failed_dependencyFailed Dependency Error
TooEarly425too_earlyToo Early Error
UpgradeRequired426upgrade_requiredUpgrade Required Error
PreconditionRequired428precondition_requiredPrecondition Required Error
TooManyRequests429too_many_requestsToo Many Requests Error
RequestHeaderFieldsTooLarge431request_header_fields_too_largeRequest Header Fields Too Large Error
UnavailableForLegalReasons451unavailable_for_legal_reasonsUnavailable For Legal Reasons Error
InternalServerError500internal_server_errorInternal Server Error
NotImplemented501not_implementedNot Implemented Error
BadGateway502bad_gatewayBad Gateway Error
ServiceUnavailable503service_unavailableService Unavailable Error
GatewayTimeout504gateway_timeoutGateway Timeout Error
HTTPVersionNotSupported505http_version_not_supportedHTTP Version Not Supported Error
VariantAlsoNegotiates506variant_also_negotiatesVariant Also Negotiates Error
InsufficientStorage507insufficient_storageInsufficient Storage Error
LoopDetected508loop_detectedLoop Detected Error
NotExtended510not_extendedNot Extended Error
NetworkAuthenticationRequired511network_authentication_requiredNetwork Authentication Required Error

Error Type Examples

import { BErrors } from "./errors";

new BErrors.BadRequest('Invalid input');
// => BadRequestError { name: 'BadRequestError', message: 'Invalid input', statusCode: 400, errorCode: 'bad_request' }

// by status code
new BErrors['404']('Not found');
// => NotFoundError { name: 'NotFoundError', message: 'Not found', statusCode: 404, errorCode: 'not_found' }

Helpers for working with error causes

  • findCauseByReference - finding an error of a specific type within the cause chain
  • getErrorCause - getting the direct cause of an error, if there is any
  • fullMessage - gets the error message with the messages of its cause chain appended to it
  • fullStafck - gets a stack trace for the error + all its causes

findCauseByReference

Finding an error of a specific type within the cause chain. Is typescript friendly.

import {findCauseByReference} from 'berrors/helpers';

try {
  /* Something that can break */
} catch (err) {
  /** @type {MySpecialError} */
  const specialErr = findCauseByReference(err, MySpecialError);

  if (specialErr && specialErr.specialProperty === 'specialValue') {
    // Its okay, chill!
  } else {
    throw err;
  }
}

Used to find a specific type of error in the chain of causes in an error.

Similar to VError.findCauseByName but resolves causes in both Error Causes style, .cause, and VError style, .cause() + takes a reference to the Error class that you are looking for rather than simply the name of it, as that enables the TypeScript types to properly type the returned error, typing it with the same type as the reference.

Can be useful if there's some extra data on it that can help determine whether it's an unexpected error or an error that can be handled.

If it's an error related to a HTTP request, then maybe the request can be retried? If its a database error that tells you about a duplicated row, then maybe you know how to work with that? Maybe forward that error to the user rather than show a 500 error?

Note: findCauseByReference has protection against circular causes

getErrorCause

Getting the direct cause of an error, if there is any. Is typescript friendly.

import {getErrorCause} from 'berrors/helpers';

try {
  /* Something that can break */
} catch (err) {
  // Returns the Error cause, BError cause or undefined
  const cause = getErrorCause(err);
}

The output is similar to VError.cause() but resolves causes in both Error Causes style, .cause, and VError style, .cause().

Always return an Error, a subclass of Error or undefined. If a cause in Error Causes style cause is not an Error or a subclass of Error, it will be ignored and undefined will be returned.

fullMessage

Gets the error message with the messages of its cause chain appended to it.

import {BError, fullMessage} from 'berrors';

try {
  try {
    // First error...
    throw new Error('First error');
  } catch (err) {
    // ...that's caught and wrapped in a second error
    throw new BError('Second error', {cause: err});
  }
} catch (err) {
  // Logs the full message trail: "Second error: First error"
  console.log(fullMessage(err));
}

The output is similar to the standard VError behaviour of appending message with the cause.message, separating the two with a :.

Since Error Causes doesn't do this, fullMessage exist to mimic that behaviour.

It respects VError messages, it won't append any error message of their causes, though it will walk past the VError causes to see if there's a non-VError cause up the chain and then append that.

The reason to use this method is explained by VError:

The idea is that each layer in the stack annotates the error with a description of what it was doing. The end result is a message that explains what happened at each level.

If an inner error has a message ENOENT, stat '/nonexistent', an outer error wraps it and adds Can't perform X and maybe one more error wraps that and adds Can't start program, then fullMessage will join those three errors together when providing it with the outer most error and return Can't start program: Can't perform X: ENOENT, stat '/nonexistent' which provides details about both cause and effect as well as the connection between the two – each which on their own would be a lot harder to understand the impact of.

Note: fullMessage has protection against circular causes

fullStack

Gets a stack trace for the error + all its causes.

import {fullStack} from 'berrors/helpers';

try {
  /* Something that can break */
} catch (err) {
  console.log('We had a mishap:', fullStack(err));
}

The output is similar to VError.fullStack() but resolves causes in both Error Causes style, .cause, and VError style, .cause().

Note: fullStack has protection against circular causes

Output looks like:

Error: something really bad happened here
    at Object.<anonymous> (/examples/fullStack.js:5:12)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)
    at node.js:968:3
Caused by: Error: something bad happened
    at Object.<anonymous> (/examples/fullStack.js:3:12)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Function.Module.runMain (module.js:441:10)
    at startup (node.js:139:18)
    at node.js:968:3

Credits

This project is inspired and draws on the following projects:

  • spur-errors: Common error builder utility for Node.js. Contains common error types, and stack trace tracking to support more detailed error messages.
  • pony-cause: Ponyfill and helpers for the standardized Error Causes
  • error-class-utils: Utilities for error classes
  • error-custom-class: Create custom error classes