node express try catch not working as expected - javascript

I'm a beginner in Node/Express.js and I'm trying out some try catch logic but doesn't seem to work as expected.
app.get("/tasks/:id", async (req, res) => {
try {
const _id = req.params.id;
const task = await Task.findById(_id);
if (!task) {
return res.status(404).send("task not found");
}
res.send(task);
} catch (error) {
res.status(500).send("Internal server error");
}
});
from this code sample, I'm making a query to Mongo DB to fetch some task but the problem is that if the task is not found, instead of running through the if statement, the program jumps directly to the catch block hence the if condition is not checked thus resulting to a different error. How can I fix this issue?

This is simply how MongooseJS works - check their Promises documentation and you will see that if a document is not found, an error will be thrown. If you were not using await, the findById() documentation shows how an error is returned if it cannot be found:
// find adventure by id and execute
Adventure.findById(id, function (err, adventure) {});
You can simply modify your route to look like the following:
app.get("/tasks/:id", async (req, res) => {
let task;
try {
task = await Task.findById(req.params.id);
} catch (error) {
return res.status(404).send("task not found");
}
/* perform other checks or actions here if desired */
res.send(task);
});
This is a little more flexible if you want to perform other error-checking; task is declared first so it can be accessible outside the try/catch block, and a 404 error is thrown if the task does not exist.
You can also look at the exists method which will return true or false based on if the document exists, but since you need the actual result that does not make as much sense.

You don't indicate what Task is, but it appears that it is rejecting when it doesn't find anything, rather than returning a false value (which is what you seem to be expecting).
Given that, you should probably just handle the error that it is throwing with something like
} catch ( error ) {
// Will need to adapt this if to something that identifies the error thrown by `getById`
if ( error.message.includes( 'not found' ) ) {
res.status( 404 ).send( 'task not found' );
} else {
res.status( 500 ).send( 'Internal server error' );
}
}

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}).

jwt.decode() stuck instead of returning an error on malformed token

On an express nodejs server I try to validate a token with the jsonwebtoken package (v8.5.1). I experience something really odd and don't seem to find a solution for this.
Whenever I try to verify a malformed token the jwt.verify method becomes stuck instead of throwing the usual error which I expected. Can someone please point out what I am doing wrong. Underneath you'll find the code which becomes completely stuck.
When the token is valid, the console.log statement returns the content of the jwt. When it is invalid, the console.log statement is never run and the endpoint just never responds. So for some reason, it becomes completely stuck on the jwt.verify method.
router.post('/session', async (req, res) => {
try {
const token = req.headers['x-auth-token'];
if (!token) {
return res.json(false);
}
const verified = jwt.verify(token, process.env.JWT_SECRET);
console.log(verified);
if (!verified) {
return res.json(false);
}
return res.json(true);
} catch (e) {
return res.status(500);
}
});
Hey in that case I would suggest to use promisify and wait for the promise to throw an error.
const verified = await promisify(jwt.verify)(req.params.token, process.env.JWT_SECRET);
using this require statement:
const { promisify } = require('util');
you can check the node.js documentation
I still don't know exactly why it behaved the way it did, but at least I found a workaround that answers immediately when the token is incorrect instead of returning nothing.
I added a callback to the jwt.verify function:
const verified = jwt.verify(
token,
process.env.JWT_SECRET,
(err, verified) => {
if (err) {
return res.status(401).json('Error');
}
return verified;
}
);

Flow control with async without throwing Errors

I am having an issue where Intellij is warning me about 'throw' of exception caught locally. After doing some digging on why this is not ok it makes sense, errors should not be used for flow control. The problem I am facing though is in async I cannot reject the promise without throw something locally and I get the warning.
Some of my example code.
Top level takes in the request and awaits for the response from the controller:
Router.post("/", async (req, res) => {
try {
let itemController = new ItemController(req.body);
let response = await itemController.request();
res.send(response);
} catch (error) {
res.status(500).send({ error: error});
}
});
The controller takes in the request and awaits on other functions to get some data.
async request() {
try {
await isValidItem();
return await this.initialize();
} catch(error) {
throw error;
}
}
Then I have a function which gets manufacturer ID for this item and I run into my problem. If the SQL query doesn't fail and nothing is in the response I need to throw a local error so that the request can fail gracefully. and send a proper 500 error to the client.
async queryManufacturerID() {
try {
let result = await this.queryManufacturerID(this.itemID, this.brand);
if (result === false) {
throw new Error("this item has no manufacturer ID");
} else {
this.manufacturerID = result["manufacturerItemID"];
}
} catch (error) {
throw error;
}
}
My problem is I know I can adjust this so other functions that get a reply from this can know that this function failed without a error but that would have to happen in this entire chain to prevent throwing locally. Seems like a lot of bloat.
The only thing that makes this code bloaty and the IDE complain is not throwing errors, but this:
try {
//...
} catch (error) {
throw error;
}
It's a no op. You can safely remove it without changing the logic.
The only case where you should use catch is when you actually plan to handle the error and get the execution back on track.
After doing some digging on why this is not ok it makes sense, errors should not be used for flow control
I disagree. Errors are a part of proper flow control, they allow you to handle unplanned things. Throwing an error if something unexpected occured makes sense, also in this case.

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.

throw error versus normal return in express

I know how to write simple API using node.js (express). But now I'm confused and couldn't differentiate this two block of code
if(err){ return res.status(500).json(err) }
return res.json(result)
versus
if(err) { throw new Error(err) }
return res.json(result)
What is the standard for a API response? I simply return 2 property, like
if(err){ return res.json({ status: false, msg: 'user password is incorrect }) }
return ({ status: true, msg: result.token })
What's wrong with my approach and why we should use throw?
You don't generally want to throw an error in Express at all, because unless it's caught it will crash the process without giving your user warning, and it's not easy to catch the error and maintain the request context to do so otherwise.
Instead the choice in an Express handler should be between directly returning an error response (as in your example) and calling next(err). In my apps I always do the latter, because it lets me set up error handling middlware to always and consistently handle the various problem cases.
Example below:
app.get('/something', (req, res, next) => {
// whatever database call or the like
Something.find({ name: 'something'}, (err, thing) => {
// some DB error, we don't know what.
if (err) return next(err);
// No error, but thing wasn't found
// In this case, I've defined a NotFoundError that extends Error and has a property called statusCode set to 404.
if (!thing) return next(new NotFoundError('Thing was not found'));
return res.json(thing);
});
});
Then some middleware for handling errors like so:
app.use((err, req, res, next) => {
// log the error; normally I'd use debug.js or similar, but for simplicity just console in this example
console.error(err);
// Check to see if we have already defined the status code
if (err.statusCode){
// In production, you'd want to make sure that err.message is 'safe' for users to see, or else use a different value there
return res.status(err.statusCode).json({ message: err.message });
}
return res.status(500).json({ message: 'An error has occurred, please contact the system administrator if it continues.' });
});
Note that nearly everything in Express is done through middleware.

Categories

Resources