├── .npmignore ├── .gitignore ├── test ├── mocha.opts ├── codes_test.js └── index_test.js ├── .travis.yml ├── codes.js ├── LICENSE ├── Makefile ├── package.json ├── index.js ├── codes.json ├── CHANGELOG.md └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | /*.tgz 2 | /.travis.yml 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /*.tgz 3 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive 2 | --check-leaks 3 | --require must 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "stable" 5 | - "0.11" 6 | - "0.12" 7 | - "4" 8 | 9 | notifications: 10 | email: ["andri@dot.ee"] 11 | -------------------------------------------------------------------------------- /codes.js: -------------------------------------------------------------------------------- 1 | var CODES = require("http").STATUS_CODES 2 | exports = module.exports = require("./codes.json") 3 | for (var code in CODES) if (!(code in exports)) exports[code] = CODES[code] 4 | -------------------------------------------------------------------------------- /test/codes_test.js: -------------------------------------------------------------------------------- 1 | var O = require("oolong") 2 | var isVersion = require("semver").satisfies.bind(null, process.version) 3 | var CODES_JSON = require("../codes.json") 4 | var STATUS_CODES = require("http").STATUS_CODES 5 | 6 | ;(isVersion(">= 4") ? describe : xdescribe)("codes.json", function() { 7 | // Test for superset rather than equivalence because future Node versions 8 | // might _add_ status codes. During the switch from Node 0.12 to Node v4 some 9 | // names _were_ changed. 10 | O.each(STATUS_CODES, function(name, code) { 11 | it("must have " + code + " equal " + name, function() { 12 | CODES_JSON[code].must.equal(name) 13 | }) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | StandardHttpError.js 2 | Copyright (C) 2015– Andri Möll 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU Affero General Public License as published by the Free 6 | Software Foundation, either version 3 of the License, or any later version. 7 | 8 | Additional permission under the GNU Affero GPL version 3 section 7: 9 | If you modify this Program, or any covered work, by linking or 10 | combining it with other code, such other code is not for that reason 11 | alone subject to any of the requirements of the GNU Affero GPL version 3. 12 | 13 | In summary: 14 | - You can use this program for no cost. 15 | - You can use this program for both personal and commercial reasons. 16 | - You do not have to share your own program's code which uses this program. 17 | - You have to share modifications (e.g bug-fixes) you've made to this program. 18 | 19 | For the full copy of the GNU Affero General Public License see: 20 | http://www.gnu.org/licenses. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NODE = node 2 | NODE_OPTS = 3 | TEST_OPTS = 4 | 5 | love: 6 | @echo "Feel like makin' love." 7 | 8 | test: 9 | @$(NODE) $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot $(TEST_OPTS) 10 | 11 | spec: 12 | @$(NODE) $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec $(TEST_OPTS) 13 | 14 | autotest: 15 | @$(NODE) $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot --watch $(TEST_OPTS) 16 | 17 | autospec: 18 | @$(NODE) $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec --watch $(TEST_OPTS) 19 | 20 | pack: 21 | @file=$$(npm pack); echo "$$file"; tar tf "$$file" 22 | 23 | constants: 24 | @$(NODE) -e '\ 25 | var ERRORS = require("./"); \ 26 | for (var name in ERRORS) console.log("`%d` | `%s`", ERRORS[name], name) \ 27 | ' 28 | 29 | codes.json: .FORCE 30 | $(NODE) -e 'console.log(JSON.stringify(require("http").STATUS_CODES, null, "\t"))' > "$@" 31 | 32 | publish: 33 | npm publish 34 | 35 | tag: 36 | git tag "v$$($(NODE) -e 'console.log(require("./package").version)')" 37 | 38 | clean: 39 | rm -f *.tgz 40 | npm prune --production 41 | 42 | .PHONY: love 43 | .PHONY: test spec autotest autospec 44 | .PHONY: pack publish tag 45 | .PHONY: clean 46 | .PHONY: .FORCE 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "standard-http-error", 3 | "version": "2.0.1", 4 | "description": "Standard HTTP error class. Proper serialization, no bloat. Extensible.", 5 | "keywords": [ 6 | "error", 7 | "errors", 8 | "http", 9 | "exception" 10 | ], 11 | "homepage": "https://github.com/moll/js-standard-http-error", 12 | "bugs": "https://github.com/moll/js-standard-http-error/issues", 13 | 14 | "author": { 15 | "name": "Andri Möll", 16 | "email": "andri@dot.ee", 17 | "url": "http://themoll.com" 18 | }, 19 | 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/moll/js-standard-http-error.git" 23 | }, 24 | 25 | "licenses": [{ 26 | "type": "LAGPL", 27 | "url": "https://github.com/moll/js-standard-http-error/blob/master/LICENSE" 28 | }], 29 | 30 | "main": "index.js", 31 | "scripts": {"test": "make test"}, 32 | 33 | "browser": { 34 | "./codes.js": "./codes.json" 35 | }, 36 | 37 | "dependencies": { 38 | "standard-error": ">= 1.1.0 < 2" 39 | }, 40 | 41 | "devDependencies": { 42 | "mocha": ">= 2.1.0 < 3", 43 | "must": ">= 0.12.0 < 0.13", 44 | "oolong": ">= 1.7.0 < 2", 45 | "http-codes": ">= 1.0.0 < 2", 46 | "semver": ">= 5 < 6" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | exports = module.exports = HttpError 2 | var StandardError = require("standard-error") 3 | var STATUS_CODE_TO_NAME = require("./codes") 4 | var STATUS_NAME_TO_CODE = exports 5 | 6 | function HttpError(code, msg, props) { 7 | if (typeof code == "string") code = STATUS_NAME_TO_CODE[code] 8 | if (typeof code != "number") throw new TypeError("Non-numeric HTTP code") 9 | if (typeof msg == "object" && msg != null) { props = msg; msg = null } 10 | StandardError.call(this, msg || STATUS_CODE_TO_NAME[code], props) 11 | this.code = code 12 | } 13 | 14 | HttpError.prototype = Object.create(StandardError.prototype, { 15 | constructor: {value: HttpError, configurable: true, writable: true} 16 | }) 17 | 18 | // Set name explicitly for when the code gets minified. 19 | HttpError.prototype.name = "HttpError" 20 | 21 | Object.defineProperties(HttpError.prototype, { 22 | statusCode: alias("code"), 23 | statusMessage: alias("message"), 24 | 25 | status: { 26 | configurable: true, 27 | get: function() { return this.code }, 28 | set: function(value) { 29 | Object.defineProperty(this, "status", { 30 | value: value, configurable: true, enumerable: true, writable: true 31 | }) 32 | } 33 | } 34 | }) 35 | 36 | HttpError.prototype.toString = function() { 37 | return this.name + ": " + this.code + " " + this.message 38 | } 39 | 40 | for (var code in STATUS_CODE_TO_NAME) { 41 | var name = STATUS_CODE_TO_NAME[code] 42 | exports[name.replace("'", "").replace(/[- ]/g, "_").toUpperCase()] = +code 43 | } 44 | 45 | function alias(name) { 46 | return { 47 | configurable: true, 48 | get: function() { return this[name] }, 49 | set: function(value) { return this[name] = value } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /codes.json: -------------------------------------------------------------------------------- 1 | { 2 | "100": "Continue", 3 | "101": "Switching Protocols", 4 | "102": "Processing", 5 | "200": "OK", 6 | "201": "Created", 7 | "202": "Accepted", 8 | "203": "Non-Authoritative Information", 9 | "204": "No Content", 10 | "205": "Reset Content", 11 | "206": "Partial Content", 12 | "207": "Multi-Status", 13 | "208": "Already Reported", 14 | "226": "IM Used", 15 | "300": "Multiple Choices", 16 | "301": "Moved Permanently", 17 | "302": "Found", 18 | "303": "See Other", 19 | "304": "Not Modified", 20 | "305": "Use Proxy", 21 | "307": "Temporary Redirect", 22 | "308": "Permanent Redirect", 23 | "400": "Bad Request", 24 | "401": "Unauthorized", 25 | "402": "Payment Required", 26 | "403": "Forbidden", 27 | "404": "Not Found", 28 | "405": "Method Not Allowed", 29 | "406": "Not Acceptable", 30 | "407": "Proxy Authentication Required", 31 | "408": "Request Timeout", 32 | "409": "Conflict", 33 | "410": "Gone", 34 | "411": "Length Required", 35 | "412": "Precondition Failed", 36 | "413": "Payload Too Large", 37 | "414": "URI Too Long", 38 | "415": "Unsupported Media Type", 39 | "416": "Range Not Satisfiable", 40 | "417": "Expectation Failed", 41 | "418": "I'm a teapot", 42 | "421": "Misdirected Request", 43 | "422": "Unprocessable Entity", 44 | "423": "Locked", 45 | "424": "Failed Dependency", 46 | "425": "Unordered Collection", 47 | "426": "Upgrade Required", 48 | "428": "Precondition Required", 49 | "429": "Too Many Requests", 50 | "431": "Request Header Fields Too Large", 51 | "500": "Internal Server Error", 52 | "501": "Not Implemented", 53 | "502": "Bad Gateway", 54 | "503": "Service Unavailable", 55 | "504": "Gateway Timeout", 56 | "505": "HTTP Version Not Supported", 57 | "506": "Variant Also Negotiates", 58 | "507": "Insufficient Storage", 59 | "508": "Loop Detected", 60 | "509": "Bandwidth Limit Exceeded", 61 | "510": "Not Extended", 62 | "511": "Network Authentication Required" 63 | } 64 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.1 (Sep 12, 2017) 2 | - Removes a trailing comma for old Internet Explorer compatibility. 3 | 4 | ## 2.0.0 (Oct 4, 2015) 5 | - Updates HTTP status codes and constants from Node v4: 6 | 7 | Code | Old Name | New Name 8 | ------|-----------------------------------|--------- 9 | `302` | `MOVED_TEMPORARILY` | `FOUND` 10 | `408` | `REQUEST_TIME_OUT` | `REQUEST_TIMEOUT` 11 | `413` | `REQUEST_ENTITY_TOO_LARGE` | `PAYLOAD_TOO_LARGE` 12 | `414` | `REQUEST_URI_TOO_LARGE` | `URI_TOO_LONG` 13 | `416` | `REQUESTED_RANGE_NOT_SATISFIABLE` | `RANGE_NOT_SATISFIABLE` 14 | `504` | `GATEWAY_TIME_OUT` | `GATEWAY_TIMEOUT` 15 | 16 | ## 1.2.0 (Oct 4, 2015) 17 | - Restores status code names and constants to pre Node v4 times (Node v0.12) for 18 | backwards compatibility. Will release StandardHttpError.js v2 briefly with 19 | Node v4's names and constants. 20 | 21 | - Adds new status codes and constants from Node v4: 22 | 23 | Code | Name 24 | ------|----- 25 | `208` | `ALREADY_REPORTED` 26 | `226` | `IM_USED` 27 | `421` | `MISDIRECTED_REQUEST` 28 | `508` | `LOOP_DETECTED` 29 | 30 | - Makes `StandardHttpError` inherit from `StandardError` so you could more 31 | easily differentiate between bugs (usually thrown as `TypeError`, 32 | `SyntaxError` et al.) from user-facing errors that you've intentionally 33 | thrown. If all of your custom errors inherit from `StandardError` 34 | ([StandardError.js](https://github.com/moll/js-standard-error)), this will 35 | come in handy. 36 | 37 | ```javascript 38 | var StandardError = require("standard-error") 39 | var HttpError = require("standard-http-error") 40 | 41 | function magic(n) { 42 | if (isNaN(n)) throw new TypeError("Bug! Should never be NaN!") 43 | if (n <= 0) throw new HttpError(422, "Think positively!") 44 | // ... 45 | } 46 | 47 | try { magic(42) } 48 | catch (ex) { 49 | if (ex instanceof StandardError) console.error("Uh-oh: " + ex.message) 50 | else throw ex 51 | } 52 | ``` 53 | 54 | Make sure your dependencies have been deduped (see `npm dedupe`) to ensure 55 | a single `StandardError` instance (needed for `instanceof` to work) across 56 | your whole app. 57 | 58 | ## 1.1.1 (Jun 4, 2015) 59 | - Sets HttpError's name as a property on its prototype for when the code gets 60 | minified and the constructor name is changed. 61 | 62 | ## 1.1.0 (May 21, 2015) 63 | - Adds support for use in the browser by adding a cached copy of status codes. 64 | For example, use StandardHttpError.js with 65 | [Browserify](https://github.com/substack/node-browserify). 66 | 67 | ## 1.0.1 (Feb 23, 2015) 68 | - Fixes subclassing example in README. 69 | 70 | ## 1.0.0 (Feb 22, 2015) 71 | - Keep calm and invent a standard. 72 | -------------------------------------------------------------------------------- /test/index_test.js: -------------------------------------------------------------------------------- 1 | var O = require("oolong") 2 | var HttpError = require("..") 3 | var isVersion = require("semver").satisfies.bind(null, process.version) 4 | var describeNodeV4 = isVersion(">= 4 < 5") ? describe : xdescribe 5 | 6 | function RemoteError(code, msg, props) { 7 | HttpError.call(this, code, msg, props) 8 | } 9 | 10 | RemoteError.prototype = Object.create(HttpError.prototype, { 11 | constructor: {value: RemoteError, configurable: true, writeable: true} 12 | }) 13 | 14 | describe("HttpError", function() { 15 | describe("new", function() { 16 | it("must be an instance of HttpError", function() { 17 | new HttpError(400).must.be.an.instanceof(HttpError) 18 | }) 19 | 20 | it("must set code", function() { 21 | new HttpError(404).code.must.equal(404) 22 | }) 23 | 24 | it("must throw TypeError given undefined code", function() { 25 | (function() { new HttpError(undefined) }).must.throw(TypeError, /HTTP/) 26 | }) 27 | 28 | it("must throw TypeError given null code", function() { 29 | (function() { new HttpError(null) }).must.throw(TypeError, /HTTP/) 30 | }) 31 | 32 | it("must set code from constant name", function() { 33 | new HttpError("NOT_FOUND").code.must.equal(404) 34 | }) 35 | 36 | it("must throw TypeError given unknown constant", function() { 37 | (function() { new HttpError("DUNNO") }).must.throw(TypeError, /HTTP/) 38 | }) 39 | 40 | it("must set message from code", function() { 41 | new HttpError(404).message.must.equal("Not Found") 42 | }) 43 | 44 | it("must set message from code given null", function() { 45 | new HttpError(404, null).message.must.equal("Not Found") 46 | }) 47 | 48 | it("must set message from constant name", function() { 49 | new HttpError("NOT_FOUND").message.must.equal("Not Found") 50 | }) 51 | 52 | it("must set message if given", function() { 53 | new HttpError(404, "Dunno").message.must.equal("Dunno") 54 | }) 55 | 56 | it("must set name to HttpError", function() { 57 | new HttpError(400).name.must.equal("HttpError") 58 | }) 59 | 60 | it("must set name to constructor's name", function() { 61 | new RemoteError(400).name.must.equal("RemoteError") 62 | }) 63 | 64 | it("must set code, message and properties", function() { 65 | var err = new RemoteError(404, "Dunno", {url: "/dunno"}) 66 | err.code.must.equal(404) 67 | err.message.must.equal("Dunno") 68 | err.url.must.equal("/dunno") 69 | }) 70 | 71 | it("must set code and properties", function() { 72 | var err = new RemoteError(404, {url: "/dunno"}) 73 | err.code.must.equal(404) 74 | err.message.must.equal("Not Found") 75 | err.url.must.equal("/dunno") 76 | }) 77 | 78 | it("must set code and properties given null message", function() { 79 | var err = new RemoteError(404, null, {url: "/dunno"}) 80 | err.code.must.equal(404) 81 | err.message.must.equal("Not Found") 82 | err.url.must.equal("/dunno") 83 | }) 84 | 85 | it("must set stack", function() { 86 | var stack = new HttpError(400).stack.split(/\n\s*/) 87 | stack[0].must.equal("HttpError: Bad Request") 88 | stack[1].must.include("index_test.js") 89 | }) 90 | 91 | it("must set stack from constructor", function() { 92 | var stack = new RemoteError(400).stack.split(/\n\s*/) 93 | stack[0].must.equal("RemoteError: Bad Request") 94 | stack[1].must.include("index_test.js") 95 | stack[2].must.not.include("index_test.js") 96 | }) 97 | }) 98 | 99 | describe(".prototype.statusCode", function() { 100 | it("must be an alias to code", function() { 101 | var err = new HttpError(404) 102 | err.statusCode.must.equal(404) 103 | err.statusCode = 500 104 | err.code.must.equal(500) 105 | }) 106 | 107 | it("must be non-enumerable", function() { 108 | new HttpError(412).must.have.nonenumerable("statusCode") 109 | }) 110 | }) 111 | 112 | describe(".prototype.statusMessage", function() { 113 | it("must be an alias to message", function() { 114 | var err = new HttpError(412, "Bad CSRF Token") 115 | err.statusMessage.must.equal("Bad CSRF Token") 116 | err.statusMessage = "Awful CSRF Token" 117 | err.message.must.equal("Awful CSRF Token") 118 | }) 119 | 120 | it("must be non-enumerable", function() { 121 | new HttpError(412).must.have.nonenumerable("statusMessage") 122 | }) 123 | }) 124 | 125 | describe(".prototype.status", function() { 126 | it("must be an alias to code", function() { 127 | new HttpError(404).status.must.equal(404) 128 | }) 129 | 130 | it("must be non-enumerable", function() { 131 | new HttpError(404).must.have.nonenumerable("status") 132 | }) 133 | 134 | it("must be overwritable", function() { 135 | var err = new HttpError(404) 136 | err.status = "OK" 137 | err.code.must.equal(404) 138 | 139 | err.status.must.equal("OK") 140 | var desc = Object.getOwnPropertyDescriptor(err, "status") 141 | desc.configurable.must.be.true() 142 | desc.writable.must.be.true() 143 | desc.enumerable.must.be.true() 144 | }) 145 | }) 146 | 147 | describe(".prototype.toString", function() { 148 | it("must return code and message", function() { 149 | new HttpError(404, "Dunno").toString().must.equal("HttpError: 404 Dunno") 150 | }) 151 | 152 | it("must use set name", function() { 153 | var err = new HttpError(404, "Dunno") 154 | err.name = "OtherError" 155 | err.toString().must.equal("OtherError: 404 Dunno") 156 | }) 157 | }) 158 | 159 | describe("HTTP status codes", function() { 160 | it("must have NOT_FOUND equal 404", function() { 161 | HttpError.must.have.property("NOT_FOUND", 404) 162 | }) 163 | 164 | it("must have INTERNAL_SERVER_ERROR equal 500", function() { 165 | HttpError.must.have.property("INTERNAL_SERVER_ERROR", 500) 166 | }) 167 | 168 | // Changed between Node v0.12 and Node v4. 169 | it("must have FOUND equal 302", function() { 170 | HttpError.must.have.property("FOUND", 302) 171 | new HttpError(302).message.must.equal("Found") 172 | }) 173 | 174 | describeNodeV4("when on Node v4", function() { 175 | var STATUS_NAMES = require("http-codes") 176 | 177 | // Fail safes: 178 | STATUS_NAMES.must.have.property("NOT_FOUND", 404) 179 | STATUS_NAMES.must.have.property("INTERNAL_SERVER_ERROR", 500) 180 | 181 | O.each(STATUS_NAMES, function(code, name) { 182 | it("must have " + name + " equal " + code, function() { 183 | HttpError[name].must.equal(code) 184 | }) 185 | }) 186 | }) 187 | }) 188 | }) 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | StandardHttpError.js 2 | ==================== 3 | [![NPM version][npm-badge]](https://www.npmjs.com/package/standard-http-error) 4 | [![Build status][travis-badge]](https://travis-ci.org/moll/js-standard-http-error) 5 | 6 | **StandardHttpError.js** is a very simple but useful **error class** for 7 | JavaScript and Node.js that represents HTTP errors. You can then detect it with 8 | `instanceof` in error handling middleware and act accordingly. Also works in the 9 | browser through [Browserify](http://browserify.org). 10 | 11 | You can use StandardHttpError.js with any error code you like, standardized or 12 | not. They don't have to exist beforehand, so if you're living on the cutting 13 | edge, feel free to use `new HttpError(451, "Unavailable For Legal Reasons")`. 14 | 15 | [npm-badge]: https://img.shields.io/npm/v/standard-http-error.svg 16 | [travis-badge]: https://travis-ci.org/moll/js-standard-http-error.png?branch=master 17 | 18 | 19 | Installing 20 | ---------- 21 | ```sh 22 | npm install standard-http-error 23 | ``` 24 | 25 | StandardHttpError.js follows [semantic versioning](http://semver.org/), so feel 26 | free to depend on its major version with something like `>= 1.0.0 < 2` 27 | (a.k.a `^1.0.0`). 28 | 29 | 30 | Using 31 | ----- 32 | ```javascript 33 | var HttpError = require("standard-http-error") 34 | throw new HttpError(404) 35 | ``` 36 | 37 | Your error handler will then receive an instance of `HttpError` along with the 38 | following enumerable properties: 39 | 40 | Property | Value 41 | ---------|------ 42 | name | `"HttpError"` 43 | code | `404` 44 | message | `"Not Found"` 45 | 46 | As always for errors, the non-enumerable `stack` property is there as well. 47 | 48 | For compatibility with Express or Koa's default request handler (the one that 49 | prints your errors out if you don't handle them), StandardHttpError.js also sets 50 | `status`, `statusCode` and `statusMessage` to be aliases of `code` and 51 | `message`. They're non-enumerable to not pollute serialization. 52 | 53 | ### Creating a new instance by error name 54 | StandardHttpError.js also supports passing a constant name instead of the error 55 | code. 56 | 57 | ```javascript 58 | new HttpError("NOT_FOUND") 59 | new HttpError("FORBIDDEN") 60 | ``` 61 | 62 | See below for a [list of error code names](#error-codes). 63 | 64 | ### Setting a custom message 65 | ```javascript 66 | new HttpError(412, "Bad CSRF Token") 67 | ``` 68 | 69 | The default "Precondition Failed" message that the error code 412 would've 70 | resulted in will then be replaced by "Bad CSRF Token". 71 | 72 | Note that status messages were always meant to be human readable, so it's 73 | perfect fine and even preferable to provide clarification in the status message. 74 | Try to stick to the capitalized form, though, as that will match the default 75 | HTTP status message style. 76 | 77 | ### Setting custom properties 78 | You can pass custom properties to be attached to the error instance as an 79 | object: 80 | 81 | ```javascript 82 | new HttpError(404, {url: req.url}) 83 | new HttpError(412, "Bad CSRF Token", {session: req.session}) 84 | ``` 85 | 86 | You can access the given `session` property then as `err.session`. 87 | 88 | 89 | ### Subclassing StandardHttpError 90 | If you wish to add your own functionality to StandardHttpError, subclass it: 91 | 92 | ```javascript 93 | var HttpError = require("standard-http-error") 94 | 95 | function RemoteError(res) { 96 | HttpError.call(this, res.statusCode, res.statusMessage) 97 | } 98 | 99 | RemoteError.prototype = Object.create(HttpError.prototype, { 100 | constructor: {value: RemoteError, configurable: true, writeable: true} 101 | }) 102 | ``` 103 | 104 | The [StandardError.js](https://github.com/moll/js-standard-error) library that 105 | StandardHttpError.js uses makes sure the `name` and `stack` properties of your 106 | new error class are set properly. 107 | 108 | If you don't want your new error class to directly inherit from 109 | `StandardHttpError`, feel free to leave the `RemoteError.prototype` line out. 110 | Everything will work as before except your `RemoteError` will no longer be an 111 | `instanceof` StandardHttpError.js. You might want to manually grab the 112 | `HttpError.prototype.toString` function then though, as that's useful for nice 113 | `String(err)` output. 114 | 115 | ### Switching based on error codes 116 | ```javascript 117 | switch (err.code) { 118 | case HttpError.UNAUTHORIZED: return void res.redirect("/signin") 119 | case HttpError.NOT_FOUND: return void res.render("404") 120 | case 451: return void res.redirect("/legal") 121 | default: return void res.render("500") 122 | } 123 | ``` 124 | 125 | ### Using with Express 126 | StandardHttpError.js comes very handy when used with Connect/Express's error 127 | handling functionality: 128 | 129 | ```javascript 130 | var HttpError = require("standard-http-error") 131 | var app = require("express")() 132 | 133 | app.get("/account", function(req, res, next) { 134 | if (req.account == null) throw new HttpError(401) 135 | if (req.account.budget == 0) throw new HttpError(402) 136 | // ... 137 | }) 138 | 139 | app.use(function(err, req, res, next) { 140 | if (!(err instanceof HttpError)) return void next(err) 141 | 142 | res.statusCode = err.code 143 | res.statusMessage = err.message 144 | res.render("error", {title: err.message}) 145 | }) 146 | ``` 147 | 148 | 149 | 150 | Error Codes 151 | ----------- 152 | StandardHttpError.js comes with a list of status message constants that you can 153 | use for comparison and in `switch` statements. 154 | 155 | ```javascript 156 | HttpError.NOT_FOUND // => 404 157 | ``` 158 | 159 | When running on Node.js, cached status codes and their names get merged with new 160 | codes from `Http.STATUS_CODES`. Existing status codes will not be changed 161 | without bumping StandardHttpError.js's major version number. That ensures 162 | consistent constants in Node and in the browser. 163 | 164 | Code | Name 165 | ------|----- 166 | `100` | `CONTINUE` 167 | `101` | `SWITCHING_PROTOCOLS` 168 | `102` | `PROCESSING` 169 | `200` | `OK` 170 | `201` | `CREATED` 171 | `202` | `ACCEPTED` 172 | `203` | `NON_AUTHORITATIVE_INFORMATION` 173 | `204` | `NO_CONTENT` 174 | `205` | `RESET_CONTENT` 175 | `206` | `PARTIAL_CONTENT` 176 | `207` | `MULTI_STATUS` 177 | `208` | `ALREADY_REPORTED` 178 | `226` | `IM_USED` 179 | `300` | `MULTIPLE_CHOICES` 180 | `301` | `MOVED_PERMANENTLY` 181 | `302` | `FOUND` 182 | `303` | `SEE_OTHER` 183 | `304` | `NOT_MODIFIED` 184 | `305` | `USE_PROXY` 185 | `307` | `TEMPORARY_REDIRECT` 186 | `308` | `PERMANENT_REDIRECT` 187 | `400` | `BAD_REQUEST` 188 | `401` | `UNAUTHORIZED` 189 | `402` | `PAYMENT_REQUIRED` 190 | `403` | `FORBIDDEN` 191 | `404` | `NOT_FOUND` 192 | `405` | `METHOD_NOT_ALLOWED` 193 | `406` | `NOT_ACCEPTABLE` 194 | `407` | `PROXY_AUTHENTICATION_REQUIRED` 195 | `408` | `REQUEST_TIMEOUT` 196 | `409` | `CONFLICT` 197 | `410` | `GONE` 198 | `411` | `LENGTH_REQUIRED` 199 | `412` | `PRECONDITION_FAILED` 200 | `413` | `PAYLOAD_TOO_LARGE` 201 | `414` | `URI_TOO_LONG` 202 | `415` | `UNSUPPORTED_MEDIA_TYPE` 203 | `416` | `RANGE_NOT_SATISFIABLE` 204 | `417` | `EXPECTATION_FAILED` 205 | `418` | `IM_A_TEAPOT` 206 | `421` | `MISDIRECTED_REQUEST` 207 | `422` | `UNPROCESSABLE_ENTITY` 208 | `423` | `LOCKED` 209 | `424` | `FAILED_DEPENDENCY` 210 | `425` | `UNORDERED_COLLECTION` 211 | `426` | `UPGRADE_REQUIRED` 212 | `428` | `PRECONDITION_REQUIRED` 213 | `429` | `TOO_MANY_REQUESTS` 214 | `431` | `REQUEST_HEADER_FIELDS_TOO_LARGE` 215 | `500` | `INTERNAL_SERVER_ERROR` 216 | `501` | `NOT_IMPLEMENTED` 217 | `502` | `BAD_GATEWAY` 218 | `503` | `SERVICE_UNAVAILABLE` 219 | `504` | `GATEWAY_TIMEOUT` 220 | `505` | `HTTP_VERSION_NOT_SUPPORTED` 221 | `506` | `VARIANT_ALSO_NEGOTIATES` 222 | `507` | `INSUFFICIENT_STORAGE` 223 | `508` | `LOOP_DETECTED` 224 | `509` | `BANDWIDTH_LIMIT_EXCEEDED` 225 | `510` | `NOT_EXTENDED` 226 | `511` | `NETWORK_AUTHENTICATION_REQUIRED` 227 | 228 | 229 | License 230 | ------- 231 | StandardHttpError.js is released under a *Lesser GNU Affero General Public 232 | License*, which in summary means: 233 | 234 | - You **can** use this program for **no cost**. 235 | - You **can** use this program for **both personal and commercial reasons**. 236 | - You **do not have to share your own program's code** which uses this program. 237 | - You **have to share modifications** (e.g. bug-fixes) you've made to this 238 | program. 239 | 240 | For more convoluted language, see the `LICENSE` file. 241 | 242 | 243 | About 244 | ----- 245 | **[Andri Möll][moll]** typed this and the code. 246 | [Monday Calendar][monday] supported the engineering work. 247 | 248 | If you find StandardHttpError.js needs improving, please don't hesitate to type 249 | to me now at [andri@dot.ee][email] or [create an issue online][issues]. 250 | 251 | [email]: mailto:andri@dot.ee 252 | [issues]: https://github.com/moll/js-standard-http-error/issues 253 | [moll]: http://themoll.com 254 | [monday]: https://mondayapp.com 255 | --------------------------------------------------------------------------------