Wrapping thrown errors so they don't crash the node webserver (node-mongodb-native)-Collection of common programming errors
Simplified code… but the basic scenario is I’m doing a findOne query with Mongo to lookup a user, but if the user doesn’t exist it throws an error that crashes the entire webserver. Can someone point me in the right direction for correctly wrapping these errors so they don’t bring everything down?
The Route:
server.get('/v1/user/:token', function(req,res){
console.log("user endpoint hit");
var user = users.findOne({token:req.params.token}, function(err,user){
if (user) {
res.json({token:user.token,credits:user.credits,subscribed:user.subscribed,searches:user.searches});
} else {
console.log("DB error in lookup user");
throw new DBError("Error looking up user in get endpoint");
}
});
});
The DBError declaration:
function DBError(msg) {
this.name = "DBError";
console.log("DBError " + msg);
Error.call(this,msg);
Error.captureStackTrace(this, arguments.callee);
}
And here is the chunk that handles errors:
server.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.send(404,{error: "404 Not Found"});
}
else if (err instanceof DBError) {
res.send(400, {error: "Database error"});
} else {
res.send(500,{error:"500 internal error"});
}
});
Now when I run my unit tests here is the stacktrace, which then ends the server process (not ideal!):
user endpoint hit
DB error in lookup user
DBError Error looking up user in get endpoint
/Users/msencenb/Development/nodeProjects/reversePhoneLookup/server/app/node_modules/mongodb/lib/mongodb/connection/server.js:563
throw err;
^
[object Object]
-
In general you just don’t throw errors. Errors are supposed to shut down the process/worker when something goes terribly wrong. I wouldn’t consider not finding a user in your code above to be something that went terribly wrong, but sure depending on your specific case that might be something that’s not supposed to ever happen.
When you need to handle errors though, the most widespread way [citation needed] is to pass it as the first argument in the callback (as you can see in the callback for
findOne()
). In express it’s done using the third parameter in a middleware, known asnext
, and an error handler. For example like this:Your route:
server.get('/v1/user/:token', function(req,res,next){ console.log("user endpoint hit"); var user = users.findOne({token:req.params.token}, function(err,user){ if (user) { res.json({token:user.token,credits:user.credits,subscribed:user.subscribed,searches:user.searches}); } else { next(new DBError("Error looking up user in get endpoint")); } }); });
Specify an error handler where you use your middleware:
app.use(function(err, req, res, next){ // This will handle all errors sent through next() console.error(err.stack); res.send(500, 'Something broke!'); });
Also you might want to have a look at domains in node. That page also touches how to properly respond to errors.