mongoose Chaining with then and catch - javascript

How To Convert This Function to Chaining with then and catch?
Is better to Chained?
I mean User.findOne().then().catch()
User.findOne({_id: msg.chat.id}, (err, doc) => {
if (err) {
console.log(err);
}
if (doc) {
console.log(doc.name);
} else {
console.log('Empty');
}
});

The function you pass to then is called with the returned document (or null) if the operation succeeds, and the catch is called with the error if the operation fails (e.g. no connection). Putting it together looks like this:
User.findOne({_id: msg.chat.id})
.then(doc => {
if (doc) {
console.log(doc.name);
} else {
console.log('Empty');
}
}).catch(err => {
if (err) {
console.log(err);
}
});
As an aside, when you are searching for one document by id, then you can use findById:
User.findById(msg.chat.id)
.then(doc => {
if (doc) {
console.log(doc.name);
} else {
console.log('Empty');
}
}).catch(err => {
if (err) {
console.log(err);
}
});

Better switch to ES2017 async/await syntax, you can avoid Promise Hell
async function foo () {
try {
var doc = await User.findOne({_id: msg.chat.id}).exec()
if (doc)
return console.log(doc.name);
console.log('Empty');
} catch (err) { console.log(err) }
}
foo()
This will help you when you're going to nest DB calls or using for...loops.
async function foo () {
try {
var users = await User.find({}).exec()
for (var user in users) {
var tweets = await Tweet.find({_user: user._id}).exec()
user.tweets = tweets.map(t => t.text)
await user.save()
}
} catch (err) { console.log(err) }
}
foo()

Related

How to handle errors with Async and Await [Node.js]

I have already a function written with bluebird promises and I would like to rewrite it with async and await. When I have made the changes I have found out that earlier with promises the reject statement always transfers the control to called function catch block though if the catch block is already there in the file from where we are rejecting. How to handle this situation properly with async and await?. (Added comments to the code to explain the issue)
With Promise:
const callingFunc = (req, res) => {
return new Promise((resolve, reject) => {
// execute request which returns promise
functionCall()
.then((response) => {
let error;
try {
xml2js(response.body, { explicitArray: false }, (err, result) => {
if (err) {
return reject(err); /* throws the correct error to catch block of the file from where callingFunc is called*/
}
if (!_.isEmpty(result.Response.errorCode)) {
return reject(result.Response); /* throws the correct error to the catch block of the file from where callingFunc is called*/
}
return resolve(result);
});
} catch (e) {
error = new Error('xml2js conversion error');
reject(error);
}
})
.catch((error) => {
const Error = new Error('Internal Server Error');
reject(Error);
});
});
};
With async and await:
const callingFunc = (req, res) => {
try {
const response = await functionCall();
let error;
try {
xml2js(response.body, { explicitArray: false }, (err, result) => {
if (err) {
throw (err); /* throws the error to the below catch block and returning xml2js conversion error and changing behaviour*/
}
if (!_.isEmpty(result.Response.errorCode)) {
throw result.Response; /* throws the error to the below catch block and returning xml2js conversion error and changing behaviour*/
}
return result;
});
} catch (e) {
error = new Error('xml2js conversion error');
throw error;
}
} catch(error) {
const Error = new Error('Internal Server Error');
throw Error;
}
};
If functionCall returns a promise, then this code is inappropriate...
return new Promise((resolve, reject) => {
// execute request which returns promise
functionCall()
.then((response) => {
If xml2js is async using callbacks, then it is appropriate to wrap it in a promise...
// return a promise that resolves with the result of xml2js
async function xml2js_promise(body) {
return new Promise((resolve, reject) => {
xml2js(body, { explicitArray: false }, (err, result) => {
if (err) reject(err);
else if (!_.isEmpty(result.Response.errorCode)) reject(result.Response);
else resolve(result);
});
});
}
Now we can await these. There's no need to nest the try's. (And you only need the try if you're going to do something on the catch).
async callingFunction = (req, res) => {
try {
const response = await functionCall();
} catch (error) {
// do something with this error
}
try {
const result = await xml2js_promise(response.body)
} catch(error) {
// do something with this error
}
return result;
}

Proper way to get the result of MySQL from Node.js function callback?

What is the proper way to get the result of this MySQL Query out of GetAllFarms and into a variable called err and farms? Sorry, doing a quick code try and coming from a different language.
var err, farms = GetAllFarms()
console.log("GetAllFarms:")
console.log(farms)
console.log(err)
function GetAllFarms(callback) {
query = db.query("SELECT * FROM farms ", function (err, result) {
console.log("DEBUG:QUERY//");
console.log(query.sql);
// console.log(result)
if (err) {
// console.log(err)
return callback(err, null)
} else {
// console.log(result)
return callback(null, result)
}
});
// db.end()
console.log("query")
console.log(query.result)
return query
}
Any help is much appreciated. Thanks
You have to decide wether you want to provide result via callback or with return. Don't mix them, it's confusable.
Callback approach
var err, farms = GetAllFarms()
console.log("GetAllFarms:")
console.log(farms)
console.log(err)
function GetAllFarms(callback) {
query = db.query("SELECT * FROM farms ", function (err, result) {
console.log("DEBUG:QUERY//");
console.log(query.sql);
// console.log(result)
if (err) {
// console.log(err)
return callback(err, null)
} else {
// console.log(result)
return callback(null, result)
}
});
// db.end()
console.log("query")
console.log(query.result)
}
// usage
GetAllFarms((error, result) => {
if (error) {
// handle error
}
// process result
})
Promise approach
var err, farms = GetAllFarms()
console.log("GetAllFarms:")
console.log(farms)
console.log(err)
function GetAllFarms() {
return new Promise((resolve, rejct) => {
db.query("SELECT * FROM farms ", function (err, result) {
console.log("DEBUG:QUERY//");
console.log(query.sql);
if (err) {
return reject(err)
} else {
return resolve(result)
}
});
});
}
// usage
(async () => {
const res = await GetAllFarms();
// or
GetAllFarms().then(/* ... */).catch(/* ... */);
})

ReferenceError: err is not defined in nodejs

why is that ReferenceError: err is not defined even though it is defined here?
const sampleObject = require('./sampleObject');
const sampleModel = (callback) => {
if (true) {
sampleObject.sampleRetrieval(err, data => {
if (err) {
callback(err)
} else {
callback(data)
}
})
} else {
console.log('Something went wrong.');
}
}
module.exports = sampleModel;
This is the file that executes the query and passes it to the sampleModel.
const myDB = require('../db/database');
module.exports = {
sampleRetrieval: () => {
let sql = "SELECT * FROM ACCOUNTS";
myDB.query(sql, (err, data) => {
if (err) {
callback(null, err)
} else {
callback(err, data)
}
})
}
}
Error : err is not defined
newUser.save()
.then( ()=> res.json('User added!'))
.catch( err => res.status(400).json('Error: '+ err));
Fixed
newUser.save()
.then( ()=> res.json('User added!'))
.catch( (err) => res.status(400).json('Error: '+ err));
The problem is that your function sampleRetrieval is not receiving a callback function as a parameter. It should be something like this:
sampleRetrieval: (callback) => {
let sql = "SELECT * FROM ACCOUNTS";
myDB.query(sql, (err, data) => {
if(err) {
callback(null, err);
} else {
callback(err, data);
}
})
}
And then fix the parentheses like the comment of Dan O says:
const sampleModel = (callback) => {
if (true) {
sampleObject.sampleRetrieval((err, data) => {
if (err) {
callback(err)
} else {
callback(data)
}
})
} else {
console.log('Something went wrong.');
}
}
Hope it helps!

Mongoose .catch block is the same in multiple places. How do I avoid this?

I use mongoose promises in my Node REST api and I have a lot of .catch blocks that are the same.
Here is a short route:
router.get('/', (req, res) => {
Post.find()
.populate('category', 'name')
.exec()
.then(posts => {
res.status(200).json(posts);
})
.catch(err => {
log.error(err);
res.status(500).json(err);
});
});
I have some routes where that exact .catch block appears three times in the same function.
Can I create a function where to have the log and result and pass the function to the .catch? Or can I use some middleware? How can I clean my code a bit?
Here is the mess:
router.delete('/:commentId', checkAuth, (req, res) => {
// Find the comment
Comment.findById(req.params.commentId).exec()
.then(comment => {
foundComment = comment;
// Delete the comment
Comment.deleteOne({ _id: req.params.commentId }).exec()
.then(result => {
if (result.deletedCount) {
// If the comment has replies, remove them as well
if (foundComment.replies.length > 0) {
Comment.deleteMany({ _id: { $in: foundComment.replies } }).exec()
.then(result => {
if (result.deletedCount) {
res.status(200).json({
deletedCount: result.deletedCount + 1
});
}
})
.catch(err => {
log.error(err);
res.status(500).json(err);
});
return;
}
res.status(200).json({
deletedCount: result.deletedCount
});
} else {
res.status(404).json({
message: `Comment with id ${req.params.commentId} doesn't exist.`
});
}
})
.catch(err => {
log.error(err);
res.status(500).json(err);
});
})
.catch(err => {
log.error(err);
res.status(500).json(err);
});
});
Return each of the Promises in the Promise chain, so that they're all tied together, allowing you to put a single .catch at the very end, which will run if anything throws inside:
router.delete('/:commentId', checkAuth, (req, res) => {
let foundComment;
Comment.findById(req.params.commentId).exec()
.then(comment => {
foundComment = comment;
return Comment.deleteOne({ _id: req.params.commentId }).exec();
})
.then(result => {
if (!result.deletedCount) {
res.status(404).json({
message: `Comment with id ${req.params.commentId} doesn't exist.`
});
return;
}
if (foundComment.replies.length <= 0) {
res.status(200).json({
deletedCount: result.deletedCount
});
return;
}
return Comment.deleteMany({ _id: { $in: foundComment.replies } }).exec()
.then(result => {
if (result.deletedCount) {
res.status(200).json({
deletedCount: result.deletedCount + 1
});
}
});
})
.catch(err => {
log.error(err);
res.status(500).json(err);
});
});
Generally, try to avoid nesting .thens if you can help it - flatter code is more readable code. async/await will make things even clearer:
router.delete('/:commentId', checkAuth, async (req, res) => {
try {
const foundComment = await Comment.findById(req.params.commentId).exec();
const deleteOneResult = await Comment.deleteOne({ _id: req.params.commentId }).exec();
if (!deleteOneResult.deletedCount) {
res.status(404).json({
message: `Comment with id ${req.params.commentId} doesn't exist.`
});
return;
}
if (foundComment.replies.length <= 0) {
res.status(200).json({
deletedCount: result.deletedCount
});
return;
}
const deleteManyResult = await Comment.deleteMany({ _id: { $in: foundComment.replies } }).exec()
if (deleteManyResult.deletedCount) {
res.status(200).json({
deletedCount: deleteManyResult.deletedCount + 1
});
}
} catch (err) {
log.error(err);
res.status(500).json(err);
}
});
I suggest changing your code to use async/await and use try/catch to handle error.
router.get('/', async (req, res) => {
try {
let posts = await Post.find().populate('category', 'name');
let comment = await Comment.findById(req.params.commentId);
if (!comment) {
return res.status(404).json({
message: `Comment with id ${req.params.commentId} doesn't exist.`
});
}
// Another query...
res.status(200).json(posts);
} catch(err) {
log.error(err);
res.status(500).json(err);
});
});
You could pass a function directly to .catch like:
function handleError(err) {
console.error('the error:', err.message)
}
const promise = new Promise((resolve, reject) => reject(new Error('your error')))
promise.catch(handleError)

mongodb nodejs store value outside connect

I need a way to store value outside mongoDB connect call:
read(object) {
let result
MongoClient.connect(this.url, function (err, db) {
if (err!=null){
result = err;
} else {
db.collection(object.collection).find(object.field).toArray(function(err, docs) {
assert.equal(err, null);
db.close();
result = docs;
});
}
});
return result
}
When i call this method, which is part of a class, return is called before result assignment, as normal.
Example: console.log(read(obj)) returns undefined
The idea is to store value in a variable and return might wait until connect terminate.
Is there any way to resolve this problem?
Without promise:
Call return inside find and err function:
read(object) {
let result
MongoClient.connect(this.url, function (err, db) {
if (err!=null){
result = err;
return result; //here
} else {
db.collection(object.collection).find(object.field).toArray(function(err, docs) {
assert.equal(err, null);
db.close();
result = docs;
return result; // here
});
}
});
}
Or set a timeout for return with enough time to wait for other process to end:
read(object) {
let result
MongoClient.connect(this.url, function (err, db) {
if (err!=null){
result = err;
} else {
db.collection(object.collection).find(object.field).toArray(function(err, docs) {
assert.equal(err, null);
db.close();
result = docs;
});
}
});
setTimeout(function(){ return result}, 3000); // 3secs
}
With Promise you can try the following:
function read(object) {
let result
return new Promise((resolve, reject) => {
MongoClient.connect(this.url, function (err, db) {
if (err!=null){
reject(err);
} else {
db.collection(object.collection).find(object.field).toArray(function(err, docs) {
db.close();
if (err) {
reject(err);
} else {
resolve(docs);
}
});
}
});
});
}
// then you can call the function and use the result like this
read(obj).then(docs => {
console.log(docs);
})
.catch(err => {
// handle error
console.log(err);
})

Categories

Resources