diff --git a/README.md b/README.md index 4694b38..b7266f0 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,34 @@ app.use('/api', api); ### .use(errorHandler([options])) -Currently no options. +Can optionally pass an object of options: + +- `showStack` -- Boolean, will include the stack trace in response sent to client for errors. The default behavior when no option is set is to show stack traces when `process.env.NODE_ENV !== 'production'`. Setting `showStack` option will override the default behavior. You can check yourself what environment the code is running in, and set the option accordingly: + +```js +var errorHandler = require("api-error-handler"); + +var api = new express.Router(); +api.get("/users/:userid", function(req, res, next) {}); + +function shouldShowStack() { + var environment = process.env.NODE_ENV; + switch (environment) { + // no stack for production or testing environment + case "production": + case "testing": + return false; + // include stack in dev or when NODE_ENV is not set + case "dev": + default: + return true; + } +} + +api.use(errorHandler({ showStack: shouldShowStack() })); + +app.use("/api", api); +``` ### Errors diff --git a/index.js b/index.js index 2bf606a..96bb9ab 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,20 @@ var statuses = require('statuses'); -var production = process.env.NODE_ENV === 'production'; +var isProduction = process.env.NODE_ENV === 'production'; + +module.exports = function (options) { + var opts = options || {} + + if (typeof opts.showStack === 'undefined') { + // if showStack is not set, or undefined, set it to false when in production + // otherwise, if not set and not in production, set it to true + opts.showStack = !isProduction + } + if (typeof opts.showStack !== 'boolean') { + throw new Error('Expected boolean value for showStack option') + } -module.exports = function () { return function apiErrorHandler(err, req, res, next) { var status = err.status || err.statusCode || 500; if (status < 400) status = 500; @@ -13,9 +24,7 @@ module.exports = function () { status: status }; - // show the stacktrace when not in production - // TODO: make this an option - if (!production) body.stack = err.stack; + if (opts.showStack) body.stack = err.stack; // internal server errors if (status >= 500) { diff --git a/test/dev.test.js b/test/dev.test.js new file mode 100644 index 0000000..31a53b9 --- /dev/null +++ b/test/dev.test.js @@ -0,0 +1,71 @@ +var path = require('path') +var error = require('http-errors'); +var request = require('supertest'); +var express = require('express'); +var assert = require('assert'); + +var handler; + +describe('Dev behavior', function() { + before(function() { + // reset the require cache for this module, so we can reset NODE_ENV + delete require.cache[path.join(__dirname, '../index.js')] + process.env.NODE_ENV = 'dev' + handler = require('..'); + }) + it('NODE_ENV !== production', function() { + assert.notStrictEqual(process.env.NODE_ENV, 'production') + }) + it('shows stack by default when not in production', function(done) { +var app = express(); + app.use(function (req, res, next) { + next(error(401)); + }); + app.use(handler()); + + request(app.listen()) + .get('/') + .end(function (err, res) { + assert.ifError(err); + + var body = res.body; + assert.notStrictEqual(body.stack, undefined); + done() + }) + }) + it('hides stack when showStack is false', function(done) { +var app = express(); + app.use(function (req, res, next) { + next(error(401)); + }); + app.use(handler({ showStack: false})); + + request(app.listen()) + .get('/') + .end(function (err, res) { + assert.ifError(err); + + var body = res.body; + assert.equal(body.stack, undefined); + done() + }) + }) + it('shows stack when showStack is true', function(done) { + var originalENV = process.env.NODE_ENV +var app = express(); + app.use(function (req, res, next) { + next(error(401)); + }); + app.use(handler({showStack: true})); + + request(app.listen()) + .get('/') + .end(function (err, res) { + assert.ifError(err); + + var body = res.body; + assert.notStrictEqual(body.stack, undefined); + done() + }) + }) +}) diff --git a/test/production.test.js b/test/production.test.js new file mode 100644 index 0000000..ebb8856 --- /dev/null +++ b/test/production.test.js @@ -0,0 +1,72 @@ +var path = require('path') +var error = require('http-errors'); +var request = require('supertest'); +var express = require('express'); +var assert = require('assert'); + +var handler + +describe('Production behavior', function() { + + before(function() { + // reset the require cache for this module, so we can reset NODE_ENV + delete require.cache[path.join(__dirname, '../index.js')] + process.env.NODE_ENV = 'production' + handler = require('..'); + }) + it('NODE_ENV === production', function() { + assert.equal(process.env.NODE_ENV, 'production') + }) + it('hides stack by default in production', function(done) { +var app = express(); + app.use(function (req, res, next) { + next(error(401)); + }); + app.use(handler()); + + request(app.listen()) + .get('/') + .end(function (err, res) { + assert.ifError(err); + + var body = res.body; + assert.equal(body.stack, undefined); + done() + }) + }) + it('hides stack when showStack is false', function(done) { +var app = express(); + app.use(function (req, res, next) { + next(error(401)); + }); + app.use(handler({ showStack: false})); + + request(app.listen()) + .get('/') + .end(function (err, res) { + assert.ifError(err); + + var body = res.body; + assert.equal(body.stack, undefined); + done() + }) + }) + it('shows stack in production when showStack is true', function(done) { +var app = express(); + app.use(function (req, res, next) { + next(error(401)); + }); + app.use(handler({showStack: true})); + + request(app.listen()) + .get('/') + .end(function (err, res) { + assert.ifError(err); + + var body = res.body; + assert.notStrictEqual(body.stack, undefined); + assert.strictEqual(process.env.NODE_ENV, 'production') + done() + }) + }) +}) diff --git a/test/test.js b/test/test.js index 4eb1f93..3afce93 100644 --- a/test/test.js +++ b/test/test.js @@ -50,5 +50,18 @@ describe('API Error Handler', function () { assert.equal(body.code, 'b'); done(); }) - }) + }) + it('throws if showStack is passed with a non boolean value', function() { + var app = express(); + assert.throws(function() { + app.use(handler({showStack: null})) + }) + assert.throws(function() { + app.use(handler({showStack: function() {return}})) + }) + assert.throws(function() { + app.use(handler({showStack: 'dev'})) + }) + + }) })