1.0.0-beta.1 • Published 6 years ago

@restify-ts/errors v1.0.0-beta.1

Weekly downloads
1
License
MIT
Repository
github
Last release
6 years ago

Build Status

@restify-ts/errors

A collection of HTTP and REST Error constructors.

The constructors can be used to new up Error objects with default status codes set.

The module ships with the following HttpErrors:

  • 400 BadRequestError
  • 401 UnauthorizedError
  • 402 PaymentRequiredError
  • 403 ForbiddenError
  • 404 NotFoundError
  • 405 MethodNotAllowedError
  • 406 NotAcceptableError
  • 407 ProxyAuthenticationRequiredError
  • 408 RequestTimeoutError
  • 409 ConflictError
  • 410 GoneError
  • 411 LengthRequiredError
  • 412 PreconditionFailedError
  • 413 RequestEntityTooLargeError
  • 414 RequesturiTooLargeError
  • 415 UnsupportedMediaTypeError
  • 416 RangeNotSatisfiableError
  • 416 RequestedRangeNotSatisfiableError
  • 417 ExpectationFailedError
  • 418 ImATeapotError
  • 422 UnprocessableEntityError
  • 423 LockedError
  • 424 FailedDependencyError
  • 425 UnorderedCollectionError
  • 426 UpgradeRequiredError
  • 428 PreconditionRequiredError
  • 429 TooManyRequestsError
  • 431 RequestHeaderFieldsTooLargeError
  • 500 InternalServerError
  • 501 NotImplementedError
  • 502 BadGatewayError
  • 503 ServiceUnavailableError
  • 504 GatewayTimeoutError
  • 505 HttpVersionNotSupportedError
  • 506 VariantAlsoNegotiatesError
  • 507 InsufficientStorageError
  • 509 BandwidthLimitExceededError
  • 510 NotExtendedError
  • 511 NetworkAuthenticationRequiredError

and the following RestErrors:

  • 400 BadDigestError
  • 405 BadMethodError
  • 500 InternalError
  • 409 InvalidArgumentError
  • 400 InvalidContentError
  • 401 InvalidCredentialsError
  • 400 InvalidHeaderError
  • 400 InvalidVersionError
  • 409 MissingParameterError
  • 403 NotAuthorizedError
  • 412 PreconditionFailedError
  • 400 RequestExpiredError
  • 429 RequestThrottledError
  • 404 ResourceNotFoundError
  • 406 WrongAcceptError

Some of the status codes overlap, since applications can choose the most applicable error type and status code for a given scenario. Should your given scenario require something more customized, the Error objects can be customized with an options object.

Getting Started

Install the module with: npm install @restify-ts/errors

Usage

Creating Errors

With @restify-ts/core you can use @restify-ts/errors like this:

import { Server, Request, Response } from '@restify-ts/core';
import { BadRequestError } from '@restify-ts/errors';

class Controller
{
  run(req: Request, res: Response)
  {
    if( !req.query.foo )
    {
      return res.send( new BadRequestError );
    }

    res.send(200, 'ok!');
  }
}

const server = new Server;

server.get('/foo', Controller, 'run');

Checking Error types

You can easily do instance checks against the Error objects:

class Controller
{
  redirectIfErr(req: Request, res: Response)
  {
    const err = req.data.error;
    
    if(err)
    {
      if(err instanceof InternalServerError)
      {
        res.send(err);
      }
      else if(err instanceof NotFoundError)
      {
        res.redirect('/notfound');
      }
    }
  }
}

Rendering Errors

All Error objects in this module are created with a body property. restify supports 'rendering' Errors as a response using this property. You can pass Errors to res.send and the error will be rendered out as JSON:

class Controller
{
  render(req: Request, res: Response)
  {
    res.send( new errors.InternalServerError );
  }
}

@restify-ts/core will render an application/json response with an http 500 { message: '' }

Customizing Errors

If you'd like to change the status code or message of a built-in Error, you can pass an options object to the constructor:

class Controller
{
  render(req: Request, res: Response)
  {
    const myErr = new errors.InvalidVersionError
    ({
      statusCode: 409,
      message: 'Version not supported with current query params'
    });

    res.send(myErr);
  }
}

Even though InvalidVersionError has a built-in status code of 400, it has been customized with a 409 status code. Restify-ts will now render an application/json response with an http 409:

{
  message: 'Version not supported with current query params'
}

Passing in prior errors (causes)

Like WError, all constructors accept an Error object as the first argument to build rich Error objects and stack traces. Assume a previous file lookup failed and an error was passed on:

class Controller
{
  wrapError(req, res)
  {
    if(req.error)
    {
      const myErr = new errors.InternalServerError(req.error, 'bad times!');
      return res.send(myErr);
    }
  }
}

This will allow Error objects to maintain context from previous errors, giving you full visibility into what caused an underlying issue:

console.log(myErr.message);
// => 'bad times!'

console.log(myErr.toString());
// => InternalServerError: bad times!; caused by Error: file lookup failed!

// if you're using Bunyan, you'll get rich stack traces:
bunyanLogger.info(myErr);

InternalServerError: bad times!
    at Object.<anonymous> (/Users/restify/test.js:30:16)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3
Caused by: Error: file lookup failed!
    at Object.<anonymous> (/Users/restify/test.js:29:15)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

Bunyan support

Since errors created via @restify-ts/errors inherit from VError, you'll get out of the box support via bunyan's standard serializers. If you are using the context property, you can use the serializer shipped with @restify-ts/errors:

import * as bunyan from 'bunyan';
import * as restifyErrors from '@restify-ts/errors';

const log = bunyan.createLogger
({
  name: 'myLogger',
  serializers: { err: restifyErrors.bunyanSerializer }
});

const err = new restifyErrors.InternalServerError
({
  message: 'cannot service this request!',
  context: { foo: 'bar', bar: 1 }
});

log.error(err, 'oh noes');
[2016-08-31T22:27:13.117Z] ERROR: log/51633 on laptop: oh noes (err.code=InternalServer)
    InternalServerError: cannot service this request! (foo="bar", bar=1)
        at Object.<anonymous> (/restify/test.js:11:11)
        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:974:3

You can, of course, combine this with the standard set of serializers that bunyan ships with.

VError support

This serializer also comes with support for VError's new info property:

const err = new VError
(
  {
    name: 'BoomError',
    info: { foo: 'bar', baz: 1 }
  }
  ,'something bad happened!'
);

log.error(err, 'oh noes');
[2016-08-31T22:21:35.900Z] ERROR: log/50874 on laptop: oh noes
    BoomError: something bad happened! (foo="bar", baz=1)
        at Object.<anonymous> (/restify/test.js:11:11)
        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:974:3

VError's MultiError is also supported:

const underlyingErr = new Error('boom');

const multiErr = new verror.MultiError
([
    new Error('boom')
    ,new restifyErrors.InternalServerError
    (
      underlyingErr
      ,{
        message: 'wrapped',
        context: { foo: 'bar', baz: 1 }
      }
    )
]);

log.error(multiErr, 'oh noes');
[2016-08-31T22:48:43.244Z] ERROR: logger/55311 on laptop: oh noes
    MultiError 1 of 2: Error: boom
        at Object.<anonymous> (/restify/test.js:16:5)
        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:974:3
    MultiError 2 of 2: InternalServerError: wrapped (foo="bar", baz=1)
        at Object.<anonymous> (/restify/test.js:17:5)
        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:974:3
    Caused by: Error: boom
        at Object.<anonymous> (/restify/test.js:14:21)
        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:974:3

For more information about building rich errors, check out VError.

Subclassing Errors

You can also create your own Error subclasses by using the provided makeConstructor() method. Making a new subclass will add the constructor to the existing exports object:

errors.makeConstructor('ExecutionError', {
    statusCode: 406,
    failureType: 'motion'
});

const myErr = new errors.ExecutionError('bad joystick input!');

console.log(myErr instanceof ExecutionError);
// => true

console.log(myErr.message);
// => 'ExecutionError: bad joystick input!'

console.log(myErr.failureType);
// => 'motion'

console.log(myErr.statusCode);
// => 406

console.log(myErr.stack);

ExecutionError: bad joystick input!
    at Object.<anonymous> (/Users/restify/test.js:30:16)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

Custom errors are subclassed from RestError, so you get all the built-in goodness of HttpError/RestError. The constructor returned to you accepts all the same signatures accepted by HttpError/RestError.

API

All error constructors are variadic and accept the following signatures:

constructor(message ?: string, ...sprintfArgs: any[]);
constructor(cause: Error, message ?: string, ...sprintfArgs: any[]);
constructor(options: HttpErrorOptions, message ?: string, ...sprintfArgs: any[]);

All VError and WError signatures are also supported, including extsprintf.

You can pass in a message like a regular error:

  • message {String} - an error message

Or pass in an options object for more customization:

  • options.message {String} - an error message string
  • options.statusCode {Number} - an http status code
  • options.restCode {Number} - a description code for your Error. This is used by restify to render an error when it is directly passed to res.send(). By default, it is the name of your error constructor (e.g., the restCode for a BadDigestError is BadDigest).
  • options.context {Object} - object of contextual properties relevant to the creation of the error, i.e., the url of a failed http request

In all signatures, you can optionally pass in an Error as the first argument, which will cause WError to use it as a prior cause:

  • priorErr {Error} - an Error object

Returns: {Error} an Error object

IMPORTANT: If a printf style signature is used, the Error message will prefer that over options.message.

makeConstructor(name , defaults)

Creates a custom Error constructor, adds it to the existing exports object.

  • name {String} - the name of your Error
  • defaults {Object} - an object of default values that will added to the prototype. It is possible to override the default toString() and toJSON() methods.

Returns: {void}

makeErrFromCode(name , args...)

Create an Error object using an http status code. This uses http module's STATUS_CODES to do the status code lookup. Thus, this convenience method is useful only for creating HttpErrors, and not RestErrors.

  • statusCode {Number} - an http status code
  • args - arguments to be passed on to the constructor

Returns: {Object} an Error object

License

  • Copyright (c) 2015 Alex Liu
  • Copyright (c) 2017 Костя Третяк

Licensed under the MIT license.