I'm trying to figure out from the documentation how to properly deal with errors (such as a bad template) in the optional callback to res.render in Express.
I am calling it pretty much identically to the example in the docs and using it in certain situations to append extra data to the rendered output.
res.render('template', undefined, (err, html) => {
if (err) // then what?
var processed = process(html)
res.send(processed)
})
The documentation says:
callback, a callback function. If provided, the method returns both the possible error and rendered string, but does not perform an automated response. When an error occurs, the method invokes next(err) internally.
But it seems like the only way to get proper behavior is if I can next(err) myself within the callback. Can someone tell me what the docs are trying to say here when they say next(err) will be called automatically?
Expressjs has a very good documentation on error handling. So, what you can do is throw an error if err exists:
res.render('template', undefined, (err, html) => {
if (err) throw new Error('Something went wrong in render');
var processed = process(html)
res.send(processed)
});
Then define a middleware that handles your error. You define it last after other app.use() and routes calls. A basic error handling middleware looks something like the following:
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
It is imperative this middleware is error free. This is because this is the last bit of code that will catch any error that is passed down from the rest of your code defined above it.
Related
I have been looking through some code online for building a React to-do app that uses an Express backend. The link to the website is here, and I came across this part of the code:
app.get("/todos", async (req, res, next) => {
try {
const todos = await db.Todo.find({});
return success(res, todos);
} catch (err) {
next({ status: 400, message: "failed to get todos" });
}
});
I know that the next function is a function that passes the operation of the current middleware function that it is in to the next middleware function of the same route. However, sources online just use the simple "next()" function, but this code has a value, an object, that is passed into the next function.
What does this mean?
this code has a value, an object, that is passed into the next function. What does this mean?
Ans: This means that you are passing an object as a parameter to the next middleware function.
app.use((err, req, res, next) => {
return res.status(err.status || 400).json({
status: err.status || 400,
message: err.message || "there was an error processing request"
});
});
Here err parameter is the object that you have passed.
Hope this helps
It seems to be a naming convention in Node.js, used to control the next matching route.
This stuff is frequently found, also very handy, and mostly used in access checks or wildcard routes. (/user/:id)
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in. If not, redirect to login.
if (!store.getters.isLoggedIn) {
next({
path: `/${ViewName.UserLogin}`,
query: { redirect: to.fullPath }
});
} else {
next();
}
}
From the express documentation:
Starting with Express 5, middleware functions that return a Promise will call next(value) when they reject or throw an error. next will be called with either the rejected value or the thrown Error.
So it seems to me like the value inside the next() function is the return value that is sent to the next callback. Often you don't want to send a custom value and just go to the next middleware function, however in this case they apparently wanted to set the error message inside the next() function and thus override any default values.
Hope this helps
I'm starting out w/ NodeJS and Express. Coming from the other popular scripting languages and C++ background, asynchronously calling DB functions is a bit foreign. I've sorted out a pattern, but I'm still curious about catching exceptions. Below is my basic pattern.
var callback = function(req, res) {
// do stuff
connection.query(queryString, function(err,result){
if (err) throw err;
// process results.
};
};
var express = require('express');
var app = express();
app.get('/', callback);
app.listen(3000,function() {
console.log('listening');
};
Generally I have a lot of endpoints and callbacks. I'm a bit lost on where I set up ta try/catch block to catch errors thrown in the callback though. I've looked around for some suggestions, but a lot of them seem to be on the web framework (if any) being used.
When you throw in an asynchronous callback, the exception just goes back to the internals of the database event handler and there is NO way for you to ever catch or handle that exception. So, basically it does no good at all. It will just cause you to abort the handling of that request and you will never send a response on that request.
Basically, you have several choices for how to handle the error. You can handle it completely right in each endpoint and send some sort of error response.
Send Response right at each point of error
app.get('/', function(req, res) {
// do stuff
connection.query(queryString, function(err,result){
if (err) return res.status(500).send(someErrorResponse);
// process results.
};
});
Forward on to centralized error handler
Or, you can forward the error on to a centralized error handler by calling next(err):
app.get('/', function(req, res, next) {
// do stuff
connection.query(queryString, function(err,result){
// if error, forward it on to our centralized error handler
if (err) return next(err);
// process results.
};
});
// centralized error handler - note how it has four parameters
app.use(function(err, req, res, next) {
// formulate an error response here
console.log(err);
res.status(500).send(someErrorMessage)
});
See Nodejs handle unsupported URLs and request types for more info on the ways to have generalized error handlers in Express.
Use promises to collect errors within each route
If you are using more involved sequences of asynchronous operations where you may have more than one async operation sequenced together, then it does get to be a pain to handle errors at every single async operation. This is where using promises with all your async operations more easily allows all the errors to percolate up to one .catch() statement at the top level of each route. You don't say what database you're using, but here's an idea what that looks like. The general idea is that you can write your code so that all promise rejections (e.g. errors) will propagate up to one central .catch() in each route handler and you can then call next(err) from that .catch(), sending the error to your centralized error handler. Here's how that looks for a recent version of Mongoose (you didn't say which database you were using) with one database operation.
app.get('/', function(req, res, next) {
// do stuff
connection.query(queryString).exec().then(function(result){
// process results.
}).catch(next);
});
// centralized error handler - note how it has four parameters
app.use(function(err, req, res, next) {
// formulate an error response here
console.log(err);
res.status(500).send(someErrorMessage)
});
And, here's what it looks like if you have more than one operation:
app.get('/', function(req, res, next) {
// do stuff
connection.query(queryString).exec().then(function(result){
// process results, then make another query
// return the promise from this second operaton so both results
// and error are chained to the first promise
return connection.query(...).exec();
}).then(function(result) {
// process chained result
}).catch(next);
});
// centralized error handler - note how it has four parameters
app.use(function(err, req, res, next) {
// formulate an error response here
console.log(err);
res.status(500).send(someErrorMessage)
});
Since ES6 built in support for promises and ES7 will add support for async/await for asynchronous operations (which is based on promises) and all significant libraries that offer asynchronous operations have added or are adding support for promises, it is clear that promises are the future of the language for managing asynchronous operations. That would be my strong recommendation.
You should never, ever throw an error like that! :) The reason is that at some point your whole node app will just stop working, because of some db query failed. This should be handled instead of just die.
And because this is a route handler - handles specific url that the user is getting (for example /), there should be some output. You can always show a page with status 500 and a nice design, if there was such an error that you cannot handle or you might have your internal state messed up.
So basically just act as nothing happened - return respones of any kind, or even render a page, but provide information that something went wrong.
Also, a common scenario is something like what Alon Oz presented. All routes in express are actually a middleware functions, that are called one after another. If the route does not match the requested one, the function just skips and the next one is called. You can manually control that. The actual pattern of the router is this:
app.get('/', function(req, res, next) {
// you can have the request
// you can send response like res.send('hello')
// OR you can skip this function using NEXT
});
The actual signature of next is next(err). So if you call it without any arguments, it will just skip to the next middleware. If you call it with an argument, it will skip all regular functions and go to the last ones in the stack, or more specifically the ones that handle errors. They are like the regular ones, but taking four arguments instead of three:
app.use(function (err, req, res, next) { });
It's very important to understand that this function will be called if you call next with an argument. Throwing an error won't do any good! Of course if none of your routes match the specific criteria (url) the final one will in the call will be called, so you can still handle the "not found" error.
This is a common scenario that you will use:
// development error handler, will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
debug('ERROR [ip: %s]:: dev env -> ', req.ip, err); // I'm using debug library - very helpful
res.status(err.status || 500);
res.render('deverr', { // I render custom template with the whole stack beautifully displayed
errMessage: err.message,
error: err
});
});
}
// production error handler, no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('pages/error', { // custom error page with nice design and a message
errMessage: err.message,
error: {}
});
});
Hope that helps! :)
Since you are using express, it has its own way to handle exceptions,
defined like this:
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
app.use(clientErrorHandler)
For more info:
https://expressjs.com/en/guide/error-handling.html
There are most commonly three major types of errors that we need to take into account.
Promise failures (Any failures that come up during async/await or result of a promise in then/catch)
In order to handle promise failures, as suggested in the strong loop document or node js 2018 best practices, it's important to have a common function that can handle it.
// app.js file
app.get('/:id', async (req,res,next) => {
if(!req.params.id) {
return res.status(412).send('enter a valid user id');
}
try {
const results = await UserDAL(id);
} catch(e) {
next(e);
}
}
// common error middleware defined in middleware/error.js
module.exports = function (err,req,res,next) {
logger.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
return res.status(500).send('something failed.');
};
Unhandled Rejections
process.on('unhandledRejection', e => {
// do something
});
Unhandled exceptions
process.on('uncaughtException', e => {
// do something
});
If you see a lot of try/ catch blocks in your express methods you can abstract that to a separate async function like below:
module.exports = function asyncMiddleWare(handler) {
return async (req,res,next) => {
try {
await handler(req,res)
} catch(e) {
next(e);
}
}
};
I can't seem to wrap my head around how to properly handle errors.
The basic 404, is no problem (simply set header 404 and render 'not found' page). But let's say for example:
You find a user by id, but the user doesn't exist. I suppose for this you set the header-status to 500. But how do you redirect the page back (or simply assign a redirect page) and set a flashmessage?
In most tutorials I usually find the following:
model.SignUp.forge({id: req.params.id}).fetch({withRelated: ['usermeta']}).then(function(user) {
res.render('admin/pages/users/details', {title: 'Signups', error: false, details: user});
}).catch(function(err) {
res.status(500).json({error: true, data: {message: err.message}});
});
You simply catch the problem whenever an error occurs. I also come across this sometimes:
transporter.sendMail(mailOptions, function(err) {
if(err) {
req.flash('error', 'blablabla');
res.redirect('back');
}
});
In the first case you return a json file but no redirect or render. In the second part no status has been provided.
What practices do you guys implement?
I'm a huge fan of central error handling in my express apps. How does this work? Well, I have a library of HTTP error objects that all have a 'status' property on them. All my route handlers and middeware return a callback with one of those error objects depending on what happened, and do not call res.send (or any other res.* method) if there was an error. I then have an error handling middleware (or more than one, if I it's getting to be complex) that decides if I want to do a redirect or just send the response code, or whatever depending on the needs of the app.
Taking your example:
app.post('/signup', function(req, res, next){
model.SignUp.forge({id: req.params.id}).fetch({withRelated: ['usermeta']}).then(function(user) {
res.render('admin/pages/users/details', {title: 'Signups', error: false, details: user});
}).catch(function(err) {
return next(new HttpServerError(err));
});
}
an HttpServerError has a status of 500, and so I have at least a 'catch all' error handling middleware that looks like this (in the case of a json api):
app.use(function(err, req, res, next){
console.log(err.stack);
res.status(err.status).send({message: err.clientMessage});
});
You can also do multiple handlers, and render or redirect based on the state of the request (e.g. accepts headers or type of error).
For example, in a traditional web app, I might use the name of the error to figure out what template to render, and I might redirect to a login page if it's a 403 error.
For sake of completeness, here's an example HttpServerError:
'use strict';
const util = require('util');
function HttpServerError(message){
this.message = message;
this.clientMessage = 'Dangit! Something went wrong on the server!';
this.status = 500;
Error.captureStackTrace(this, NotFoundError);
}
util.inherits(HttpServerError, Error);
HttpServerError.prototype.name = 'HttpServerError';
module.exports = HttpServerError;
I'm trying to add Bugsnag to my Node Restify service. We have a ton of routes already and such so I'm trying not to add Bugsnag calls all over our code base and I'm also trying to do something global so there's never a mistake where a dev forgets to add the error reporting.
Conceptually I want after any res.send() to get the status code. If the statusCode is >=400 i want to notify Bugsnag by calling Bugsnag.notify. I already check for errors everywhere so no errors ever show up to the clients (browsers, phones, etc) but they do get sent, for example, res.send(401, { message: 'You dont have permission to do that' }) which I'd like to be able to hook into and pass who tried to do that, what route, etc. Problem is I can't get the after event to fire at all:
server.on('after', function (req, res, route, error) {
console.log('AFTER')
});
I think I misunderstand what after is for. It's at the top of my index.js before any routes or other middleware (server.use) is defined.
My general code structure looks something like:
server.post('/foo', function (req, res, next) {
FooPolicy.create(req, function (err) {
if (err) return res.send(err.code, err.data);
FooController.create(req.params, function (response) {
res.send(response.code, response.data)
next();
});
});
});
FooPolicy == checking permissions
FooController == actually creating the model/data
The issue is that the after event is currently treated like any other handler. That means that if you don't call next in every code path, the after event will never be emitted.
In the meantime, adding a next call will cause your after event handler to fire.
if (err) {
res.send(err.code, err.data);
next();
return;
}
In nodejs express to handle exceptions , check for err in callbacks as :
if(err!==null){
next(new Error ('Erro Message'));
}
Which in turn calls the express's error handler middleware .
app.use(function(err, req, res, next){
if(!err) return next();
console.log('<-------Error Occured ----->');
res.send(500, JSON.stringify(err, ['stack', 'message']));
});
But to call invoke next(err) , I'm forced to pass around the reference for next across all the callback methods through all the layers . I find this a messy aproach . Is there a better way to handle exceptions and send a proper response using events or domains .
You should always delegate the error in the routes / controllers to the error handler by calling next (so you can just deal with them in one place instead of having them scattered throughout your application).
Here's an example:
app.get('/', function(req, res, next) {
db.findUser(req.params.userId, function(err, uid) {
if (err) { return next(err); }
/* ... */
});
});
/* Your custom error handler */
app.use(function(err, req, res, next) {
// always log the error here
// send different response based on content type
res.format({
'text/plain': function(){
res.status(500).send('500 - Internal Server Error');
},
'text/html': function(){
res.status(500).send('<h1>Internal Server Error</h1>');
},
'application/json': function(){
res.send({ error: 'internal_error' });
}
});
});
Note: You don't have to check for the err param in the error handler because it will always be present.
Also very important: always do return next(err); because you don't want the success code to be executed.
Both your code samples were flawed: in the first one you didn't use return next(err) and in the second one you have used return next(err), so code that followed shouldn't be handling the error (because it will never get there in case there's an error), but instead it should have been the 'success' code.
The error pages example from Express showsthe canonical way of handling errors:
https://github.com/visionmedia/express/blob/master/examples/error-pages/index.js
// error-handling middleware, take the same form
// as regular middleware, however they require an
// arity of 4, aka the signature (err, req, res, next).
// when connect has an error, it will invoke ONLY error-handling
// middleware.
// If we were to next() here any remaining non-error-handling
// middleware would then be executed, or if we next(err) to
// continue passing the error, only error-handling middleware
// would remain being executed, however here
// we simply respond with an error page.