How do you properly stop execution of an express.js endpoint? - javascript

I have a middleware error handler that is working great but next(err) and return and return next(err) seems to not stop execution when dealing with promises. What is the proper way to stop execution of my code when an error is found?
For reference: err is a standard Error class in this case.
I don't think you need the code in userProvider.fetchFriends to help with this but if that's wrong please let me know.
const uid = req.query.steamUserId;
//Use the userProvider to get steam friend data
const friendData = await userProvider.fetchFriends(uid)
.catch(err => {
return next(err); //Does not stop execution. Why? just next(err) doesn't either.
});
//Putting this after every catch works but seems very stupid. How do I avoid this?
if(res.writableEnded)
return;
...Other code that runs but causes errors
}

You've got two problems here.
First: next() is explicitly continuing to the next middleware or endpoint.
To finish dealing with the request, send a response.
const middleware = (req, res, next) => {
if (something(req)) {
next();
} else {
res.status(500).json({ ok: false, msg: "some error message" });
}
}
Second: You need to watch your asynchronous logic carefully.
You can't:
trigger something asynchronous
send a response
send a different response when the asynchronous function is complete
You can only send one response to a request.
Either:
Don't call next or res.something in the catch block and just log the error internally or
Don't call next or res.something outside the promise handling and move it to a then handler instead (you might want to switch to using async/await to make your logic easier to follow)

The issue was that I was mixing async/await with .then/.catch. Needed to use try/catch.
ty #jonsharpe
export const getSteamFriends = async (req, res, next) => {
try{
const uid = req.query.steamUserId;
//Use the userProvider to get steam friend data
const friendData = await userProvider.fetchFriends(uid);
//more code in the middle
} catch(e) {
return next(e);
}
};

Related

Is there a functional difference between these 2 snippets of my code? Does the second one handle possible errors the same way as the first?

I asked a question on stack overflow earlier, and a kind user suggested some improvements for my code which seemed great, so I started going over my code in order to implement these changes. The problem is that I'm not sure if my reworked code is handling possible errors the same way as the first.
Here's my current code right now:
module.exports.deleteBrand = async (req, res, next) => {
let brandId = req.body.brandId
let brand
try {
brand = await Brand.destroy({
where: {
id: brandId
}
})
} catch (e) {
console.log(e)
}
if (brand) {
res.status(200).json({
brand: brand
})
} else {
res.status(500)
}
}
And this is how I intend to rework it:
module.exports.deleteBrand = async (req, res, next) => {
let brandId = req.body.brandId
try {
let brand = await Brand.destroy({
where: {
id: brandId
}
})
res.status(200).json({
brand: brand
})
} catch (e) {
console.log(e)
res.status(500)
}
}
As you can see, in my first code snippet, the try-catch block surrounds only my database query and then I check if the database query was successful outside the try-catch, and only send the 200 status response if it is. Otherwise, I send a 500 status.
Is my if-else useless in that scenario, considering that if the database query fails, the error would be caught by the catch block? Should the code that returns 500 status be placed in the catch block?
There is a small difference in error handling, yes, but if anything it seems likely to be an improvement. The difference is:
In your first example, an error raised by res.status(200).json({brand: brand}) after successfully retrieving brand from the database is not caught and terminates deleteBrand.
In your second example, that error is caught and results in calling the res.status(500) in the catch block.
json({brand: brand}) might throw an error if brand has any circular references and so can't be converted to JSON. In that case, your res.status(500) will overwrite your previous res.status(200) (assuming response headers haven't been sent yet, which is probably a correct assumption).
Side note: You're using ES2015+ code, which means you can use shorthand property notation and change .json({brand: brand}) to simply .json({brand}).

Handling a success in JS

I am experimenting with t library, and am trying to install the functionality into my own search bar to practice (still a student).
Can anyone provide advice to the format this would be in??
#PascalLamers answer is the cleanest way to go about it, using the "await" pattern.
To see what this looks like with promise chaining:
app.get('/:url', async function (req, res) {
return wappalyzer
.init()
.then(function(){
return wappalyzer.open(decodeURIComponent(req.params.url))
})
.then(function(site){
return site.analyze()
})
.then(function(data){
return res.status(200).json(data);
})
.catch(function(err){
return res.status(500).json({ message : err.message })
})
}
Each then executes a single async operation, and on success passes its result to the next then() operation.
Each then() is only called upon the success of the previous then(). If an error occurs at any point, the catch() is executed and the function returns (in this case, returns an error 500).
Completely ignoring what wappalyzer actually is or does, I would suggest the following, since you are already providing an async function as callback to your route controller :
app.get('/:url', async function (req, res) {
try {
await wappalyzer.init();
const site = await wappalyzer.open(decodeURIComponent(req.query.url));
const data = await site.analyze();
// if you end up here, everything was successfull
return res.status(200).json(data);
} catch (ex) {
// if you end up here, something went wrong
return res.status(500).json({ message : ex.message });
}
});
The above doesn't really make much sense, since you are telling your backend to look for an url param but using a query parameter instead. I woudln't recommend to send an url as either, param or query. Use the request body instead :
// receive request in backend, with an endpoint that makes a bit more sense
// also using POST method, otherwise you won't be able to send a body
app.post('/search', async function (req, res) {
const url = req.body.url;
// do stuff
});
// sending request to backend, using axios for example
const respond = await axios.post('/search', { url : 'www.google.com' });
// or using fetch
const respond = await fetch('/search', {
method: 'post',
body: JSON.stringify({ url : 'www.google.com' });
Please be aware these are just pointers, nothing to copy & paste :) .
If you are using Express, I suggest reading the documentation again. I think you are misunderstanding how the router works : https://expressjs.com/de/guide/routing.html#route-parameters

Why do we return the status code and response in express?

So I'm currently learning node.js and express, and I encountered this:
exports.checkBody = checkBody = (req, res, next) => {
if (!req.body.name || !req.body.price) {
// Return statement is here
return res.status(400).json({
status: "fail",
message: "Please add a name and price"
})
}
next();
}
Why do I need to put the return statement here, because when I remove the return, it works exactly the same.
Here is the routes
router.post(tourController.checkBody, tourController.createTour);
Because return stops the execution of the function. If there is no return you send the response back but that doesnt mean that the function stops to execute.
Imagine you have some kind of auth middleware that checks if an user is valid and you either send an error back or you move to the next Middleware with next()
Now you dont use return. You get an response on the client that you got rejected but on the serverside the code still got executed. I hope you get what i mean. Its maybe not the best example but... Yea..
The return is used for the effect of exiting the function in this case, not for usefully returning a value. It’s a shorter equivalent that some people prefer as a style choice over this:
res.status(400).json({
status: "fail",
message: "Please add a name and price"
})
return
If you remove the return entirely, next() will be called, and the next handler will run, which usually isn’t correct after you’ve already responded, and especially not in this case where the middleware being implemented is validation that’s supposed to stop the route from running when validation fails.
I personally prefer the more explicit separate return for the purposes of avoiding exactly this type of confusion, but it doesn’t come up as much anymore now that promises and async + await are available as options.
The reason for the return is so you don't execute next() if an error is detected.
The return value isn't important, because res.status() sends the response to the client. So this is actually just a shorthand for doing
exports.checkBody = checkBody = (req, res, next) => {
if (!req.body.name || !req.body.price) {
res.status(400).json({
status: "fail",
message: "Please add a name and price"
});
return;
}
next();
}

Conditionally Sending Response in Catch Block

I'm trying to figure out the proper way to handle a potential bad Fetch response. If the Fetch response is not okay, I want to immediately send a 500 response and and stop the rest of the code from executing. But if the response is okay, I want to continue the program and send a response at the end. My code does seem to work properly at the moment but I get
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
I assume this is because program continues after the catch block and tries to send the second response. Again, the code works but this is obviously not the right way to do it. Is there way I can get the program to stop executing after the response in the catch block is sent?
app.get('/route', async function(req, res){
try{
let response = await fetch('https://www.google.com/NonExistantPage')
if(!response.ok)
throw new Error("err")
}catch{
res.status(500).end()
}
/*
Program continues if fetch was successful
*/
res.send("data")
})
Your code is trying to call the res.send("data") even though it sets the response when error occurs inside catch res.status(500).end(). Try returning the response to break the execution of the final response once the response headers are set inside catch as shown below
app.get('/route', async function(req, res){
try{
let response = await fetch('https://www.google.com/NonExistantPage')
if(!response.ok)
throw new Error("err")
}catch{
return res.status(500).end()
}
/*
Program continues if fetch was successful
*/
return res.send("data")
})
try-catch is async so don't need to set async function. I use this:
app.get('/route',(req, res)=>{
try
{
let response = await fetch('https://www.google.com/NonExistantPage')
if(!response.ok) {
throw new Error("err")
}
}
catch (err)
{
/* handle errors */
}
finally
{
res.status("status code")
// send data and other things ...
res.send("data")
return.end()
}
})

Using express, await, catch and next to stop function execution on error

I'm writing an express JS app using this style for routing:
router.post('/account/create', async function(req, res, next) {
var account = await db.query(`query to see if account exists`).catch(next);
if (account) {
res.send('Email is unavailable.');
} else {
// Create account
}
});
If the query returns successful but with no rows, the route executes perfectly. account is empty and so the if statement works and we create an account.
However if there was an issue with the db query, the catch statement is called and account is undefined, so the function continues to attempt to create a new account, even though next has been called which logs the error and sends a 500.
In an effort to continue with the ease of this async/await simple coding style, is there a way to easily stop function execution (or another solution) to prevent the subsequent code from executing without going back to callbacks?
Something like below should do the job?
It utilises try / catch, coupled with async/await, this way there are no callbacks.
router.post('/account/create', async function(req, res, next) {
var account;
try {
account = await db.query(`query to see if account exists`);
} catch (e) {
return res.status(500).send("Error checking if account exists.");
}
// If the account exists, return early
if (account) {
return res.status(500).send("Account already exists.");
}
// Account doesn't exist, so let's create the account!
try {
// Here you could create your new user account, and save it in the database, the catch would catch any DB error.
// await db.query......
} catch (e) {
// Something went wrong creating the account, oops! Return.
return res.status(500).send("Error creating account");
}
// The account would have been created at this point.
return res.status(200).send("Account created!");
});
Using promises, without async/await.
router.post('/account/create', async function(req, res, next) {
db.query(`query to see if account exists`)
.then((account) => {
// If the account exists, return early
if (account) {
return res.status(500).send("Account already exists.");
}
// Now create account
db.query(`query to create account`)
.then((result) => {
// Account created fine, return.
return res.status(200).send("Account created!");
})
.catch((err) => {
// Error creating account, return.
return res.status(500).send("Error creating account");
});
})
.catch((err) => {
return res.status(500).send("Error checking if account exists.");
})
});
I've decided to use the solution here which is to wrap my route handlers in a function that catches errors for the entire route handler and calls next. Then if I need to handle an error specifically I can use try-catch. So 90% of cases use the default next error handler, the other 10 just use try-catch.
Keeps everything clean and super convenient, and you don't ever have to use .catch() on await calls.

Categories

Resources