Skip to content

Instantly share code, notes, and snippets.

@moleike
Forked from justmoon/custom-error.js
Last active December 22, 2019 21:13
Show Gist options
  • Save moleike/e7ddf35e5d08a75da023372a9f76d446 to your computer and use it in GitHub Desktop.
Save moleike/e7ddf35e5d08a75da023372a9f76d446 to your computer and use it in GitHub Desktop.
HTTP Error classes in Node.js
'use strict';
const statusCodes = require('http').STATUS_CODES;
function createError(code, name) {
return function(message) {
Error.captureStackTrace(this, this.constructor);
this.name = name;
this.message = message;
this.statusCode = code;
}
}
Object.keys(statusCodes)
.filter(code => code >= 400)
.forEach(code => {
let name = statusCodes[code]
.replace(/\W/g, '')
.concat('Error')
exports[name] = createError(Number(code), name);
require('util').inherits(exports[name], Error);
})

4xx/5xx errors with their statusCode in it.

Usage

var BadRequestError = require('./http-errors').BadRequestError;

function doSomethingBad() {
  throw new BadRequestError('It went bad!');
}

try {
  doSomethingBad();
} catch (err) {
  assert.strictEqual(err.statusCode, 400);
}

Features

  • 4xx/5xx status as errors classes - Status text is the class name, e.g. UnauthorizedError, NotFoundError
  • Status code property - Error handlers can use the status code to send responses.
  • Correct stack trace - no extra stack frames, no double capturing of the stack trace

Anti-patterns

These are some things that I've seen in other proposed solutions that you should avoid.

  • Error.call(this) - creates another error object (wasting a bunch of time) and doesn't touch this at all
  • Error.captureStackTrace(this, arguments.callee); - works, but arguments.callee is deprecated, so don't use it
  • this.stack = (new Error).stack - this... I don't even...
// Mini test suite for our 4xx/5xx error
var assert = require('assert');
var BadRequestError = require('./http-errors').BadRequestError
function doSomethingBad() {
throw new BadRequestError('It went bad!');
}
try {
doSomethingBad();
} catch (err) {
// The name property should be set to the error's name
assert(err.name = 'BadRequestError');
// The error should be an instance of its class
assert(err instanceof BadRequestError);
// The error should be an instance of builtin Error
assert(err instanceof Error);
// The error should be recognized by Node.js' util#isError
assert(require('util').isError(err));
// The error should have recorded a stack
assert(err.stack);
// toString should return the default error message formatting
assert.strictEqual(err.toString(), 'BadRequestError: It went bad!');
// The stack should start with the default error message formatting
assert.strictEqual(err.stack.split('\n')[0], 'BadRequestError: It went bad!');
// The first stack frame should be the function where the error was thrown.
assert.strictEqual(err.stack.split('\n')[1].indexOf('doSomethingBad'), 7);
// The extra property should have been set
assert.strictEqual(err.statusCode, 400);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment