Comparing Implementations to Handle Exceptions in JavaScript - javascript

I have a question about handling exceptions/errors. Consider this implementation using a try/catch. This is certainly one option, but I've also heard try/catch can be a little heavy-handed.
Option 1:
async function updateCustomerRegion(params, auth) {
const { id, region } = params;
const Customer = await CustomerModel();
const filter = { _id: id };
const update = { region: region };
try {
const customer = await Customer.findOneAndUpdate(filter, update, { new: true })
.lean()
.exec();
} catch (error) {
console.log(error);
throw "Error: Unable to update customer";
}
return {
count: customer ? 1 : 0,
data: customer
};
}
Another option is this implementation where I just throw an error if "customer" is false:
Option 2:
async function updateCustomerRegion(params, auth) {
const { id, region } = params;
const Customer = await CustomerModel();
const filter = { _id: id };
const update = { region: region };
const customer = await Customer.findOneAndUpdate(filter, update, { new: true })
.lean()
.exec();
if (!customer) throw "Error: Unable to update customer";
return {
count: customer ? 1 : 0,
data: customer
};
};
My question is, how functionally equivalent are these two implementations? Will Option #1 handle more than Option #2, or is using try/catch a little heavy-handed in this instance?

Option 1 As far as I know, it's best practice to wrap async/await with try/catch, anywhere if anything goes wrong then exceptions/errors will be handled in catch block i.e; you can throw custom exceptions in catch block along with any generic exceptions.
In your Option 2, I don't think code would reach if (!customer) throw "Error: Unable to update customer"; if there is an exception/error it would return from above line and error out from there with a non-user friendly exception in response - wouldn't go to caller and return a response(either expected or user friendly error message). if(!customer) is just like your function executed well & you're checking on the return object is not valid - if empty do return no customer found or something it's not equal to catching errors. So to be precise it's more like functionality check rather than exception handling. Ex :-
Combining both for better implementation :
async function updateCustomerRegion(params, auth) {
const { id, region } = params;
/* here you are basically validating region is string or not and returning from here, with out any further exception (just a basic example)*/
if (!region && region !== typeof ('')) throw "Error: invalid region";
const Customer = await CustomerModel();
const filter = { _id: id };
const update = { region: region };
try {
const customer = await Customer.findOneAndUpdate(filter, update, { new: true })
.lean()
.exec();
/* Here as well a validation check not to execute further assuming your DB call will respond with empty object*/
if (Object.entries(customer).length !== 0 && customer.constructor === Object) throw "Error: Unable to update customer - no customer found";
/* or */
if (Object.entries(customer).length !== 0 && customer.constructor === Object) return { count: 0, data: [] };
} catch (error) {
console.log(error);
/* Basically this is just an example to show you can check something here & do custom exceptions */
if (error.stack === 'operation exceeded time limit') {
throw "Error: Unable to update due to timeout or someother";
} else {
throw "Error: Unable to update";
}
}
/* more logic to add more fields and return */
return {
count: 1,
updatedBy: 'name',
reason: 'just updated like that',
data: customer
};
}

They are not functionally equivalent.
Implementation #1 will throw an error if findOneAndUpdate is actually throwing an error itself, regardless of its return value (it's not returning a value at all in this case). Here you are handling exceptions that have been thrown in findOneAndUpdate or that have bubbled from a dependency of it.
Implementation #2 will throw an error if findOneAndUpdate doesn't throw an error but returns a falsy value. Here you are deciding that the return value is unexpected, and you cannot proceed with the method execution, hence throwing an exception. In this case, if findOneAndUpdate throws an exception for whatever reason, you are not handling it and it will bubble up to updateCustomerRegion callee, possibly crashing your application.
As a rule of thumb, I would suggest to try/catch all your async calls whenever it makes sense (e.g. performing API calls, queries, etc...). You can also combine the two, handling the exception and throwing on falsy returns might make complete sense in your use case.
Also, I would advise against throwing raw strings but to always rely on an actual instance of an Error class (either typescript stock errors or a custom one).

Related

nodejs app crash on openai dall-e 2 api rejected request

I'm surely dumb, but I'm not able to figure out how to handle openai api rejected requests
( for the context, dall-e 2 is an image generator )
when user tries to generate forbidden images, my nodejs app just exits
async function start(arg) {
try{
// generate image
const response = openai.createImage({
prompt: arg,
n: 1,
size: "1024x1024",
});
// on success response
response.then(res =>{
console.log("ok");
})
response.catch(err =>{
console.log(err);
});
} catch(e){
console.log(e);
}
}
it gives me something like that on the exit :
data: {
error: {
code: null,
message: 'Your request was rejected as a result of our safety system. Your prompt may contain text that is not allowed by our safety system.',
param: null,
type: 'invalid_request_error'
}
}
tried using response.catch and try catch without success, the app just exits everytime
I at least want to ignore this error in the first place
in a second hand, I would like to console.log the given message (data.error.message)
I don't know what to do to by honest, don't even understand why try catch isn't working
With the details given, my guess would be that the Promise returned by getImages is being rejected. You could debug this a bit by adding some additional logs into your .catch callback and catch statement.
How to do this really depends on what you're trying to do with this api, the code as it's currently written would log something and exit no matter what happens.
There's a couple ways to handle this
Use your .catch to handle the error. Utilizing promise chainability you can get something like this
openai.createImage({
prompt: arg,
n: 1,
size: "1024x1024",
user: msg.author.id,
})
.catch((e) => {
if (e.data.error.message.includes('safety system')) {
return 'something'
}
console.error(e)
})
If you need the response object, the asnwer might be different. Looks like the openai package is built on axios and you can pass axios options into it. See https://axios-http.com/docs/handling_errors and the Request Options section of https://npmjs.com/package/openai
EDIT
I found my solution thanks to #JacksonChristoffersen
Basically I was getting http status 400
I just added request options from axios to validate http status smaller than 500
Here's the solution:
async function start(arg) {
try{
// generate image
const response = openai.createImage({
prompt: arg,
n: 1,
size: "1024x1024",
},{
validateStatus: function (status) {
return status < 500; // Resolve only if the status code is less than 500
}
});
// on success response
response.then(res =>{
console.log("ok");
})
response.catch(err =>{
console.log(err);
});
} catch(e){
console.log(e);
}
}

saving documents to mongoDB preventing duplicates

I'm trying to save multiple documents in mongodb using mongoose; and I'm also willing to prevent duplicates. my function looks sth like this:
const Stock = require('./models/stock')
let _symbol = 'symb'
const writeToDB = async (dataObj) => {
try {
let stock = await Stock.find({symbol : _symbol } , function (err) {
if(err) return null
})
if (!stock) {
stock = new Stock({
dataObj
})
await stock.save()
console.log(`${symbol} is successfully saved to database`)
} else {
stock = await Stock.updateMany(
dataObj, function (err) {
if (err) {
console.log(err)
} else {
console.log(`${symbol} successfully added`)
}
})
}
} catch (error) {
console.log(error)
}
}
but I keep getting timeout error. can someone pls inform me what's wrong.
update
with a well handled connection approach findOneAndUpdate()works fine
Using the upsert option, in findOneAndUpdate(). An upsert behaves like a normal findOneAndUpdate() if it finds a document that matches filter. But, if no document matches filter, MongoDB will insert one by combining filter and update as shown below
var query = {symbol : _symbol };
try{
let result = await Stock.findOneAndUpdate(query, dataObj, {upsert: true})
}
catch(err){
console.log();
}
if you have a big collection, for increase speed findOneAndUpdate(), you should indexed symbol field.
when you use async await, it's better don't use callback and use try catch
I think the best, simply and easy way to prevent duplicate values is use unique value in the schema.
So your Stock schema has to have something similar to this:
symbol:{
type: String, // or whatever
unique: true
}
If you try to insert two object with same value, mongoose will trhow an error like:
MongoError: E11000 duplicate key error dup key: { : "repeatedSymbol" }
Also you can check the documentation.

Is my code the best way to use async await?

Am try to implement and learn async await functions in my login example, but I don't know if is the best, elegant and clean code. I have doubs meanly in catch errors, and if I need implement in a best way the const and functional programing. Can share your opinions?
app.post('/', async (req, res) => {
try {
const { email } = req.body.email; // destructuring
const usuarioEncontrado = await Usuario.findOne({email: email});
// Validate user exist
if (!usuarioEncontrado) { // when not exist return null
throw res.status(404).json({error: 'El usuario no existe'});
}
// validate user pass
if (!bcrypt.compareSync(req.body.password, usuarioEncontrado.password)) {
throw res.status(404).json({error: 'No match'});
}
const token = jwt.sign( // generate token
{
usuario: usuarioEncontrado
},
SEED,
{
expiresIn: (60 * 60)
}
);
res.status(200).json({ // send response
token: token,
usuario: usuarioEncontrado
});
} catch (e) { // send error
res.status(404).json(e);
}
}
THANKS
Your code shows a couple problems:
You're attempting to send double responses. First you do throw res.status(404).json(...). Then, you catch that exception and do res.status(404).json(e) again. That's not right. If you're going to send the response, then just return, don't throw. Or, just throw the exception without sending a response and send the actual error response from the catch handler.
Also, throw res.status(404).json({error: 'No match'}); sends the response and then throws whatever .json() returns which is probably not what you want. That won't be an error object of any kind.
I prefer to centralize the places I send an error response to one place in the request handler. That keeps you from ever attempting to send multiple responses and just makes the flow of the request handler easier to understand (in my opinion).
To do that, I just throw a custom error that may have a custom message/status associated with it and then catch all possible errors in one place. Here's one way to do that. The myError class can be used everywhere in your project, not specific to just one route. The idea is that often when you throw, you know in that context what you want the status and message to be so you set that in the custom Error object and can then use that info in the catch. The catch then has to determine whether it has your custom error or just a regular error. First, I have a reusable Error subclass that lets me throw, not only a message, but also a status value.
// reusable error class that contains a status in addition to the message
class MyError extends Error {
// this static method saves having to compare if it's a custom error object or not
// every time we use this
static sendError(res, e, status = 500) {
if (e instanceof MyError) {
e.sendError(res);
} else {
res.sendStatus(status);
}
}
constructor(msg, status = 500) {
// allow calling with or without new
if (!(this instanceof MyError)) {
return new MyError(msg, status);
}
super(msg);
this.status = status;
}
sendError(res) {
res.status(this.status).send(this.message);
}
}
And, then here's how you use that in your code and centralize the sending of the error status.
app.post('/', async (req, res) => {
try {
const { email } = req.body.email; // destructuring
const usuarioEncontrado = await Usuario.findOne({email: email});
// Validate user exist
if (!usuarioEncontrado) { // when not exist return null
throw MyError('El usuario no existe', 404);
}
// validate user pass
if (!bcrypt.compareSync(req.body.password, usuarioEncontrado.password)) {
throw MyError('No Match', 404);
}
const token = jwt.sign( // generate token
{
usuario: usuarioEncontrado
},
SEED,
{
expiresIn: (60 * 60)
}
);
res.status(200).json({ // send response
token: token,
usuario: usuarioEncontrado
});
} catch (e) { // log and send error response
// e may be either MyError or some other system generated Error
console.log(e);
MyError.sendError(res, e);
}
}

meteor methods functions return is not working

I am trying if my data is removed then show return "removed successfully"; but it's not working also not removing the data. If I am not used function then data is removed but not getting any return result form call back
Its working
Meteor.methods({
removeFAV: function(userID, product_id) {
Favorites.remove(
{ user_id: userID, product_id: product_id },
{ multi: true }
);
}
});
It's not working
Meteor.methods({
removeFAV: function(userID, product_id) {
Favorites.remove(
({ user_id: userID, product_id: product_id }, { multi: true }),
function(err) {
if (err) {
return err;
} else {
return "removed successfully";
}
}
);
}
});
The Meteor Mongo.Collection is not a native Mongo Collection but a wrapper that integrates native Mongo calls into the Meteor environment.
See: https://docs.meteor.com/api/collections.html#Mongo-Collection
The insert update and remove methods have a specific blocking behavior unless you provide a callback:
On the server, if you don’t provide a callback, then remove blocks until the database acknowledges the write and then returns the number of removed documents, or throws an exception if something went wrong.
If you do provide a callback, remove returns immediately. Once the remove completes, the callback is called with a single error argument in the case of failure, or a second argument indicating the number of removed documents if the remove was successful.
https://docs.meteor.com/api/collections.html#Mongo-Collection-remove
Since the blocking type call is automatically throwing en error, there is theoretically no need to explicitly handle the exception:
Meteor.methods({
removeFAV: function(userID, product_id) {
const removedDocs = Favorites.remove(
{ user_id: userID, product_id: product_id },
{ multi: true });
// remove returns the number of docs being removed
return `removed [${removedDocs}] document(s) successfully`
}
});
Such a method will return in the callback of Meteor.call either the thrown error as first parameter or the result as second.
However, it also makes sense to handle the exception and let the method fail silently:
Meteor.methods({
removeFAV: function(userID, product_id) {
let removedDocs = 0
try {
// remove returns the number of docs being removed
removedDocs = Favorites.remove(
{ user_id: userID, product_id: product_id },
{ multi: true });
} catch (e) {
// log error
myErrorLog.log(e)
} finally {
return `removed [${removedDocs}] document(s) successfully`
}
}
});
This will never return an error to the client but logs the error on the server.

catch error: structure (127 [object] [object]) on neo4j cypher session

I have had a similar error before but that was caused by a syntax error in the .run statement.
I have looked this code over and over and can't find a syntax so I think something else is going on. This function is called by the passport deserializer and the value of "id" was confirmed using node-inspector. however, no matter which type of MATCH query I use, I get the same .catch error.
I have tried WHERE option and the direct option...they both work in the neo4j browser. Can someone see what I am not seeing
router.getUserByID = function (id, callback) {
session
.run ("MATCH (user {id(user) : {paramUserID}}) RETURN user",{paramUserID: parseInt(id)})
.then (function(result)
{
if ( !result.records[0])
{
console.log("unknow user by id");
session.close();
if (typeof callback==="function") {
return callback(null,false);
}
} // end of if not found
else
{
console.log("user by id found");
session.close();
if (typeof callback === "function") {
return callback(null,result);
}
}
})
.catch(function(err)
{
console.log("catch error: "+err);
});
} // end of get user by id
You cannot reference the Neo4j id of the node that way, you have to use a WHERE clause:
MATCH (user) WHERE id(user) = {paramUserID} RETURN user;
If you have an application id (almost always a good idea), then you could do:
MATCH (user {uuid: {paramUuid}}) RETURN user;
It would be better with a label though, because different entities could have the same id, and you can use a unicity constraint (which also indexes the values, so the query is faster):
CREATE CONSTRAINT ON (n:User) ASSERT n.uuid IS UNIQUE;
// Later
MATCH (user:User {uuid: {paramUuid}}) RETURN user;

Categories

Resources