Hi. How to use find query in mongoose - Express + MongoDB base - node.js

What I'm doing is trying to write logic for cart in my web-app what I'm creating. Problem what I'm dealing right now is that when I use query to find cart by id :
const isCart = await cartModel.findById("5f82372f2654ce1d18553ac4");
or like this
const isCart = await cartModel.find({ _id : "5f82372f2654ce1d18553ac4"} );
if cart exist with this id all works good it returns me this cart, but if in DB cart doesn't exist with this id then it throws me an error. WHY (pic below) ?? It would make more sense if it returns empty array or object so I can continue building logic of I want to do. But once it throws error it shuts down all further logic. I can't write if there is nothing found, create new cart and so on. Hopefully you got what I mean. Is it something I don't write correctly query or it is the way it is. If so, then I guess I should already control it on client side - if it returns error to a client side then client side sends new request to a new rout on creating a new cart and so on...
Here is solution what I did after someone told me that I might try do with try - catch
Here is a result, it works I'm not sure if this solution is right tho
async function sendCart (code, cart, res){
res.status(code).json({
status: "success",
data: {
cart
}
})
}
exports.createCart = async (req, res, next) => {
try{
const isCart = await cartModel.findById(req.params.id);
await sendCart(200, isCart, res);
}
catch(error){
const newCart = await cartModel.create({ items: [] });
await sendCart(201,newCart, res);
}
}

Related

mongoose throws internal server error when ID doesn't exist

mongoose findOne query throws Interal Server Error (500) when looking for a value that doesn't exists.
I'm pretty sure it should return null or empty array instead of throwing error.
Mongoose version: ^5.8.7
First attempt
router.get('/:myId', async(req, res, next) => {
const myId = req.params.myId;
await myDocument.findOne({ _id: myId }, (err, myData) => {
if (!myData) {
res.status(404).render('404');
} else {
res.render('myPage', myData);
}
}).exec()
});
second attempt
router.get('/:myId', async(req, res, next) => {
const myId = req.params.myId;
const myData = myDocument.findOne({_id: myId}).exec();
if (myData) {
//render normal page
}
// render 404 page
});
According to documentation, this should NOT happen.
Note: conditions is optional, and if conditions is null or undefined, mongoose will send an empty findOne command to MongoDB, which will return an arbitrary document. If you're querying by _id, use findById() instead.
Also tried to use findById() and find(). All cases the same happens.
It works perfectly when I pass a valid ID parameter.
How to search for data without throwing http error in case Id doesn't exists in collection?
router.get('/:myId', async(req, res, next) => {
You should check your route
You should try this code. First assign the ObjectId on top where are dependencies being called.
MongoDB id is not string. It is adviseble to pass the ObjectId.
const { ObjectId } = require('mongodb');
router.get('/:myId', async(req, res, next) => {
const myId = ObjectId(req.params.myId);
const myData = await myDocument.findOne({_id: myId}).exec();
if (myData) {
//render normal page
}
// render 404 page
});
The error was actually happening during the page render.
In fact, mongoose does NOT throws any error in this case.
I was trying to render a html page while setting ejs as View Engine.
Apparentely we can't mix things.

Implementing Transaction in node js using mongoose

I am Trying to implement transaction in my project, since I would like to update more than one collection on the same api
I am sharing the sample code, I tried to understand the concept
sample code
const mongoose = require("mongoose");
const session = await mongoose.startSession();
session.startTransaction();
const opts = { session };
try {
const sample = await sample_table.findOneAndUpdate({
user_name : "nishanth"
},{status: "Inactive"},
{opts});
const transaction = await transaction_table.findOneAndUpdate({
bank_name : SBI
},{status: "Inactive"}, { opts });
await session.commitTransaction();
session.endSession();
return res.send({message: "success", sample_data: sample, transaction_data:
transaction});
}catch(err)
{
await session.abortTransaction();
session.endSession();
return res.send({err: err.message});
}
In the above code, I am trying to update two collections status to Inactive with a filter.
I had give an undefined variable for the transaction_table so the second update query will fail before commit and go to catch block and perform abortTransaction
When I use the above code, It updates the sample_table collection in db. could someone explain me where I am going wrong and share me a sample project for the above example
I am using "mongoose": "^5.13.14", version

JSON object persisting state from previous calls in nodejs express

In my express code I have a config in json, refer to the "template" variable below. I am doing some modifications in it for some purpose in call one and then in my second call when I am trying to get it, it is returning the modified value of field xyz that is A while I expect it to return the original config.
I did try to move that first line "const template = xxx" inside each method so that a new object gets created but it is still having the same issue. How can I fix this?
const express = require("express");
const { db, pgp } = require("../../helpers/dbConnection");
const { auth } = require("../middlewares/Auth");
const router = express.Router();
const template = require('../../config/template.json');
router.get("/callone", async (req, res) => {
try {
template.xyz = "A"
return res.status(200).send();
}
catch (err) {
return res.status(500).send(err.message);
}
});
router.get("/calltwo", async (req, res) => {
try {
return res.status(200).send(template);
}
catch (err) {
return res.status(500).send(err.message);
}
});
This approach is generally a bad idea.
It doesn't work if you ever want to scale beyond 1 server.
It doesn't work when there's more than 1 user using the system at the same time.
The right way to handle something like this is by adding either a database, or a session system.
Sessions feels like the right approach here, because that concept is specific for letting you store information specific to a user, and store/retrieve that over the course of several requests.

How to query MongoDB using aysnc/await?

So I'm following a course on Udemy to learn web development using Node/Express/MongoDB and using mongoose to interact with the database.
I have reached a lecture where the instructor started explaining on how to query the database so here created a mongoose model and named it Tour and in a controller file he wrote this to query the data that we already imported like this:
exports.getAllTours = async (req, res) => {
try {
const queryObj = { ...req.query };
const exludedFields = ["sort", "limit", "page", "fields"];
exludedFields.forEach(el => delete queryObj[el]);
const query = Tour.find(queryObj);
const tours = await query
res.status(200).json({
status: 'success',
data: {
tours: tours
}
})
} catch (err) {
res.status(404).json({
status: 'fail',
message: err.message
});
}
}
As you can see from the code above we used the handler function getAllTours to get all the tours which by the way it handles this route app.get('/api/v1/tours/').
At the beginning of the lecture the code for the query was like this: const query = await Tour.find(queryObj); and then he removed the await keyword to look like the code above were he included the await keyword for the tours object later.
The instructor explained this by saying that if we used the await on the query like this const query = await Tour.find(queryObj); then the code will execute immediately and then we would not be able to chain other methods on the query like .where() or .equals().
I need someone to explain these questions for me:
why would the code execute immediately if we used await?
the Tour.find() does need time to query the database, so I think we should add await, shouldn't we?
The Tour.find() returns a promise (is an async call), adding the await in front of it will wait until the promise is resolved and return it results.
If you want to do a where() after you do the find you can do it after the query is executed since you will have the results in that object in your case the tours object.
Honestly I prefer doing const tours = await Tour.find() you save a varaible that is kinda of innecesary, just is necesary if you want to catch an error of the promise or something like that.
In your case you using a try catch so the error will go to the catch block if something happens with the query (connection issues or other problems)

How can I force my function to wait promises before moving on?

I read another posts but they were not solving my particular problem.
I'm trying to move all dependent entries from one user to a default one before deleting the user. But even with Promise.all().then() I'm getting an error saying that I still have foreign key constraints preventing me to delete the user (But if i try to delete it manually after that it works fine, because I really moved all the dependencies to the default user).
async delete (req, res){
try {
const {id} = req.body;
// Here I call all the recipes that this user owns
const recipes = await Recipe.find({where: {chef_id: id}});
// Create the Promises for every UPDATE request in the Database
const changeChefPromises = recipes.map( recipe => {
Recipe.update(recipe.id,{chef_id : 1})
});
/* HERE is the problem, I'm trying to update every single recipe
before I delete the User, but my program is trying to delete
the User before finishing the Update process for all dependencies */
await Promise.all(changeChefPromises).then(() => {
Chef.delete(id);
});
return res.redirect(`/admin/chefs`);
} catch (err) {
console.error(err);
}
}
const changeChefPromises = recipes.map( recipe => {
Recipe.update(recipe.id,{chef_id : 1})
});
This is not creating an array of promises which is what you're expecting. You either need to remove the body of the arrow function or explicitly return the promise inside the body.
E.g.
const changeChefPromises = recipes.map(recipe => Recipe.update(recipe.id,{chef_id : 1}));
or
const changeChefPromises = recipes.map( recipe => {
return Recipe.update(recipe.id,{chef_id : 1})
});
Also, it's a bit weird to mix async/await and .then() so it might also be a good idea to change:
await Promise.all(changeChefPromises).then(() => {
Chef.delete(id);
});
to
await Promise.all(changeChefPromises);
await Chef.delete(id);

Resources