I have API to deal with post request as follow(simplified):
myFunc(req: express.Request, res: express.Response, next){
let err = 'err detected!';
//validateSometing() returns a boolean value, true if validation pass false otherwise
if(!validateSomething()){
res.status(500).json(err);
return next(err);
}
//more code...logic if validation pass
}
I would like to know if return next(err); or return; is required to stop the function flow after sending the status and related err back to client. In other words, does res.status(500).json(err); stops the function flow?
Thanks!
next() is a middleware function in the application’s request-response cycle. You must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
res.json(), res.send() is a express function used to send response to the client application . In other words it used this functions used to build your HTTP Reponse.
return keyword returns from your function, thus ending its execution. This means that any lines of code after it will not be executed.
Note : Both next() and res.send() will not end your function from execution. Where adding a return will stop function execution after triggering the callback.
Use return is to ensure that the execution stops after triggering the callback. In some circumstances, you may want to use res.send and then do other stuff.
Example :
app.use((req, res, next) => {
console.log('This is a middleware')
next()
console.log('This is first-half middleware')
})
app.use((req, res, next) => {
console.log('This is second middleware')
next()
})
app.use((req, res, next) => {
console.log('This is third middleware')
next()
})
Your output will be:
This is a middleware
This is second middleware
This is third middleware
This is first-half middleware
That is, it runs the code below next() after all middleware function finished.
However, if you use return next(), it will jump out the callback immediately and the code below return next() in the callback will be unreachable.
Related
In the following test code req.query is checked to see if its value is name=cat. If this is not true next() fires off the next piece of middleware. This is accomplished without including a return statement after next() and it works as expected.
app.get('/test', (req, res, next) => {
if (req.query.name != 'cat') {
next();
}
res.send('it was cat');
});
app.get('/test', (req, res) => {
res.send('it was not cat');
});
However when I change res.send to res.sendFile in the second piece of middleware the behavior is completely different.
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
After the change res.send('it was cat'); in the first piece of middleware fires every time, regardless of the value of name. In addition the second piece of middleware never fires.
This can easily be fixed by adding a return after next() in the first piece of middleware. The behavior becomes predictable again.
app.get('/test', (req, res, next) => {
if (req.query.name != 'cat') {
next();
return;
}
res.send('it was cat');
});
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
Why does this happen with and without the return statement? A return is needed when I use res.sendFile but not when I use res.send. I'm sure I'm missing something obvious but I don't understand the pattern.
This is accomplished without including a return statement after next() and it works as expected.
No, it looks like it works as expected, but it doesn't. For instance, if you retrieve /test without query parameter, or with a query parameter that doesn't equal name=cat, Express will log an error:
Error: Can't set headers after they are sent.
I cannot reproduce your second example; for me, it always returns "it was cat" (in the example without return).
The common rule for any Express handler/middleware is: it should either end the response itself (by calling res.end, res.send, res.render, res.sendFile, etc) or pass the request along using next. In your case, without return, you're doing both. The result of that will be, practically speaking, undefined.
When your listener performs res.send('it was not cat') the entire processing of the request takes place without hitting the trampoline. The next() call never completes until after res.send('it was not cat') finishes. So by the time the code for send('it was the cat') is reached, the response is already sent.
But when your listener performs res.sendFile() the processing of the response is asynchronous. The file has to be read into memory, which is non-blocking I/O in Node. The callback for that I/O is placed into the event queue, and then processing continues. So the next() call returns and then send('it was the cat') executes. All this is before the file is even read into memory. Later when the file is read, the code dutifully attempts to send the response, but it's too late.
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);
}
}
};
The following question is asked in order to better understand from your answers how to think "Async" when developing on Node.js
I have the following code:
router.get('/', function(req, res, next) {
... //Definition of rules and paramsObj
//Validation that returns a promise
Indicative.validate(rules,paramsObj)
.then(function(success){
//we passed the validation. start processing the request
//ProcessRequest has async calls but when all async functions are over, it sets paramObj.someVal with a calculated value.
processRequest(paramsObj,next);
//My problem is here. at this point paramsObj.someVal is not set yet. and therefore the response to the user will be incorrect.
res.send(paramsObj.someVal);
}).catch(function(err){
console.log(err);
next(err);
}).done();
}
I wish to understand how to better think "async" while i need to wait with the response to the user until all async functions are over.
My question is how to execute res.send(paramObj.someVal) only after the paramObj.someVal is set by some async methods in processRequest(paramsObj,next);
If you need to wait on the result of processRequest for paramsObj.someVal to be set then ultimately you need to handle that callback
router.get('/', function(req, res, next) {
... //Definition of rules and paramsObj
//Validation that returns a promise
Indicative.validate(rules,paramsObj)
.then(function(success){
//we passed the validation. start processing the request
//ProcessRequest has async calls but when all async functions are over, it sets paramObj.someVal with a calculated value.
processRequest(paramsObj, function(err) {
if (!err && !paramsObj.someVal) {
// raise a custom error if the value is not set
err = new Error('Value not set');
}
if (err) {
next(err);
} else {
res.send(paramsObj.someVal);
}
});
}).catch(function(err){
console.log(err);
next(err);
}).done();
}
Assuming the second argument to processRequest() is a completion callback, you can pass your own function for that callback and do your res.send() in that custom callback like this:
router.get('/', function(req, res, next) {
... //Definition of rules and paramsObj
//Validation that returns a promise
Indicative.validate(rules,paramsObj)
.then(function(success){
//we passed the validation. start processing the request
//ProcessRequest has async calls but when all async functions are over, it sets paramObj.someVal with a calculated value.
processRequest(paramsObj,function() {
res.send(paramsObj.someVal);
});
}).catch(function(err){
console.log(err);
next(err);
}).done();
}
Since you do res.send(...), I assume you don't want to actually call next() in that code path.
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.
I have a weird situation...
I have a Express.js, Node.js and Mongoose web app.
One of the routes has a mongoose callback that calls respond.send(...). However, because there is nothing else after the callback, I suspect it goes to next() route automatically.
Ex:
//getItem
app.get('/api/ItemOne', isUserValid, routes.getItem);
//getAnotherItem
app.get('/api/getAnotherItem', isUserValid, routes.getAnotherItem);
//Routes
exports.getItem = function (req, res) {
//console.log ('In getItem');
getItem .findOne({ some_id : some_id}, function(err, getItem ){
//console.log ('In getItem callback');
res.send({
itemName : getItem .itemName,
itemValue : getItem .itemValue;
});
})
});
exports.getAnotherItem = function (req, res) {
//console.log ('In getAnotherItem');
getAnotherItem.findOne({ some_id : some_id}, function(err, getAnotherItemRet){
//console.log ('In getAnotherItem Callback');
res.send({
itemName : getAnotherItemRet.itemName,
itemValue : getAnotherItemRet.itemValue;
});
})
});
I get the following sequence of messages on the console...
In getItem
In getAnotherItem
In getItem callback
In getAnotherItem callback
I am assuming because the route does not finish, it calls next() automatically.
Q: How do I prevent the second route from being called automatically.
Try reversing req and res:
// wrong
exports.getItem = function (res, req) {
// right
exports.getItem = function (req, res) {
(and same for getAnotherItem).
In order to understand why you get messages in that order, we need to know what url are you calling to generate that message.
In any case though, "/api/getItem" does not call "/api/getAnotherItem" for two reasons:
if you call next in "/api/getItem", it will call the next matching route, in this case, it will match either a route on "/api", "/" or nothing at all. the next() function basically calls the route's "parent".
Next() must be called explicitly, if you do not return an answer, express will simply wait indefinitely until res.send is called (which is how it can handle async functions).
It's possible you have some kind of middleware (I.E. used with app.use and not app.get) which could call something like that, but most likely, you are calling both urls at the same time somehow.