Error Handling in Strongloop Loopback - javascript

I know the difference between a callback and a middleware next() function.
If a write a custom remote-method in Loopback, I can easily send errors in callback e.g callback(error,null) but in remote hooks or observers whenever I send error in next() function e.g
var err = new Error('This is error');
next(err)
it always says that Internal server error but it does not tell me what error is.
In order to view error I have to login to server and view logs.
Please tell me how can I send error as a response in next() function so that the on frontend I know what error has occurred.

Maybe use a middleware to hook in:
app.use( function(err,req,res){
res.json(err);
});
(This needs to he the last middleware defined...)

basically you can define callbacks with err and result.
For example in loopback,
if I have a model call "Action" you can simply send err or result to front end using json.
app.get('/your/api/call', function (req, res, next) {
var getTeam = function (cb) {
app.models.Team.find({}, function (err, teams) {
if (err) {
cb(err);
} else {
cb(null, teams);
}
});
};
async.waterfall([
getTeam
], function (err, team, role) {
if (err){
res.send(err); //send error to front end
} else {
res.send(team); //send result to front end
}
});
});
This approach can use with "app.use" function in root level also.

Related

Error: Can't set headers after they are sent - Correctly handling an error in an express controller

I have an issue with the below code. When an error is hit, the if statement is been hit and res.send is been called. However, the code then continues onto the res.json call below. This results in the headers error.
Is using an else statement the only correct way to do this? I would assume that calling res.send would break from the function.
exports.new = function (req, res) {
let team = new Team();
team.name = req.body.name;
team.description = req.body.description;
team.members = [];
// save the team and check for errors
team.save(function (err) {
if (err) {
res.status(400).send(err);
}
res.status(200).json({
message: 'Team successfully created',
team: team
});
});
};
I have also tried using req, res, next as the function parameters and using the following method:
if (err) {
res.status(400);
next(err);
}
res.status(200).json({
message: 'Team successfully created',
team: team
});
Whilst this doesn't throw an error, it still proceeds to call the final res.status.json (resulting in a 200, not a 400 response)
You can't send response after res.end is called, which is done internally by express when you call res.send function
In your case just add return when there's an error
if (err) {
return res.status(400).send(err);
}
Because you don't return in that error case where you send status, so that code bellow just executes after anyway.
You need to write: return res.status(400).send(err);
or just use if else
Your assumption is incorrect about res.send() breaking, it will only do that if you return. Call return on res.send() in case of an error so that it does not continue. If you get an error it will set the status to 400 (in a header), then the error in the body, but because it does not return it then tries to set the status to 200 (in the header) causing the error you are seeing.
// save the team and check for errors
team.save(function (err) {
if (err) {
// return here so that it will not continue!
return res.status(400).send(err);
}
// return here if there is no error!
return res.status(200).json({
message: 'Team successfully created',
team: team
});
});
Express chains middlewares together using function(req, res, next) and it is possible to return results via res.send() in multiple middlewares. The next middleware in the chain is called when you return next(), so if you wanted to return additional data in another middleware on success for example, would not return res.send() and instead call return next() after the res.send().
The error that you are getting is because the line of code
res.status(200).json({
message: 'Team successfully created',
team: team
});
is being called whether it gets error or not . In case of the error the response header is already being sent in this code by the express
if (err) {
res.status(400).send(err);
}
So when the express tries the code below it gets the error that you expressed . The answer is to use the answer provided by Rami Jarrar
// save the team and check for errors
team.save(function (err) {
if (err) {
res.status(400).send(err);
}
res.status(200).json({
message: 'Team successfully created',
team: team
});
});
Please try with this:
team.save(function (err) {
if (err) {
return res.status(400).send(err); // here i have added a return
}
res.status(200).json({
message: 'Team successfully created',
team: team
});
}
The problem is when we send the response to client the tcp connection will be closed and sending data on the same response object again will throw an error which you are getting in current code.

ExpressJS doesn't wait for my promise

I'm making a search-page on my server. When the endpoint is reached and the user waits for the search function to return the results and render the page Express falls through to the 404 handler instead, and I get the following error when I suppose the render function is called:
Error: Can't set headers after they are sent.
What am I doing wrong?
router.get("/", async (req, res) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if(s.indexOf(",") > -1){
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": {tags: {$all: searchedTags}, _forSale: true}
};
const results = await Search.search(options).then(result => result).catch(err => {
throw err;
});
//This res.render -call is called after the 404 splat-route.
return res.render("partial/search.pug", {user: user, search: {
query: queryString,
results: results
}});
//If I'd use res.send for debugging, it is instead called before the splat-route, like the following:
return res.send(results);
} catch(err) {
next(err);
}
});
module.exports = router;
I register the router:
const search = require("./search.js");
app.use("/search", search);
Followed by the 404 splat-route:
app.get("*", async (req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
To clarify:
My question is how can I make the res.render function get called just as the res.send function?
UPDATE [2017-10-05]:
I continued with another part of the site, a similar endpoint, and discovered that sending the result provided by the promise worked as expected if using res.send but not res.render. Using res.render the 404-handler kicked in again. Can this be a bug in Express?
This happens if you attempt to write to res after it is sent, so you must be calling additional code after res.render() or you already responded before calling that.
change it to return res.render(...) so it exits the functions, otherwise it will continue through the function and hit other res.render()s etc.
Something is up with that error handler also. I will update my post in a few mins with tips (on phone). It should probably have (req, res, next) and call return next(err) and pass it to your error handling middleware.
Here is the pattern I like to use in async/await Express:
// these routes occur in the order I show them
app.get('/route', async (req, res, next) => {
try {
const data = 'asdf'
const payload = await something(data)
.then((result) => createPayload(result))
// remember, if you throw anywhere in try block, it will send to catch block
// const something = willFail().catch((error) => {
// throw 'Custom error message:' + error.message
// })
// return from the route so nothing else is fired
return res.render('route', { payload })
} catch (e) {
// fire down to error middleware
return next(e)
}
})
// SPLAT
app.get('*', async (req, res, next) => {
// if no matching routes, return 404
return res.status(404).render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if err !== null, this middleware fires
// it has a 4th input param "err"
res.status(500).render('error/500')
// and do whatever else after...
throw err
})
Note: next() callback called without param is treated as no error, and proceeds to the next middleware. If anything is passed in, it will fire the error middleware with the param as the value of err in the error handling middleware. You can use this technique in routes and other middlewares, as long as the error middleware comes last. Mind your use of return with res.send/render() to prevent double setting headers.
NEW:
Something looks a little bit off with that .then() having a callback in it. I don't see logically where err would come from since the value of the resolved promise goes into the .then() function as result. At this point, it is suspect and should be removed or refactored if possible. This part here:
try {
let results = [];
await Search.search(options).then(result => {
results = result;
}, err => {
throw err;
});
console.log("res.render");
return res.render("partial/search.pug", {user: user, search: {
query: string,
results: results
}});
} catch(err) {
next(err);
}
First, here is about what I would expect to see with async/await syntax:
router.get("/", async (req, res, next) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if (s.indexOf(",") > -1) {
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": { tags: { $all: searchedTags }, _forSale: true }
};
// If a promise is ever rejected inside a try block,
// it passes the error to the catch block.
// If you handle it properly there, you avoid unhandled promise rejections.
// Since, we have async in the route function, we can use await
// we assign the value of Search.search(options) to results.
// It will not proceed to the render statement
// until the entire promise chain is resolved.
// hence, then(data => { return data }) energizes `results`
const results = await Search.search(options)
.then(data => data)
// If any promise in this chain is rejected, this will fire
// and it will throw the error to the catch block
// and your catch block should pass it through to your
// error handling middleware
.catch(err => { throw 'Problem occurred in index route:' + err });
return res.render("partial/search.pug", {
user: user, search: {
query: string,
results: results
}
});
} catch (err) {
// look at the top how we added next as the 3rd, callback parameter
return next(err);
}
});
module.exports = router;
Error handler:
// notice how we add `err` as first parameter
app.use((err, req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
From the Express docs:
Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next). For example:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
http://expressjs.com/en/guide/error-handling.html
That might be your true issue because the error handler should only fire if next() is called with any input, but yours appears to be firing every time like a normal middleware, so I suspect it's because there is no err parameter on that middleware function, so it is treated as a normal one.
The Default Error Handler
Express comes with a built-in error handler, which takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
If you pass an error to next() and you do not handle it in an error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace. The stack trace is not included in the production environment.
If you call next() with an error after you have started writing the response (for example, if you encounter an error while streaming the response to the client) the Express default error handler closes the connection and fails the request.
So when you add a custom error handler, you will want to delegate to the default error handling mechanisms in Express, when the headers have already been sent to the client:
// code example in docs
Note that the default error handler can get triggered if you call next() with an error in your code more than once, even if custom error handling middleware is in place.
I also recommend using that splat route app.get('*', async (req, res, next) => {}) right above the error handler middlware (aka as the last loaded route in your list). This will catch all unmatched routes, such as /sih8df7h6so8d7f and forward the client to your 404. I think the error handler middlware is more suited for error 500 and clean formatted type errors because it gives you a function that can parse the value of next(err) anytime it is called from a route.
I usually do this for authentication failures with JSON web token (as the first line of code inside every auth required route):
if (!req.person) return res.status(403).render('error/403')
I realize some of this may fry your wig wholesale, so try all this stuff out and see each piece working before you determine if you would like to utilize it or not.
After a few days of going through the code again and again I stumbled upon a problem in the checkAndGetUser-function, that when run without the user being signed in, and since it's faster than the async call to the DB, triggered the splat endpoint and thus showed the 404 page.
I believe the reason for not triggering the splat endpoint when the res.render call was replaced by res.send was that the res.send function is that much faster than the render-call, since it doesn't have to parse any HTML.
Thank you #agm1984 for providing very useful information about the Express framework, if anyone else are having the same or a similar problem, make sure to read through his post.

Not able to return proper response after insert

I'm using Node.js/Express.js to install data to my MySQL DB.
Inserting data works fine, but returning success / fail gives me an error.
TypeError: Cannot read property 'status' of undefined
This is my code:
var crud = {
newProject: function (req, res, callback) {
db.query('INSERT INTO projects SET ?', req.body, function(err, res) {
// This is where it fails
if(err){
return res.status(500).json({error: err});
} else {
return res.status(200).json({success: 'Insert row success'});
}
});
},
}
// Express routing
app.post('/project/*', crud.newProject);
What am I not getting right here?
Solution
So this is what I used to make it work (after changing 'res' to 'resp' as suggested):
if (err) throw err;
res.end(JSON.stringify({response: 'Success'}));
Your defining res twice. The express response object is getting overwritten by the data param in your node callback.
Try the following (see comment)
var crud = {
newProject: function (req, res, callback) {
// changed 'res' to 'resp' to avoid collision with Express' 'res' object
db.query('INSERT INTO projects SET ?', req.body, function(err, resp) { // here's your error
// This is where it fails
if(err){
return res.status(500).json({error: err});
} else {
return res.status(200).json({success: 'Insert row success'});
}
});
},
}
// Express routing
app.post('/project/*', crud.newProject);
If you define error-handling middleware functions after the last app.use() in your main configuration
app.use(function (err, req, res, next) {
res.status(500).send(err.message || 'Internal server error.')
})
You can use the next callback as a catchall error handler, so the above would then become
var crud = {
newProject: function (req, res, callback) {
db.query('INSERT INTO projects SET ?', req.body, function(err, resp) {
if (err) return callback(err);
return res.json({success: 'Insert row success'});
});
},
}
// Express routing
app.post('/project/*', crud.newProject);
res.json() by default should add a 200 Success code to the response header. Ideally you would want to inspect the resp data param from the node callback after checking the state of err to properly handle the response and proceed accordingly, especially if you are dealing with last evaluated records associated with a continuation token usually provided in the response which some DBALs and APIs do for you and some don't. Either way you will want to be sure additional recursion isn't necessary to fetch remaining records before responding successfully.
Looks like the res object is undefined as it is not returning any response after the insert. You may return a new object like:
return {
status: 200,
json: {success: 'Insert row success'}
}

Express next() error

I'm working with node+express+MongoDB.
I dont understand this error.
When I comment next() it's fine and works but when I use next() I'm getting the error: Error: Can't set headers after they are sent.
videos.route('/')
.get(function(req, res, next) {
Video.find({},function (err,videosCollection) {
if (err)
{
console.log(err);
}
if(!videosCollection.length)
{
console.log("Videos not found");
}
else
{
console.log("videos found");
res.status(200).json(videosCollection);
}
})
// next();
})
next() in express passes async control to the next middlewear in the chain.
This is how next should be used. To pass errors down the chain of middleware.
videos.route('/')
.get(function(req, res, next) {
Video.find({},function (err,videosCollection) {
if (err){
console.log(err);
// pass the error along
next(err);
}else if(!videosCollection.length){
// pass a new error along
next(new Error("Videos noz found"));
}else{
console.log("videos found");
// no need to call next
// res.json finishes the connection
res.status(200).json(videosCollection);
}
})
})
When you use res.status or res.send the request has ended and the function does a soft return. When you do next() you're passing it along in the chain of middleware and endpoints. So basicly, you'll do double return.
So the message tells you that you cant return a response after the response has been returned to the client.
Only when you're writing middleware do you need to use next.
When you call the next function it will call the following middleware after this route.
You are calling this function before the callback is called in Video.find. If you want to call next, you have to do it inside the callback as below.
videos.route('/')
.get(function(req, res, next) {
Video.find({},function (err,videosCollection) {
if (err)
{
console.log(err);
}
if(!videosCollection.length)
{
console.log("Videos not found");
}
else
{
console.log("videos found");
res.status(200).json(videosCollection);
}
next()
})
})

Catching errors from mongoose queries in express

How can I catch error from mongoose queries. In my routes I got something like this:
// router.js
router.route('/signup')
.post(function(req, res) {
var profile = new Profile(); // create a new instance of the profile model
profile.username = req.body.username;
profile.password = profile.generateHash(req.body.password);
profile.save(function(err) {
if (err) { // (A)
throw new Error('user/create error'));
} else {
res.json(200, { user_token: profile._id, username: profile.username });
}
});
});
and in my app were I set up my routes I got this:
// app.js
var router = require('./app/routes/routes');
// global-error handling middleware
app.use(function(err, req, res, next) {
console.log('Some error is happening.');
res.json(500, {status: 500, message: err.message});
});
If I generate a error so I get to line // (A) in my code above I get a stack trace and node.js exists. I want to catch the error In my error handler. How do I do this?
Well, you are already in the request handler, and you already have access to the error produced while saving the profile object. So, there is no need to throw an exception here. You can already handle the problem.
The most likely scenario here is to send a response to the user indicating that the saving of the profile failed.
function(req, res) {
profile.save(function(err) {
if (err) { // (A)
res.send(500, {message: 'Failed to save profile'}
} else {
res.json(200, { user_token: profile._id, username: profile.username });
}
});
}
And that's it. Your client will receive a 500 status error and this evidently represents a problem that your client will need to deal with, like notifying the user, doing a retry, etc, etc, etc.
you can use Promise-like error handling. mongoose permits to use promises on its methods:
profile.save().then((doc) => {
// if done correctly
}).catch((err) => {
// catch error if occurs
// handle error
});
you can read more about mongoose built-in promises there.

Categories

Resources