I went through the documentation of Express, and the part describing error handling is completely opaque to me.
I figured the app they're referring to is an instance createServer(), right? But I have no clue how to stop node.js from blowing up the application process when an exception occurs during handling a request.
I don't need anything fancy really; I just want to return a status of 500, plus an otherwise empty response, whenever there's an exception. The node process must not terminate just because there was an uncaught exception somewhere.
Is there a simple example of how to achieve this?
var express = require('express');
var http = require('http');
var app = express.createServer();
app.get('/', function(req, res){
console.log("debug", "calling")
var options = {
host: 'www.google.com',
port: 80,
path: "/"
};
http.get(options, function(response) {
response.on("data", function(chunk) {
console.log("data: " + chunk);
chunk.call(); // no such method; throws here
});
}).on('error', function(e) {
console.log("error connecting" + e.message);
});
});
app.configure(function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.listen(3000);
crashes the entire app, producing traceback
mypath/tst.js:16
chunk.call(); // no such method; throws here
^ TypeError: Object ... has no method 'call'
at IncomingMessage.<anonymous> (/Library/WebServer/Documents/discovery/tst.js:16:18)
at IncomingMessage.emit (events.js:67:17)
at HTTPParser.onBody (http.js:115:23)
at Socket.ondata (http.js:1150:24)
at TCP.onread (net.js:374:27)
If you really want to catch all exceptions and provide some handling other than exiting the Node.js process, you need to handle Node's uncaughtException event.
If you think about it, this is a Node thing, and not an Express thing, because if you throw an exception from some arbitrary piece of code, there's no guarantee Express can or will ever see it, or be in a position to trap it. (Why? Exceptions don't interact very well with asynchronous event-driven callbacky code that is the Node style. Exceptions travel up the call stack to find a catch() block that's in scope at the time the exception is thrown. If myFunction defers some work to a callback function that runs when some event happens, then return to the event loop, then when that callback function is invoked, it's invoked directly from the main event loop, and myFunction is no longer on the call stack; if this callback function throws an exception, even if myFunction has a try/catch block, it's not going to catch the exception.)
What this means in practice is that if you throw an exception and don't catch it yourself and you do so in a function that was directly called by Express, Express can catch the exception and call the error handler you've installed, assuming you've configured some piece of error-handling middleware like app.use(express.errorHandler()). But if you throw the same exception in a function that was called in response to an asynchronous event, Express won't be able to catch it. (The only way it could catch it is by listening for the global Node uncaughtException event, which would be a bad idea first because that's global and you might need to use it for other things, and second because Express will have no idea what request was associated with the exception.)
Here's an example. I add this snippet of route-handling code to an existing Express app:
app.get('/fail/sync', function(req, res) {
throw new Error('whoops');
});
app.get('/fail/async', function(req, res) {
process.nextTick(function() {
throw new Error('whoops');
});
});
Now if I visit http://localhost:3000/fail/sync in my browser, the browser dumps a call stack (showing express.errorHandler in action). If I visit http://localhost:3000/fail/async in my browser, however, the browser gets angry (Chrome shows a "No data received: Error 324, net::ERR_EMPTY_RESPONSE: The server closed the connection without sending any data" message), because the Node process has exited, showing a backtrace on stdout in the terminal where I invoked it.
To be able to catch asynchronous errors I use domain. With Express you can try this code:
function domainWrapper() {
return function (req, res, next) {
var reqDomain = domain.create();
reqDomain.add(req);
reqDomain.add(res);
res.on('close', function () {
reqDomain.dispose();
});
reqDomain.on('error', function (err) {
next(err);
});
reqDomain.run(next)
}
}
app.use(domainWrapper());
//all your other app.use
app.use(express.errorHandler());
This code will make your asynchronous error be captured and sent to your error handler. In this example I use the express.errorHandler, but it works with any handler.
For more information about domain: http://nodejs.org/api/domain.html
You can use the default error handler that express uses, which is actually connect error handler.
var app = require('express').createServer();
app.get('/', function(req, res){
throw new Error('Error thrown here!');
});
app.configure(function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.listen(3000);
Update
For your code, you actually need to capture the error and pass it to express like this
var express = require('express');
var http = require('http');
var app = express.createServer();
app.get('/', function (req, res, next) {
console.log("debug", "calling");
var options = {
host:'www.google.com',
port:80,
path:"/"
};
http.get(options,
function (response) {
response.on("data", function (chunk) {
try {
console.log("data: " + chunk);
chunk.call(); // no such method; throws here
}
catch (err) {
return next(err);
}
});
}).on('error', function (e) {
console.log("error connecting" + e.message);
});
});
app.configure(function () {
app.use(express.errorHandler({ dumpExceptions:true, showStack:true }));
});
app.listen(3000);
express 5.0.0-alpha.7 came out 27 days ago. With this very specific pre-release version, you can now finally reject a promise inside a request handler and it will be handled properly:
Middleware and handlers can now return promises and if the promise is rejected, next(err) will be called with err being the value of the rejection. (source)
For example:
app.get('/', async () => {
throw new Error();
});
app.use((err, req, res, next) => {
res.status(500).send('unexpected error :(');
});
However, only use this as a fallback. Proper error handling should still happen inside catch-phrases inside the request handlers themselves with proper error status codes.
If you don't catch an uncaught exception in your application, it will crash catastrophically, meaning the server process will exit with non-zero error code, and users will never see any response. If you don't have a process manager like pm2 installed, it will also remain dead. To avoid this, and to catch every possible kind of error like logic errors or programmer errors, you need to place the code in a try-catch block. However, there is a really simple solution that avoids having to have a try-catch block around every single controller function. This article explains how.
Simply install express-async-errors and put this on top of your app.js:
const express = require('express');
require('express-async-errors');
...
Now every possible error in a controller is automatically passed to express error handler, no matter if it's async or sync, even if you write things like null.test. You can define an error handler like the following:
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something broke!')
});
To make this more useful, you can even define your own error classes that inherits from Error, and throw it anywhere you want in your controllers, and it will be automatically caught by Express. Then check if (err instanceof MyCustomErrorClass) in your error handler to display custom messages in your 500 page.
Related
I upgraded to Express 4 and have the following problem with error handling.
Before I used to have the code in app.js — after all the possible routes I had
var routes = require('./routes')
app.use(routes.notfound)
app.use(routes.error)
app.use(routes.badrequest)
And then inside the /routes/index.js I had:
exports.notfound = function(req, res) {
res.status(404).format({
html: function() {
res.render('404')
},
json: function() {
res.send({
message: 'We did not find what you were looking for :(',
})
},
xml: function() {
res.write('<error>\n')
res.write(
' <message>We did not find what you were looking for :(</message>\n'
)
res.end('</error>\n')
},
text: function() {
res.send('We did not find what you were looking for :(\n')
},
})
}
Now when I call for 404 elsewhere in the app (not in app.js) using res.send(404) I get the right 404 code response but I don't get to the part where it selects whether it shows html or json or text.
How do I do that?
You need to handle error catching differently, here is one way to do so:
Create a middleware after all of your routes that will catch errors you pass to it, the callback would take in an extra parameter containing details about the error:
app.use((err, req, res, next) => {
// Handle the error here
});
Whenever you want to render an error, you can use next in your routes to pass it to this middleware, and pass extra information you can use to decide how to handle the error. There is a module called http-errors that can create objects like that for you. Here is an example route:
const createError = require('http-errors');
app.get('/posts', (req, res, next) => {
// Logic...
if(/* some condition */) {
next(createError(404));
}
});
This will pass the error object created to your error handling middleware, and from there you can choose how to handle it.
To extend this, and to make it work better with asynchronous code, you can wrap your router's callbacks with a function that will make sure exceptions that get thrown are passed over to the error handling middleware, this comes in handy when working with async and await:
// Wrapper function to forward errors from async scopes
const wrap = fn => (...args) => fn(...args).catch(args[2]);
app.get('/posts', wrap(async (req, res) => {
// Logic...
await controller.get('posts'); // Promise rejections will get forwarded to middleware
}));
This also lets you just throw the error object instead of calling next.
How can I properly end a middleware chain in case of error ?
In my code I have
if (someProblem) {
res.status(statuscode).json({message: 'oops'});
return;
}
Yet the midleware in the chain after that keeps getting called giving me the Can't set headers after they are senterror.
Middleware does not advance on its own. It only goes to the next step in the chain when you call next() so as long as you don't call next(), then the next step will not execute.
So, if you just send a response and return (without calling next()), then you should not have an issue.
The code you show in your question would not, all by itself, cause the error message you refer to so there must be more going on that you aren't showing us for that error to occur.
So you simply have to attach an errorHandler (which is a function that takes 4 parameters) before you create the actual server instance.
Anywhere in the app you can now simply create an error with var newError = new Error('my error message'); and pass it to the next callback with next(newError);
To stop the execution after the error has been thrown and to not send a response twice use return next (newError);
// example route
app.get('/test', function(req, res, next){
if (someProblem) {
var newError = new Error('oops');
return next(newError);
}
res.send('all OK - no errors');
});
// attach this errorHandler just before you create the server
app.use(function(error, req, res, next){
console.log(error);
res.status(500).json(error);
});
// this is where you create the server
var server = app.listen(4000, function(){
console.log('server started');
});
If you want your middleware to not go to the next step of the chain but to return the error you have to call next(error) callback.
This will call the error handler middleware where you can operate with the error and return it to the client if thats what you want.
To implement your custom error handler middleware you can use handleError(err, req, res, next)(error, req, res, next)
I'm experimenting with KrakenJS, trying to build an basic API to understand things.
One thing I'm unsure of is the middleware, specifically the 404/500 error handling.
"fileNotFound": {
"enabled": true,
"priority": 130,
"module": {
"name":"path:./lib/exceptions/404"
}
}
This catches any 404 errros, and then I handle that myself in my own configuration. However, why is this fileNotFound thrown? Where is serverError thrown for 500 errors?
I would like to define my own files for other exceptions like a 403, however how would I get this to trigger a middleware?
As #HeadCode mentioned, definitely read up on meddleware to understand how middleware gets loaded a bit better.
That said, we have to go over a few things in order to make what's happening more clear.
Handling 404s
First, let's go over how one would typically register a 404 handler in a plain old express app.
Typically you'd have the final middleware in your middleware continuation chain just assume that, if we made it that far without bailing, we simply cannot find the resource. Here's an example:
var express = require('express');
var app = express();
app.get('/firstRoute', function handler(req, res) { res.send('found me'); });
app.get('/secondRoute', function handler(req, res) { res.send('found me'); });
app.use(function notFoundHandler(req, res, next) {
res.status(404).send('Route Not Found');
});
app.listen(8000, function onListen() { console.log('listening on 8000...'); });
Since routes are resolved in the order they're added in Express 4, as long as your 404 handler is last you can be certain no other route matched.
This pattern is briefly described in the Express FAQs.
Handling 500s
Now let's move on to 500s.
Express has the concept of an error-handling middleware (also described on the Express site). An error handling middleware requires an arity of 4 (i.e., takes four arguments) and that's the only difference. They're only executed if an error is signaled which is done by passing an object into your next call. Easier explained in code:
var express = require('express');
var app = express();
app.get('/firstRoute', function handler(req, res) { res.send('found me'); });
app.get('/secondRoute', function handler(req, res) { throw new Error('oops'); });
app.use(function notFoundHandler(req, res, next) {
res.status(404).send('Route Not Found');
});
app.use(function errorHandler(err, req, res, next) {
res.status(500).send('Broken. :(');
});
app.listen(8000, function onListen() { console.log('listening on 8000...'); });
In the above example, errorHandler will only execute if either 1) another middleware or route handler throws an error or 2) we call next with an argument*, .e.g. next(new Error('oops')).
Go ahead and run that. if you visit /notFound or any random route, you'll correctly get a 404. If you visit /firstRoute you'll get found me, and if you go to secondRoute you'll get Broken. :(.
What about kraken?
Kraken—or more accurately, meddleware—just moves defining your middleware into your config. That little block of json you copied above is basically functionally equivalent to the following in a vanilla express app:
var fileNotFound = require('./lib/exceptions/404');
// ... app.use() everything with a priority lower than 130 ...
app.use(fileNotFound());
// ... app.use() everything with a priority *greater* than 130 ...
I just recently started working on an express.js based application, which also uses the pg module (https://github.com/brianc/node-postgres)
I also spent a significant amount of time, reading about node and express approach error handling, the benefits of properly designing middleware, etc. Yet, a recurring problem is still buzzing me without a solution.
Say, I have the following router method:
app.get("/:someThing/:someId", function(req, res, next) {
pgClient.query("some SQL query", function(err, data) {
if (err) { return next(err); } // some 500 handler will take it
if (data.rows.length == 0) {
next(); // send it over to a 404 handler
}
//finally, here we get the chance to do something with the data.
//and send it over via res.json or something else
});
});
If I've read correctly, this should be the proper way to do it. Yet, I bet you can also admt that it is too much of boilerplate to rewrite over and over ... and over again, even in the very same router method, in case we have multiple nested callbacks.
I've been asking myself what the best way to handle such a situation centrally would be. All of my ideas involve intercepting the pgClient.query method. In one, the query method will simply throw the error instead of passing it to the callback. In another, the call to the pgClient.query will send the router method's next to pgClient. Then the intercepted query method will know how to deal with the next being passed to it.
From what I know, throwing errors around is not really the appropriate way to get it to the 500 handlers. On another hand, passin next as an option to pgClient, gives such a low level a lot of knowledge about the the layers above, which based on my knowledge and experience, can lead to coupling, and is not very good either.
What do you suggest?
You can use connect-domain middleware. It works with connect and express and based on Doman API.
You need to add connect-domain middleware as first middleware in stack. Thats all. Now you can throw errors everywhere in your async code and they will be handled with domain middleware and passed to express error handler.
Simple example:
// Some async function that can throw error
var asyncFunction = function(callback) {
process.nextTick(function() {
if (Math.random() > 0.5) {
throw new Error('Some error');
}
callback();
});
};
var express = require('express');
var connectDomain = require('connect-domain');
var app = express();
app.use(connectDomain());
// We need to add router middleware before custom error handler
app.use(app.router);
// Common error handler (all errors will be passed here)
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
app.listen(3131);
// Simple route
app.get('/', function(req, res, next) {
asyncFunction(function() {
res.send(200, 'OK');
});
});
If I throw an Error, express renders it nicely using the connect errorHandler middleware.
exports.list = function(req, res){
throw new Error('asdf');
res.send("doesn't get here because Error is thrown synchronously");
};
And when I throw an Error within a promise it will be ignored (this makes sense to me).
exports.list = function(req, res){
Q = require('q');
Q.fcall(function(){
throw new Error('asdf');
});
res.send("we get here because our exception was thrown async");
};
However, If I throw an Error within a promise and call "done" node crashes, because the exception isn't caught by the middleware.
exports.list = function(req, res){
Q = require('q');
Q.fcall(function(){
throw new Error('asdf');
}).done();
res.send("This prints. done() must not be throwing.");
};
After running the above, node crashes with this output:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: asdf
at /path/to/demo/routes/user.js:9:11
So my conclusion is that done() isn't throwing the exception, but causes an exception to be thrown elsewhere. Is that right? Is there a way to accomplish what I'm trying - where errors in the promise will be handled by the middleware?
FYI: This hack will catch the exception at the top level, but it's outside of the realm of middleware, so doesn't suite my needs (to nicely render the error).
//in app.js #configure
process.on('uncaughtException', function(error) {
console.log('uncaught expection: ' + error);
})
Maybe you'll find connect-domain middleware useful for handling async errors. This middleware allows you to handle async errors like a regular errors.
var
connect = require('connect'),
connectDomain = require('connect-domain');
var app = connect()
.use(connectDomain())
.use(function(req, res){
process.nextTick(function() {
// This async error will be handled by connect-domain middleware
throw new Error('Async error');
res.end('Hello world!');
});
})
.use(function(err, req, res, next) {
res.end(err.message);
});
app.listen(3131);