I am currently working on node.js + express + mongoDB project. I am trying to handle error that occurs when data cannot be received from database. I am simulating this by terminating mongod process in console and calling .get in Postman. Sadly instead of getting an error in Postman I only get Unhandled Promise Rejection in console. I read a lot of posts about error handling and implemented it according to this guide: https://expressjs.com/en/guide/error-handling.html. I would be grateful for any idea of how can I fix this.
The code:
Printing all courses:
router.get("/", async (req, res, next) => {
try {
const courses = await Course.find().sort("dishName");
res.send(courses);
} catch (ex) {
next(ex);
}
});
error.js:
module.exports = function (err, res, req, next) {
res.status(500).send(`500 Error`);
};
index.js
const error = require(`./middleware/error`);
app.use(error);
app.use(error) is placed as the last app.use
There is a minor mistake in your code. The order of the req and res parameters in the error handler function should not be changed.
// Error.js
module.exports = function (err, req, res, next) {
res.status(500).send(`500 Error`);
};
Environment: node.js, Express, express-session package.
Background: I was testing an Express error handler and I got an error that I wasn't expecting.
The following simplified route throws an error.
TypeError: Cannot read property 'userValues' of undefined
exports.errorHandler = wrapAsync(async function(error, req, res, next) {
let loggedIn = req.session.userValues ? true : false;
res.render('error', { loggedIn });
});
However when I remove the error parameter it works without an error as I had anticipated.
exports.errorHandler = wrapAsync(async function(req, res, next) {
let loggedIn = req.session.userValues ? true : false;
res.render('error', { loggedIn });
});
Why might this be?
The basic pattern in the second example works in several dozen routes that don't include the error parameter.
You could use something like this. And it will only get executed whenever there is an ERROR 500 unless you passed the ERROR 404 to this one using next() function, if you handled all the errors correctly you should be able to make an ERROR 500 and this should be able to catch that ERROR.
const errorHandler = require("./your-file");
...
... every other route even error 404 handler
...
app.use(errorHandler);
What do I mean by using next() for ERROR 404
If you have used express-generator then you should already have this piece of code:
// catch 404 and forward to error handle
app.use((req, res, next) => {
next('not found');
});
The end file should looks something like this now if you use this approach:
...
... all previous routes
...
// catch 404 and forward to error handle
app.use((req, res, next) => {
next('not found');
});
// handle all other error
app.use(errorHandler);
Hope this helps
app.use(function (req, res, next) {
throw new Error('critical');
})
makes Express server to catch a critical error and output it, while I want it to crash.
Adding an error handler doesn't replace the default handler.
How can Express error handling be disabled for critical errors?
If you want your server to crash in the event of a critical error, you can define an error-handling middleware. This is done by defining a function with 4 parameters, the first being the error. This will be called when an error is thrown. You can check the error and determine if it's critical, and if so, call process.exit.
const app = require('express')()
app.use('/', (req, res) => {
throw new Error('critical')
})
app.use((err, req, res, next) => {
if (err.message === 'critical') {
process.exit(1)
} else {
// carry on listening
}
})
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 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.