mongoose findById return null - node.js

I'm doing a to do list application. I use the following function to move a task in the to-do list to the done list. However, the "columnId" method I use as the url parameter and the "findById" method that I use return null. What do you think the problem might be?
public async Put(columnId: string, todo: ITodo): Promise<ITodo | null> {
try {
const editingTodo: ITodo | null = await Todo.findById(todo._id);
if (editingTodo !== null) {
const oldColumn: IColumn | null = await Column.findById(editingTodo.Column._id).exec();
if (oldColumn !== null) {
const todoIndexByOldColumn = oldColumn.Todos.indexOf(editingTodo._id);
oldColumn.Todos.splice(todoIndexByOldColumn, 1);
const newColumn: IColumn | null = await Column.findById(columnId).populate("Todos").exec();
console.log(newColumn);
if (newColumn !== null) {
newColumn.Todos.push(todo);
newColumn.save();
oldColumn.save();
editingTodo.save();
}
}
}
return editingTodo;
} catch (error) {
throw error;
}
}

Here is crud operation for a todo list with mongoose.
// model todo
let todo = new schema({
description: { type: String },
heading: { type: String },
title: { type: String },
});
Below is the controller with logic for all operations.
// get All Todo List
let findAllTodoList = async (req, res, next ) => {
let foundAllTodo = await todo.findAll({});
res.send({ data: foundAllTodo });
};
// get Specific Todo
let findTodoById = async (req, res, next ) => {
let todo_id = req.params.todo_id;
let foundTodo = await todo.findById(todo_id);
res.send({ data: foundTodo });
};
// create Todo Element
let createTodo = async (req, res, next ) => {
let todo_obj = {
description: 'Here Add Element',
heading: 'Adding',
title: 'Add',
};
let foundTodo = await todo.create(todo_obj);
res.send({ data: 'success' });
};
// Destroy Todo Element
let DeleteTodoById = async (req, res, next ) => {
let todo_id = req.params.todo_id
let foundTodo = await todo.remove({ _id:todo_id });
res.send({ data: 'success' });
};
module.exports = {
findAllTodoList
findTodoById,
createTodo,
DeleteTodoById
};

Related

Path `comment` is required. MERN stack

I don't understand why I get this error. This is my controller:
export const createProductReview = async (req, res) => {
const { rating, comment } = req.body;
const product = await Product.findById(req.params.id);
if (product) {
const alreadyReviewed = product.reviews.find(
r => r.user.toString() === req.user.userId.toString()
);
if (alreadyReviewed) {
throw new NotFoundError('Product already reviewed');
}
const review = {
user: req.user.userId,
name: req.user.username,
rating: Number(rating),
comment,
};
product.reviews.push(review);
product.numOfReviews = product.reviews.length;
product.rating =
product.reviews.reduce((acc, item) => item.rating + acc, 0) /
product.reviews.length;
await product.save();
res.status(StatusCodes.OK).json({ message: 'Review added', review });
} else {
throw new NotFoundError('Product not found');
}
};
This is mine productPage where i dispatch addProductReview and passing product id from params and review object:
const [rating, setRating] = useState(0);
const [comment, setComment] = useState('');
const submitHandler = e => {
e.preventDefault();
dispatch(
addProductReview(id, {
rating,
comment,
})
);
};
And this is my productSlice:
export const addProductReview = createAsyncThunk(
'product/review',
async (id, { review }, thunkAPI) => {
try {
const { data } = await axios.post(
`/api/v1/products/${id}/reviews`,
review
);
return data;
} catch (error) {
const message = error.response.data.msg;
return thunkAPI.rejectWithValue(message);
}
}
);
I have no clue why i got error Path comment is required. i pass review object to route.
The issue is with the parameters used in your Thunk payloadCreator. From the documentation...
The payloadCreator function will be called with two arguments:
arg: a single value, containing the first parameter that was passed to the thunk action creator when it was dispatched. This is useful for passing in values like item IDs that may be needed as part of the request. If you need to pass in multiple values, pass them together in an object when you dispatch the thunk, like dispatch(fetchUsers({status: 'active', sortBy: 'name'})).
thunkAPI: an object containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options
Your payloadCreator has three arguments which is incorrect.
Try this instead
export const addProductReview = createAsyncThunk(
'product/review',
async ({ id, ...review }, thunkAPI) => {
try {
const { data } = await axios.post(
`/api/v1/products/${id}/reviews`,
review
);
return data;
} catch (error) {
const message = error.response.data.msg;
return thunkAPI.rejectWithValue(message);
}
}
);
and dispatch it like this
dispatch(addProductReview({ id, rating, comment }));

Nodejs exports returns undefined on mongoose Insertion

I have created nodejs application by organising as module structure , The problem I am facing is that a mongodb insertion return undefined value from one of my controller, The issue I found is that my async funtion doesn't wait to complete my mongodb operation But I could not find a solution for that, my route and controller code is given below
route.js
const {
createEvent, editEvent
} = require('./controller');
router.post("/event/create", validateEventManage, isRequestValidated, async(req, res) => {
let data = {};
data.body = req.body;
try{
let event = await createEvent(req.body);
console.log(event) // returned undefined
data.event = event;
res.status(200).json(data);
}catch(error){
console.log(error)
res.status(200).json({error:error});
}
});
controller.js
exports.createEvent = async(data) => {
// return "test" // This works correctly
const eventObj = {
name : data.name,
description : data.desc,
type : data.type,
startDate : new Date()
}
const event = await new Event(eventObj);
await event.save((error,event)=>{
if(error) {
return error;
}
if(event){
return event;
}
});
}
You should not await the new Event constructor.
Also, since you are using async - await you can
remove the callback from the save and try ... catch the error to handle it:
exports.createEvent = async (data) => {
// return "test" // This works correctly
const eventObj = {
name: data.name,
description: data.desc,
type: data.type,
startDate: new Date(),
};
try {
const event = new Event(eventObj);
await event.save();
return event;
} catch (error) {
return error;
}
};

Pass multiple queries in GET request to mongodb

// route.js
const express = require('express');
const router = express.Router();
const userHandler = require('../handler/user');
router.get('',userHandler.getUser);
module.exports = route
-
// Handler.js
const userController = require('../../core/controller/user');
// get user
getUser = async (req, res, next) => {
//console.log(req)
try {
let user = await userController.getUser(req.params.id, req.query.employeeStatus, req.query.department)
req.data = user
next()
}
catch (e) {
req.status = 400;
next(e)
}
}
module.exports = {getUser}
-
// controller.js
exports.getUser = async (userId,employeeStatus,department) => {
let userRecords = await userModel.getUser(userId,employeeStatus,department);
userRecords = userRecords.map(it => {
return {
id: it._id,
firstName: it.firstName,
lastName: it.lastName,
pic: it.pic,
gender: it.gender,
dob: it.dob,
maritalStatus: it.maritalStatus,
nationality: it.nationality,
streetAddress: it.streetAddress,
city: it.city,
state: it.state,
postalCode: it.postalCode,
country: it.country,
phone: it.phone,
email: it.email,
jobTitle: it.jobTitle,
department: it.department,
dateOfJoining: it.dateOfJoining,
employeeStatus: it.employeeStatus,
kra: it.kra,
assignedSupervisor: it.assignedSupervisor,
assignedSubordinate: it.assignedSubordinate,
workExperience: it.workExperience,
skills: it.skills,
password: it.password
}
})
return userRecords;
}
-
// query.js
exports.getUser = async(userId, employeeStatus, department) => {
var whereClause = '';
if (department) {
var whereClause ={ "department":department}
console.log(whereClause)
console.log( Object.keys(whereClause).length)
}
if(userId) return await model.find({"_id":userId}).exec();
if (employeeStatus){
console.log('debewbfewhfui')
if ( Object.keys(whereClause).length) {
console.log(whereClause)
whereClause += ','
console.log( whereClause.toSource())
console.log(whereClause.hasOwnProperty("employeeStatus"))
whereClause += {"employeeStatus":employeeStatus}
}
console.log(whereClause)
//whereClause = {"employeeStatus":employeeStatus}
console.log('e', Object.keys(whereClause).length)
// }])
// console.log(department)
// return await model.find({ $and: [ { $or: [{"employeeStatus": employeeStatus }] },{"department": department} ] }).exec();
// return await model.find({"employeeStatus":employeeStatus}).find({"department":department}).exec();
}
// if(department) {
// console.log('55')
// return await model.find({"department":department}).exec();
// };
// if (Object.keys(whereClause).length) {
// console.log(whereClause)
// whereClause = whereClause + ','
// }
var query = await model.find({$and : [whereClause] }).exec();
console.log('fssd',JSON.stringify(whereClause))
return query
}
I want if we pass any data department or employeestatus in query it will return data as required and if we dont pass any query then it will search all users. Can anyone help me please?
To conditionally compose the query filters depending on the parameters passed into the function, this should work:
query.js
exports.getUser = async (userId, employeeStatus, department) => {
let queryFilters = { userId, employeeStatus, department };
// The JSON stringify and parsing below would help remove undefined filter values
// i.e if userId is not provided, while employeeStatus and department are given
// the queryFilters object would only contain employeeStatus and department.
queryFilters = JSON.parse(JSON.stringify(queryFilters))
return model.find(queryFilters);
}

Code executes before i get the response from the database

I am new to Express and writing the code to get the list from my database. I'm trying to update the quantity of the items in my list. Now there can be multiple items and quantity for those items needs to be updated accordingly. The problem I am facing is when I try to get the list and update item accordingly, before my for loop executes to update the item it doesn't update the item's quantity in the database and saves the order. What am I doing wrong?
I have used async functions, promises and flags to update the items quantity in the database but none helps.
This is my code for to get and update the item's quantity
const Express = require("express");
const app = Express.Router();
const Menu = require("../../models/Menu");
const Order = require("../../models/order");
const User = require("../../models/user");
app.post(
"/create",
async function(req, res) {
var myorder = {};
var orderList = [];
var ordDetail = [];
var UpdateMenus = [];
orderList = JSON.parse(JSON.stringify(req.body["OD"]));
if(orderList.length>0){
const user = await User.findOne({ _id: req.user.id })
.then(user => {
if (!user) {
return res.status(400).json({ error: "User Not Found" });
}
})
.then(() => {
var order = Order({
user: req.user.id
});
myorder = order;
(async function loop() {
for (i = 0; i < orderList.length; i++) {
const ordt = new Object({
menu: orderList[i]["menuId"],
order: myorder.id,
prize: orderList[i]["prize"],
quantity: orderList[i]["quantity"]
});
await Menu.findOne({ _id: orderList[i]["menuId"] })
.exec()
.then(menu => {
if (menu) {
if (menu.quantity >= ordt.quantity) {
menu.quantity = menu.quantity - ordt.quantity;
const editmenu = menu;
(async function updateTheMenu() {
await Menu.findOneAndUpdate(
{ _id: menu.id },
{ $set: editmenu },
{
new: true,
useFindAndModify: false
}
).then(updateMenu => {
console.log(updateMenu);
ordDetail.push(ordt);
});
})();
} else {
return res.status(400).json({
error:
menu.MenuText +
"" +
ordt.quantity +
" Qunatity Is Not Available"
});
}
}
});
}
})();
}).then(()=>{
order
.save()
.then(order => {
if (!order) {
return res.json({ error: "Order is not saved" });
}
res.status(200).json(order);
})
.catch(error => {
return res
.status(400)
.json({ error: "Fields are Not Correct" });
});
});
}
}
);
There are few things wrong with your code:
If you use await then you don't need to use then. You can just assign to a variable. Example:
const menu = await Menu.findOne({ _id: orderList[i]["menuId"] })
You don't need to wrap your loop and every await call in async functions. They are already in an async function.
You can write your response handler like this:
app.post('/create', async function(req, res) {
var myorder = {};
var orderList = [];
var ordDetail = [];
var UpdateMenus = [];
orderList = JSON.parse(JSON.stringify(req.body['OD']));
if (orderList.length > 0) {
const user = await User.findOne({ _id: req.user.id });
if (!user) {
return res.status(400).json({ error: 'User Not Found' });
}
var order = Order({
user: req.user.id
});
myorder = order;
for (i = 0; i < orderList.length; i++) {
const ordt = new Object({
menu: orderList[i]['menuId'],
order: myorder.id,
prize: orderList[i]['prize'],
quantity: orderList[i]['quantity']
});
const menu = await Menu.findOne({ _id: orderList[i]['menuId'] });
if (menu) {
if (menu.quantity >= ordt.quantity) {
menu.quantity = menu.quantity - ordt.quantity;
const editmenu = menu;
const updateMenu = await Menu.findOneAndUpdate(
{ _id: menu.id },
{ $set: editmenu },
{
new: true,
useFindAndModify: false
}
);
console.log(updateMenu);
ordDetail.push(ordt);
} else {
return res
.status(400)
.json({
error:
menu.MenuText +
'' +
ordt.quantity +
' Qunatity Is Not Available'
});
}
}
}
try {
const savedOrder = await order.save();
if (!savedOrder) {
return res.json({ error: 'Order is not saved' });
}
res.status(200).json(savedOrder);
} catch (error) {
return res.status(400).json({ error: 'Fields are Not Correct' });
}
}
});

How to update document by ID without using model

I have Card model and I have an API where I'm looking for a document by ID.
app.post("/api/postcomment", async (req,res) => {
const data = req.body
const reqUrl = req.headers.referer
const re = new RegExp('([a-zA-Z0-9]*$)', 'i')
const fixedUrl = reqUrl.match(re)
try {
await Card.update({_id: fixedUrl}, {$push:{'comments': data}})
const card = await Card.findById(fixedUrl)
return res.json(card)
} catch (err) {
throw err
}
})
It works fine. But now I have few more models. All should work the same way to them. But how can I make this code reusable for every model?
Or maybe there is a way to pass a name of my model to API? and then use it like this:
app.post("/api/postcomment", async (req,res, modelName) => {
const data = req.body
const reqUrl = req.headers.referer
const re = new RegExp('([a-zA-Z0-9]*$)', 'i')
const fixedUrl = reqUrl.match(re)
try {
await modelName.update({_id: fixedUrl}, {$push:{'comments': data}})
const item = await modelName.findById(fixedUrl)
return res.json(item )
} catch (err) {
throw err
}
})
Solution1: You can create two helper functions and call the from the router. Both function accept the model object:
let updateDocument = (model, fixedUrl, data) => {
return model.update({ _id: fixedUrl }, { $push: { comments: data }})
}
let getDocument = (model, fixedUrl) => {
return model.findById(fixedUrl)
}
app.post("/api/postcomment", async (req, res, modelName) => {
const data = req.body
const reqUrl = req.headers.referer
const re = new RegExp('([a-zA-Z0-9]*$)', 'i')
const fixedUrl = reqUrl.match(re)
try {
await updateDocument(Card, fixedUrl, data)
const item = await getDocument(Card, fixedUrl)
return res.json(item )
} catch (err) {
throw err
}
})
Solution2: The much better solution is to create a base class (service), with the common functionality. And extend it for each model:
class BaseService {
constructor(model) {
this.model = model;
}
getDocument(data) {
return this.model.findOne(...);
}
updateDocument(data) {
return this.model.update(...);
}
}
class CardService extends BaseService {
constuctor() {
super(Card);
}
}

Resources