Try/Catch catches promise reject... any way around that? - javascript

I have to use try/catch because MongoDb's ObjectId() will break/error if arg is to short.
The promise .catch never fires when the try is good...it seems that the outer catch(e) will catch the promise reject(). Is this expected behavior? anyway around that?
function deleteUser(req, res) {
try {
var userId = { '_id': ObjectId(String(req.params.user_id)) };
var petData = _getAllData(userId);
petData
.then(function(doc) {
data.deleteAllData(doc);
result = user.remove(userId);
_returnApi(result, res);
})
.catch(function(err) {
console.log(`error delete user -${err}`);
res.status(500).json({ error: "error deleting user" });
});
}
catch(e) {
res.status(400).json({ error: "user id format incorrect" });
}
}

Per those two issues 1 and 2 are discussed in Mongoose issue site, after mongoose v4.2.5, the Invalid ObjectId could be caught in find method through exec(), here are the sample codes test under mongoose v4.4.2
Foo.find({_id: 'foo'}) // invalid objectId
.exec()
.then(function(doc) {
console.log(doc);
})
.catch(function(err) {
console.log(err);
})
Output:
{ [CastError: Cast to ObjectId failed for value "foo" at path "_id"]
message: 'Cast to ObjectId failed for value "foo" at path "_id"',
name: 'CastError',
kind: 'ObjectId',
value: 'foo',
path: '_id',
reason: undefined }
However, according to this issue, the update method is still failed.

The way I see it, you can reduce it to single catch block, but return different messages based on the error type then:
function deleteUser(req, res) {
let userId;
return Promise.resolve({ '_id': ObjectId(String(req.params.user_id))})
.then(_userId => {
userId = _userId;
return _getAllData(userId);
}).then(doc => {
data.deleteAllData(doc);
result = user.remove(userId);
return _returnApi(result, res);
}).catch(err => {
if(!userId) return res.status(400).json({ error: "user id format incorrect" });
console.log(`error delete user -${err}`);
res.status(500).json({ error: "error deleting user" });
});
}
}

Related

Rejection because of unhandled promise in Node Js

Hope you are all doing fine!
I am running with some difficulties when I want to deploy this api. I keep on receiving the message:"UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection"
My guess is that I am sending a response twice, but I cannot determine where. Does anyone know what could be going on?
router.post('/add', async (req, res) => {
const friend = await User.findOne({username:req.body.friend})
const user = await User.findById(req.body.id)
if(friend && friend != req.headers.username) {
user.friends.find((x) => {
switch(friend.username){
case user.username:{
res.status(401).json({
status: "Failed",
message: "We are sorry but you cant add yourself as friend",
data:null
})
}
case x.friend_username: {
res.status(401).json({
status: "Error",
message: `Sorry, your friend has been already added`,
data: []
})
}
default: {
User.findByIdAndUpdate(req.body.id, {
$addToSet:{
friends: {
friend_id: friend.id,
friend_username: friend.username
}
}
}, {
upsert: true,
safe: true
})
.then(result => {
res.status(200).json({
status: "Success",
message: `Friend has been added correctly! `,
data: result
})
})
.catch((err)=>{
res.status(500).json({
status: "Failed",
message: "Database Error",
data: err
})
})
}
}
})
} else {
res.status(404).json({
status: "Failed",
message: "We are sorry but the username was not found",
data:null
})
console.log(`There has been an failed attempt of adding a new user. \nUser: ${req.headers.username} `)
}
})
`
Try catch block might help with debugging. And it must be used with async/await to catch unhandled promises.
I am assuming that the problem is somewhere within User.findOne() or User.findById(). Ether they are working incorrectly, ether you're passing data in request incorrectly.
router.post('/add', async(req,res)=>{
try {
const friend = await User.findOne({username:req.body.friend})
const user = await User.findById(req.body.id)
if(friend&&friend!=req.headers.username){
user.friends.find((x)=>{
switch(friend.username){
case user.username:{
res.status(401).json({
status: "Failed",
message: "We are sorry but you cant add yourself as friend",
data:null
})
}
case x.friend_username:{
res.status(401).json({
status: "Error",
message: `Sorry, your friend has been already added`,
data: []
})
}
default:{
User.findByIdAndUpdate(req.body.id,{
$addToSet:{friends:{
friend_id:friend.id,
friend_username:friend.username
}}
},{upsert:true,safe:true})
.then(result=>{
res.status(200).json({
status: "Success",
message: `Friend has been added correctly! `,
data: result
})
})
.catch((err)=>{
res.status(500).json({
status: "Failed",
message: "Database Error",
data: err
})
})
}
} }
)}
else{
res.status(404).json({
status: "Failed",
message: "We are sorry but the username was not found",
data:null
})
console.log(`There has been an failed attempt of adding a new user. \nUser: ${req.headers.username} `)
}
} catch(err) {
console.log(err);
}
})
The combination of find() and switch without breaks is probably at the cause, and certainly scrambles the logic.
There are a few other things in the code that can be improved, also. Here's an edit with the changes described in comments...
router.post('/add',async(req,res)=>{
// surround the async calls with try/catch
try {
const friend = await User.findOne({ username:req.body.friend });
const user = await User.findById(req.body.id);
// detect and throw app-level errors. do the express response in the catch
// get the most basic out of the way first. we need a user and a friend for the rest of the route to work
if (!user || !friend) {
throw {
status: 404,
json: {
status: "Failed",
message: "user id or friend name not found",
data: null
}
}
}
// user adding themself as a friend doesn't need a loop to check
if (friend.username === user.username) {
throw {
status: 401,
json: {
status: "Failed",
message: "We are sorry but you cant add yourself as friend",
data:null
}
}
}
// the loop that's needed here is hidden in includes()
if (user.friends.includes(friend.username)) {
throw {
status: 401,
json: {
status: "Error",
message: `Sorry, your friend has been already added`,
data:null
}
}
}
// now, all of the errors have been detected and thrown
// do the upsert with another await and send good status
const addToSet = {
$addToSet:{friends:{
friend_id:friend.id,
friend_username:friend.username
}}
};
// note we await here (not then) since that's the style chosen above
const result = await User.findByIdAndUpdate(req.body.id, addToSet,{ upsert:true, safe:true });
res.status(200).json({
status: "Success",
message: `Friend has been added correctly! `,
data: result
});
} catch (error) {
console.log(error);
res.status(error.status).json(error.json);
}
}

Firebase function promise undefined TypeError

I am currently using the firebase functions to call the following doc from the database:
let token, userId;
db.doc(`/users/${newAccount.username}`)
.get()
.then((doc) => {
if (doc.exists === false) {
return firebase.auth().createUserWithEmailAndPassword(newAccount.email, newAccount.password).catch(err => console.error(err));
} else {
res.status(400).json({ username: 'this username is already taken' });
}
})
.then(data => {
userId = data.user.uid;
return data.user.getIdToken();
})
.then((idToken) => {
token = idToken;
const userCredentials = {
username: newAccount.username,
email: newAccount.email,
created: new Date().toISOString(),
userId
};
return db.doc(`/users/${newAccount.username}`).set(userCredentials);
})
.then(() => {
return res.status(201).json({ token });
})
.catch((err) => {
console.error(err);
if (err.code === 'auth/email-already-in-use') {
return res.status(400).json({ email: 'Email is already is use' });
} else {
return res.status(500).json({ general: 'Something went wrong, please try again' });
}
});
The code runs fine but there is a logged error, if the doc exists in the database:
TypeError: Cannot read property 'user' of undefined
I presume the promise is still running and I am a bit stuck on how to end it?
Any help would be grateful. Thanks.
Your second then callback is going to get invoked in all situations. Sending the 400 response in the first callback isn't actually going to stop the promise from propagating to all of the following then callbacks.
If you want to stop the chain of then callbacks from executing, you should instead throw an error to get picked up by a catch down the chain, skipping all the then.

Chaining more than one promise function in Node.js for mongoDB

There is a requirement, that review can be added only when product and user both are found. So i have written code in order to implement this scenario.
User.findById(req.params.userId).exec()
.then(response => {
console.log("response", response);
if (response) {
return Product.findById(req.params.productId).exec();
}
else {
return res.status(404).json({
message: 'User not found'
})
}
})
.then(response => {
console.log("response", response);
if (!response) {
return res.status(404).json({
message: 'Product not found'
})
}
const review = new Review({
_id: mongoose.Types.ObjectId(),
rev: req.body.rev,
productId: req.params.productId,
userId: req.params.userId
});
return review.save();
})
.then(response => {
console.log("responseeeeeeee", response);
res.status(201).json({
response
})
})
.catch(error => {
console.log("error", error);
res.status(500).json({
error
})
})
This is working fine, but as soon as product or user is missing it is throwing a warning as such :
(node:17276) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (D:\backup-learning\project-shop-always\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (D:\backup-learning\project-shop-always\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (D:\backup-learning\project-shop-always\node_modules\express\lib\response.js:267:15)
at User.findById.exec.then.then.then.catch.error (D:\backup-learning\project-shop-always\api\controllers\review-controller.js:58:29)
at process._tickCallback (internal/process/next_tick.js:68:7)
The problem is that the chain continues with the return value of res.status as the fulfillment value when you do return res.status(/*...*/) from within the then callback.
You can't use a single chain for this. Also, since you need to locate both the user and the product, probably best to do that bit in parallel. See comments:
// *** Start both the product and user search in parallel
Promise.all([
User.findById(req.params.userId).exec(),
Product.findById(req.params.productId).exec()
])
.then(([user, product]) => {
// *** Handle it if either isn't found
if (!user) {
res.status(404).json({
message: 'User not found'
});
} else if (!product) {
res.status(404).json({
message: 'Product not found'
});
} else {
// *** Both found, do the review
const review = new Review({
_id: mongoose.Types.ObjectId(),
rev: req.body.rev,
productId: req.params.productId,
userId: req.params.userId
});
// *** Return the result of the save operation
return review.save()
.then(response => {
console.log("responseeeeeeee", response);
res.status(201).json({
response
});
}
}
// *** Implicit return of `undefined` here fulfills the promise with `undefined`, which is fine
})
.catch(error => {
// *** An error occurred finding the user, the product, or saving the review
console.log("error", error);
res.status(500).json({
error
})
});
If you're doing this in any modern version of Node.js, you can use an async function and await:
// *** In an `async` function
try {
const [user, product] = await Promise.all([
User.findById(req.params.userId).exec(),
Product.findById(req.params.productId).exec()
]);
if (!user) {
res.status(404).json({
message: 'User not found'
});
} else if (!product) {
res.status(404).json({
message: 'Product not found'
});
} else {
// *** Both found, do the review
const review = new Review({
_id: mongoose.Types.ObjectId(),
rev: req.body.rev,
productId: req.params.productId,
userId: req.params.userId
});
const response = await review.save();
console.log("responseeeeeeee", response);
res.status(201).json({
response
});
}
} catch (error) {
console.log("error", error);
res.status(500).json({
error
})
}
Note that the entire code is wrapped in a try/catch, so the async function this is in will never reject (unless the console.log or res.send in the catch block raised errors), so it won't result in an unhandled rejection warning if you just make your Express endpoint handler async (whereas normally passing an async function into something that isn't expecting to receive a promise is an anti-pattern). (If you want to be a bit paranoid, wrap the contents of the catch block in another try/catch.)

unable to connect express.js with oracledb

I am connecting Oracle database using express.js.
oracledb.getConnection(config, function (err, connection) {
if (err) console.log(err);
connection.execute("SELECT * FROM database", function (error, results, fields) {
if (error) {
console.log(error);
res.json({
status: false,
message: "there are some error with query"
})
} else {
if(results !="" && results!= null && results!= undefined){
res.json({
data: results,
status: true,
message: "Data get from db"
})
}
}
})
})
The problem is that when I am running API I got the following error in cmd:
TypeError: Cannot read property 'execute' of undefined'
So after getting this error, I have searched and I got a solution. Then I change my code in the following way:
(async function () {
try {
connection = await oracledb.getConnection({
user: 'xyz',
password: 'xyz',
connectString: 'xyz'
});
result = await connection.execute("SELECT * FROM database");
res.json({
data: result,
status: true,
message: "Data get from db",
length: results.length
})
} catch (err) {
console.error(err.message);
} finally {
if (connection) {
try {
await connection.close(); // Always close connections
} catch (err) {
console.error(err.message);
}
}
}
})();
But after implementing this asynchronous way, I got another errro:
can not locate oracle 64bit client library: "some path.../oci.dll is not the correct architecture"...
UnhandledPromisePejectionWarning: connection is not defined....
UnhandledPromisePejectionWarning: this error is originated either by throwing inside of an async function without a catch, block or by rejecting a promise which was not handled with .catch()
What am I doing wrong?

How can i handle error with redis and bluebird?

I've been facing with problem with redis and async await.
I have old redis.get with callback:
redis.get(token, async (error, result) => {
if (error) {
return res.status(404).json({ msg: 'Confirm token is invalid.' });
}
if (result === null) {
return res.status(400).json({ msg: 'Confirm token is expired.' });
}
})
But i will want to refactor him to async/await
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
const result = async redis.asyncGet(token)
I successfully get the result, BUT how can i get the error ?
Thanks
Having your code, you just need to surround the redis call using async/await syntax in a try/catch statement:
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
try {
const result = redis.get(token);
} catch (e) {
return res.status(400).send({ msg: 'Confirm token is expired.' })
}

Categories