Mongoose async/ await correct method - node.js

I'm new to mongoose and node could anyone please point out which is the right method, my intention is to send the list of all products in my MongoDB to client.
method 1
items = await Product.findById({})
.then((items) => {
if (items == null) {
res.status(404).send("no items");
} else {
res.send(items);
next();
}
})
.catch((err) => {
res.status(404).send({ error: err });
});
method 2
items = await Product.find({}).exec();
res.status(200).send(items)
next();
Also could you point out how can i use again one more await call if i have to process items and send after that.Thanks in advance.

Method 2 should looks like that:
router.get('/:param', async function(req, res) {
try {
let items = await Product.find({}).lean()
res.status(200).json(items);
} catch (e) {
res.status(404).json(e);
}
});
You don't need .exec()
You need to use .lean() if you want to receive js object in items, not mongoose's.
Try not to request all docs collection at once, especially if collection have more then 1000+ documents. Otherwise do it via .cursor or limit(Number) to avoid problems with DB.
Also as already mentioned above, try to use async/await and ES6 syntax. It's just looks better.
And please, mark question as answered, just to avoid your question to be flagged.

Related

Find document from mongoose collections with specific condition

Recently I start using MongoDB with Mongoose on Nodejs.
This code works as it should, and returns me all data i need :
const getAllPosts = async () => {
try {
return (await PostModel.find().populate('user')).reverse();
} catch (error) {
console.log(error);
throw Error('Error while getting all posts');
}
};
But now I only need individual posts, which in the tags (represented as an array in the PostModel) contain the data that I will pass in the request.
For example, I will make a GET request to /posts/tag111 and should get all posts that have "tag111" in the tags array.
Any ways to do this?
If you are using expressjs, your route should be something like:
whatever.get('/:tag', postController.getAllPostTagged)
The function you use in your route (called getAllPostTagged above) should be similar to this one, in which you get the path param tag from req:
const postController = {
getAllPostTagged = async(req, res) => {
try {
return (await PostModel.find({tags: req.params.tag}));
} catch (error) {
console.log(error);
throw Error('Error while getting all posts');
}
}
}
The key here is to know where the params are obtained (from req.params) .

Async/await in Express with multiple MongoDB queries

I have a fairly straightforward CRUD app which renders the results of two queries onto one page. The problem that arose once I got this to "work" was that the page required a refresh in order to display the results. On first load, no results were displayed.
I came to figure out that this is a problem/symptom of Node's asynchronous nature. I've been trying to approach this problem by using async/await, and from hours of messing with things, I feel like I'm quite close to the solution, but it's just not working out - I still need a manual refresh to display/render the results on the .ejs page.
The code:
var entries = [];
var frontPageGoals = [];
app.get('/entries', async (req,res) => {
if (req.session.password) {
const entriesColl = await
db.collection('entries')
.find()
.sort({date: -1})
.toArray((err, result) => {
if (err) { console.log(err) }
else {
for (i=0; i<result.length; i++) {
entries[i] = result[i];
}
}
});
const goalsColl = await
db.collection('goals')
.find()
.toArray((err, result) => {
if (err) {console.log(err)}
else {
for (i=0; i<result.length; i++) {
frontPageGoals[i] = result[i];
}
}
});
res.render('index.ejs', {entries: entries, frontPageGoals: frontPageGoals});
}
else {
res.redirect('/');
}
});
Now, I can conceive of a few problems here, but honestly I'm just at my wits end trying to figure this out. For example, I'm sure it's problematic that the empty lists which will contain the results to be passed when the page renders are outside the actual async function. But after trying to move them a dozen different places within the async area... still no dice.
Any help would be hugely appreciated! This is basically the last big "thing" I need done for this app.
I'm not 100% sure about your database driver, but assuming that the toArray() returns a promise (which it does in the default mongodb driver), the await will actually return the value you expect in your callback, result in your case, or in case there was an error, which you expected it as err in your callback, it will be thrown, thus forcing you to use try-catch blocks, in your case, you would just use console.log(err) in the catch block, since you aren't doing any handling
Here's your code after updating :
app.get("/entries", async (req, res) => {
if (req.session.password) {
try {
const entries = await db
.collection("entries")
.find()
.sort({ date: -1 })
.toArray();
const frontPageGoals = await db
.collection("goals")
.find()
.toArray();
res.render("index.ejs", {
entries: entries,
frontPageGoals: frontPageGoals
});
} catch (err) {
console.log(err);
}
} else {
res.redirect("/");
}
});
EDIT
However, if you don't know about promises -which async/await are basically promises-, and wanna just do it using callbacks -not advised-, you would have to just send your response in the callback, and nest the 2nd query in the first query's callback, here is the code,, with some comments to hopefully help you out:
app.get("/entries", (req, res) => {
if (req.session.password) {
// First query
db.collection("entries")
.find()
.sort({ date: -1 })
.toArray((err, entryResult) => {
if (err) {
console.log(err);
} else {
// In the callback of the first query, so it will
// execute 2nd query, only when the first one is done
db.collection("goals")
.find()
.toArray((err, frontPageResult) => {
if (err) {
console.log(err);
} else {
// In the callback of the 2nd query, send the response
// here since both data are at hand
res.render("index.ejs", {
entries: entryResult,
frontPageGoals: frontPageResult
});
}
});
}
});
} else {
res.redirect("/");
}
});
I have removed the async keyword since you no longer need it
I renamed the callback arguments, instead of just result, because both callbacks would have the same argument name, and you would have had to store it in a temp variable

ExpressJS: Why does this output [] on the first GET, and then the next GET returns the data from the previous?

I am experimenting with Express and MongoDB, and have a functional API server that can add, update, delete, and retrieve a single post. The issue I have run into is returning all of the documents from Mongo.
I have a GET route that outputs the results, except it does not behave as I imagined it would. When you run this, the first GET request to /notes returns and empty array, i.e. []
let notes =[];
app.get('/notes', (req, res) => {
async function getNotes() {
try {
await db.collection('notes').find().forEach(function (myDoc) {
notes.push(myDoc);
})
} catch(err) {
console.log(err)
}
console.log(notes);
res.send((notes));
}
getNotes();
});
On the second GET to /notes, however, the data that was pushed into notes[] is returned, and it is then overwritten by the newly pushed data.
Can anyone help me fill in the blank spot in my understanding of this? I imagine there is something that I just didn't understand along the way.
Edit***
I have experimented with this a bit, and am still running into the same issues.
let array= [];
async function getNotes() {
try {
await db.collection('notes').find().toArray(function (err, notesArray) {
array = notesArray;
})
} catch (err) {
console.log(err)
}
console.log(array);
return array;
}
app.get('/notes', (req, res) => {
getNotes();
res.send(array);
});
MongoDB's .toArray() won't both invoke a callback and return a Promise.
Returns:
Promise if no callback passed
And, await depends on the Promise, so you won't be able to use the callback as well.
Though, one isn't really necessary. When the promise resolves, await will return the array of documents, allowing you to assign that to array or any other variable.
try {
array = await db.collection('notes').find().toArray();
}

How to check if a collection already exists in ArangoDB

Say I have a collection Col1 that already exists in my database. So, doing something like:
var col = db.collection('Col1');
col.save({"name":"something"});
will work perfectly fine.
But if a collection Col2 that doesn't already exist in my database is tried with the same thing i.e
var col = db.collection('Col2');
col.save({"name":"something"})
will work perfectly fine as well. Only that it doesn't exist and will be not shown in my database. Had it thrown some error or stuffs I could have used try and catch statements for result. But since that is off the plate, how do I know if a collection already exists ?
There are two things going on here that may be confusing.
First of all, arangojs (unlike the internal JS API of ArangoDB) is asynchronous for everything that needs to talk to the actual ArangoDB server. Asynchronous functions are marked as "async" in the documentation.
You can pass a node.js-style callback (like the async functions in built-in node.js modules, e.g. fs, http, etc) to these methods. Alternatively you can simply omit the callback and the method will return a promise for the result. You can learn more about how promises work in Mozilla's JavaScript reference documentation (this is not specific to Mozilla -- their reference is just very good and generally correct).
The other thing you're running into is the distinction between collection objects in arangojs and actual collections in ArangoDB. The driver lets you create collection objects for collections regardless of whether they exist yet or not. When trying to use them if the collection doesn't actually exist, you will of course see an error.
var col = db.collection('whatever');
col.create() // create the collection if it doesn't exist
.catch(function () {}) // ignore any errors
.then(function () {
return col.get(); // make sure the collection exists now
})
.then(function () {
return col.save({some: 'data'});
})
.then(function (result) {
// everything went fine
})
.catch(function (e) {
console.error('Something went wrong', e.stack);
});
Or using async/await (if you use Babel or read this answer one year from now):
var col = db.collection('whatever');
try {
await col.create(); // create the collection if it doesn't exist
} catch (e) {} // ignore any errors
try {
await col.get(); // make sure the collection exists now
const result = await col.save({some: 'data'});
// everything went fine
} catch (e) {
console.error('Something went wrong', e.stack);
}
Or using node.js-style callbacks because you're oldschool or really like pyramids:
var col = db.collection('whatever');
col.create(function () { // create the collection if it doesn't exist
// ignore any errors
col.get(function (err) { // make sure the collection exists now
if (err) {
console.error('Something went wrong', err.stack);
return;
}
col.save({some: 'data'}, function (err, result) {
if (err) {
console.error('Something went wrong', err.stack);
return;
}
// everything went fine
});
});
});
col.save does not execute the save operation immediately but returns a promise. So it will always succeed. The solution is to wait for the promise to be resolved and then react upon whether an error occurred or not:
var col = db.collection('Col2');
col.save({"name":"something"}).then(
meta => console.log('Document saved:', meta._rev),
err => { console.error('Failed to save document:', err.errorNum, err.response.body.errorMessage); }
);
https://docs.arangodb.com/3.1/Manual/DataModeling/Collections/DatabaseMethods.html#collection states
returns a single collection or null db._collection(collection-name)
so you can use
var col2 = db._collection('Col2');
if (col2) {
// collection exists
col2.save({"name":"something"});
}
This is old, but there is an exists() function.
Example in typescript/node
const metaResult = db.collection('myCollection');
if(!await metaResult.exists()) {
await db.createCollection('myCollection');
}

Using Mongoose promises to do find and update

I'm trying to using the Mongoose Promises to have a cleaner code (see nested functions).
Specifically, I'm trying to build something like this:
Model.findOne({_id: req.params.id, client: req.credentials.clientId}).exec()
.then(function(resource){
if (!resource) {
throw new restify.ResourceNotFoundError();
}
return resource;
})
.then(function(resource) {
resource.name = req.body.name;
return resource.save; <-- not correct!
})
.then(null, function(err) {
//handle errors here
});
So, in one of the promises I would need to save my model. As of the latest stable release, Model.save() does not return a promise (bug is here).
To use the classical save method, I could use this:
//..before as above
.then(function(resource) {
resource.name = req.body.name;
resource.save(function(err) {
if (err)
throw new Error();
//how do I return OK to the parent promise?
});
})
But as also commented in the code, how do I return to the holding promise the return value of the save callback (which runs async)?
Is there a better way?
(btw, findOneAndUpdate is a no-go solution for my case)
One way of doing it would be to wrap the .save code in your own method which returns a promise. You'll need a promise library, like RSVP or Q. I'll write it in RSVP, but you should get the idea.
var save = function(resource) {
return new RSVP.Promise(function(resolve, reject) {
resource.save(function(err, resource) {
if (err) return reject(err);
resolve(resource);
});
});
}
Then in your calling code:
// ...
.then(function(resource) {
resource.name = req.body.name;
return save(resource);
})
.then(function(resource) {
// Whatever
})
.catch(function(err) {
// handle errors here
});
The other way of doing it would be to nodeify the save method, but I'd do it they way I've detailed above.

Resources