Proper error handling in node using try catch - javascript

Im new to javascript programming and i am required to make a web app. Node.js will be used as the js runtime environment. In order to minimize the amount of time needed for debugging as the app develops I would like to implement a robust error handling scheme. However, due to my limited background I am not sure if what I am implementing is best practise or if it is even adequate. So any feedback will be accepted.
The function is asynchronous and will use catch to detect if any errors occurred while operating. A try-catch statement will be used to catch any errors. This was done in order to allow for individual error identification from the functions. My aim is to propagate the errors up to the calling function that will handle it in the highest level catch statement (in my case where it is logged *this will change eventually). Any feedback?
create: async function(req, res) {
let data = JSON.parse(req.body.data);
let result;
let request_body;
let sub_networks_lora;
try {
request_body = sub_network_api_request_data(data, 1);
result = await lora_app_server.create_applications(request_body)
.catch(err => {
//Error updating application on lora app server
throw err;
});
await db_sub_networks.create_sub_network(result.data.id, data.sub_network_name, data.network_id)
.catch(err => {
throw err;
//Error creating sub network in database
});
sub_networks_lora = await get_sub_networks()
.catch(err => {
throw err;
//Error getting networks from lora app server
})
sub_networks_lora = JSON.stringify(sub_networks_lora);
res.status(200).send({
sub_networks_lora
});
} catch (err) {
console.log(err)
} finally {}
}

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

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.

RethinkDB - Run query one after another

I am having trouble running multiple queries inside a single connection with RethinkDB. I have tried the r.do as seen in this question, however no success. I have also tried working with the conditional update queries. What I am looking to do is:
Open the connection.
Query to see if my field is there and if it is, perform some tasks.
Query to see if a counts field is there, subtract it by one.
What would be the best way to go about this? It seems I might be missing something?
r.connect(config.rethinkdb, function(err, conn) {
if (err) {
throw err;
}
else {
console.log('Connected.');
app.set('rethinkdb.conn', conn);
}
r.table('upcs').filter({AcceptedUPC:data}).run(conn, (err, cursor) => {
if (err) throw err;
console.log(data);
cursor.toArray((err,resu) => {
if (err) throw err;
//make a csv with some information
})
})
And in the same connection run
r.table('upcs').filter({AcceptedUPC:data}).filter(r.row.hasFields("UPCCount")).update({UPCCount: r.row("UPCCount").sub(1)}).run(conn, (err,cursor) => {
if (err) throw err;
});
Running this in NodeJS
I'm going to assume you are using this library for node.
You can that they actually allow you to do either callbacks or promises. I would recommend promises to avoid brackets of hell.
For promises you can use the bluebird library to make life easy.
You can do it by doing the following.
r.connect(config.rethinkdb).then(() => {
console.log("Connected");
app.set("rethinkdb.conn", conn);
return r.table('upcs').filter({AcceptedUPC:data}).run(conn);
}).then((cursor) => {
console.log(data); //maybe this should be cursor but I don't use rethinkDB
return cursor.toArray();
}).then((resu) => {
//make a csv with some information
}).catch((err) => {
throw err;
});

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