Mongoose doesn't execute save and doesn't display error - node.js

Hi i have the following code:
export function createProduct(req, res) {
console.log("Execution")
const product = new Product({ ...req.body })
product.save(function (err, product) {
if (err) {
console.log("error")
const errorResponse = {}
for (let key in err.errors) {
//ValidationError handler
if (err.errors[key].properties) {
errorResponse[key] = err.errors[key].properties.message
}
//CastError handler
else {
errorResponse[key] = err.errors[key].toString().split(":")[1]
}
}
return res.status(400).send({ ...errorResponse })
}
console.log("created")
return res.send({ product })
})
}
There is no error on express side, console.log("Execution") is working and display this message correctly. I tested this by using Postman, when i send some data, response never come and on Postman there is error: "Error: socket hang up".
I've made console.log for req.body, and this is my output:
{
name: 'Apple Iphone 11 Pro 64GB Space Gray',
category: 'smartphone',
price: 4699,
inMagazine: { blocked: 0, inStock: 40 },
shortDescription: 'Odkryj wszystkie zalety iPhone 11 Pro 512 GB Silver. Smartfona, który zawstydza podkręconą wydajnością. Posiada bowiem najszybszy w historii procesor A13 Bionic oraz baterię, która pozwala na wiele. Weź iPhone 11 Pro do ręki i rób zdjęcia, których nie powstydziłby się nawet profesjonalista. Teraz masz do tego odpowiednie narzędzie – nowy iPhone 11 Pro posiada potrójny aparat główny, działający w oparciu o uczenie maszynowe. Efekty swojej fotograficznej przygody wraz z najmniejszymi detalami możesz ocenić z kolei na olśniewającym ekranie Super Retina XDR.',
images: [ { order: 1, src: '' } ]
}
Right after that, I found that I would check why it hangs, I had no error in the nodejs console. So I added two console.log to the code:
console.log("error")
console.log("created")
But both doesn't execute. So i made some code refactor and this works the same like above:
export async function createProduct(req, res) {
try {
const product = await Product.create({ ...req.body })
console.log("created")
return res.send({ product })
} catch (err) {
console.log("error")
const errorResponse = {}
for (let key in err.errors) {
//ValidationError handler
if (err.errors[key].properties) {
errorResponse[key] = err.errors[key].properties.message
}
//CastError handler
else {
errorResponse[key] = err.errors[key].toString().split(":")[1]
}
}
return res.status(400).send({ ...errorResponse })
}
}
I don't know what the cause of this problem could be.
This is link to whole project: https://github.com/codemasternode/DietShopping

Assuming you know that if you are saving the products like this all the keywords in the req.body should be the same as in the Product schema.
This should work:
exports.createProduct = async (req, res) => {
try{
const product = new Product(req.body).save();
return res.json(product);
}catch(err){
const errorResponse = {}
for (let key in err.errors) {
if (err.errors[key].properties) {
errorResponse[key] = err.errors[key].properties.message
}
else {
errorResponse[key] = err.errors[key].toString().split(":")[1]
}
}
return res.status(400).send({ ...errorResponse })
}
}

Try getting rid of the return(s) in front of the res. function calls
Like this:
export async function createProduct(req, res) {
try {
const product = await Product.create({ ...req.body })
console.log("created")
res.send({ product })
} catch (err) {
console.log("error")
const errorResponse = {}
for (let key in err.errors) {
//ValidationError handler
if (err.errors[key].properties) {
errorResponse[key] = err.errors[key].properties.message
}
//CastError handler
else {
errorResponse[key] = err.errors[key].toString().split(":")[1]
}
}
res.status(400).send({ ...errorResponse })
}

I've had an pre save in "products" model that doesn't let me go through. I was copying and pasting from other model and forget to remove unecesarry code.

I suggest you get rid of that callbacks and use clean async-await. By using this codes will be shorter too. And I think this will work. Try this.
export async function createProduct(req, res) {
try{
console.log("Execution")
const product = new Product({...req.body})
let result = await product.save()
console.log("created",result)
return res.send({ product })
}catch(err){
const errorResponse = {}
for (let key in err.errors) {
if (err.errors[key].properties) {
errorResponse[key] = err.errors[key].properties.message
}
else {
errorResponse[key] = err.errors[key].toString().split(":")[1]
}
}
return res.status(400).send({ ...errorResponse })
}
}

Related

display questions based on user type MERN

I have two collections from one questions for admin comes and from other questions for user comes.
I am unable to show the questions in React using redux store.
store/action
import { QUESTIONS } from '../../constants/actionTypes';
import * as api from '../../services/api';
import * as paths from '../../constants/apiPaths';
const TOTAL_QUESTIONS = 60;
export const fetcQuestions = (router) => async (dispatch) => {
try {
const user = await api.get(paths.FETCH_USER);
const userType = user.type;
console.log(userType + "userType");
if((userType === "Student") || (userType === "STUDENT"))
{
const {questions, assessment, options} = await api.get(paths.FETCH_QUESTIONS);
console.log(api.get(paths.FETCH_QUESTIONS) + "Question path");
dispatch({ type: QUESTIONS, questions, assessment, options });
if(assessment.responded === TOTAL_QUESTIONS) {
router.push('/advice');
}
}
else
if((userType === "Admin") || (userType === "ADMIN"))
{
console.log(userType + "type of user");
const {questions, assessment, options} = await api.get(paths.FETCH_QUESTIONS);
console.log(api.get(paths.FETCH_QUESTIONS) + "Question path");
dispatch({ type: QUESTIONS, questions, assessment, options });
if(assessment.responded === TOTAL_QUESTIONS) {
console.log("thank you");
}
}
} catch (error) {
console.log(error);
}
};
export const postAssessment = (data, router) => async (dispatch) => {
try {
const {questions, assessment, options} = await api.post(paths.POST_ASSESSMENT, data);
console.log(paths.POST_ASSESSMENT + "assessment");
dispatch({ type: QUESTIONS, questions, assessment, options });
if(assessment.responded === TOTAL_QUESTIONS) {
console.log("Thank you");
}
} catch (error) {
console.log(error);
}
};
please help me looking out whats wrong I am doing here. Thank You.
Reducer:
import * as actionType from '../../constants/actionTypes';
const assessmentReducer = (state = { questions: null, assessment: null, options: null }, action) => {
switch (action.type) {
case actionType.QUESTIONS:
return { ...state, questions: action?.questions, assessment: action?.assessment, options: action?.options, loading: false, errors: null };
default:
return state;
}
};
export default assessmentReducer;
NodeJS controller:
const TOTAL_QUESTIONS = 120;
export const fetchQuestions = async (req, res) => {
try {
const user = await db.findOne('USER', { _id: req.userId});
console.log(user + "user data");
let answerdQuestions = [];
let nextQuestions;
let assessment;
if (user.assessment) {
assessment = await db.findOne('ASSESSMENT', { user: req.userId });
answerdQuestions = assessment.responses.map(response => response.number)
}
nextQuestions = getNextQuestions(answerdQuestions);
if ((user.type === "STUDENT") || (user.type === "student")) {
console.log(user.type + "type");
const questions = await db.find('QUESTIONG', { number: { $in: nextQuestions } });
console.log(questions.number + "quesstudent");
res.status(200).json({ questions, assessment: { id: assessment?._id, responded: answerdQuestions.length }, options: options });
return answerdQuestions;
}
else {
console.log(user.type + "typegh");
const questions = await db.find('QUESTION', {number: { $in: nextQuestions }});
console.log(questions.question + "quesdata");
res.status(200).json({ questions, assessment: { id: assessment?._id, responded: answerdQuestions.length }, options: options });
return answerdQuestions;
}
} catch (err) {
console.log(err)
res.status(500).json({ message: "Something went wrong" });
}
};
export const postResponses = async (req, res) => {
try {
let responses = req.body.responses;
let assessmentId = req.body.id;
responses = responses.map(response => {
return {
score: Number(response.value),
number: response.number,
category: response.category,
question: response._id
}
});
const user = await db.findOne('USER', {_id: req.userId});
console.log( user.type + "typeofuser");
let assessment = await db.findOne('ASSESSMENT', { _id: assessmentId });
if (assessment?.responses.length === TOTAL_QUESTIONS) {
res.status(200).json("completed");
}
if (!assessment) {
//
let response = {
user: req.userId,
responses: responses,
responded: responses.length
}
assessment = await db.create('ASSESSMENT', response);
await db.findOneAndUpdate('USER', { _id: req.userId }, { assessment: assessment._id });
} else {
assessment = await db.findOneAndUpdate('ASSESSMENT', { _id: assessment._id }, { $push: { responses: { $each: responses } } });
}
let answerdQuestions = assessment.responses.map(response => response.number);
const nextQuestions = getNextQuestions(answerdQuestions);
if (answerdQuestions.length === TOTAL_QUESTIONS) {
console.log("You win");
]
}
if((user.type === "STUDENT") || (user.type==="student") ){
console.log("type" + user.type);
const questions = await db.find('QUESTIONG', { number: { $in: nextQuestions } });
res.status(200).json({ questions, assessment: { id: assessment?._id, responded: answerdQuestions.length }, options: options });
} else
{
console.log(user.type + "typeg");
const questions = await db.find('QUESTION', { number: { $in: nextQuestions } });
res.status(200).json({ questions, assessment: { id: assessment?._id, responded: answerdQuestions.length }, options: options });
}
}catch (err) {
console.log(err)
res.status(500).json({ message: "Something went wrong" });
}
};
//export default fetchQuestions;
I have added reducer and NodeJS controller also, Based on user type the questions are to be shown. Please help. If type is student then the questions are not getting displayed else condition the questions are getting displayed
What I can see is, you have mentioned wrong database when you are trying to fetch data for usertype student. Here is your code:
if (user.type === 'STUDENT' || user.type === 'student') {
console.log(user.type + 'type');
const questions = await db.find('QUESTIONG', { number: { $in: nextQuestions } });
You need to correct the database name, and everything will work fine:
const questions = await db.find('QUESTION', { number: { $in: nextQuestions } });
Hope this works!!!

Fetching data from MongoDB and displaying it in React App

I am working on creating a web app of an interactive map of campus for the school I'm attending. The web-app will allow students to click on a specific building on campus, which will bring up a map of the classrooms in that building. From there, a user can then click on a classroom which will then display a list of all classes in that room for each day of the school week. I have created my own database of all the classes offered at my school with attributes such as name, section, building, roomnum, and coursenum. I have done enough of the backend where I can connect to my database using Insonmia/Postman where I am able to filter classes based on building, room number and course section (Accounting, Biology etc.) The problem that I am running into is actually displaying the data from my database on the frontend part of my application.
Here is the backend section of my application so far...
server.js
import express from "express"
import cors from "cors"
import classes from "./api/classes.route.js"
const app = express()
app.use(cors())
app.use(express.json())
app.use("/api/v1/classes", classes)
app.use("*", (req, res) => res.status(404).json({ error: "not found"}))
export default app
index.js
import app from "./server.js"
import mongodb from "mongodb"
import dotenv from "dotenv"
import ClassesDAO from "./dao/ClassesDAO.js"
dotenv.config()
const MongoClient = mongodb.MongoClient
const port = process.env.PORT || 8000
MongoClient.connect(
process.env.UJCLASSES_DB_URI,
{
maxPoolSize: 50,
wtimeoutMS: 2500,
useNewUrlParser: true }
)
.catch(err => {
console.error(err.stack)
process.exit(1)
})
.then(async client => {
await ClassesDAO.injectDB(client)
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
})
classesDAO.js
import mongodb from "mongodb"
const ObjectId = mongodb.ObjectID
let classes
export default class ClassesDAO {
static async injectDB(conn) {
if (classes) {
return
}
try {
classes = await conn.db(process.env.UJCLASSES_NS).collection("Classes")
} catch (e) {
console.error(
`Unable to establish a collection handle in ClassesDAO: ${e}`,
)
}
}
static async getClasses({
filters = null,
page = 0,
classesPerPage = 20,
} = {}) {
let query
if (filters) {
if ("name" in filters) {
query = { $text: { $search: filters["name"] } }
} else if ("section" in filters) {
query = { "section": { $eq: filters["section"] } }
} else if ("course" in filters) {
query = { "course": { $eq: filters["course"] } }
} else if ("room" in filters) {
query = {"room": { $eq: filters["room"] } }
}
}
let cursor
try {
cursor = await classes
.find(query)
} catch (e) {
console.error(`Unable to issue find command, ${e}`)
return { classesList: [], totalNumClasses: 0 }
}
const displayCursor = cursor.limit(classesPerPage).skip(classesPerPage * page)
try {
const classesList = await displayCursor.toArray()
const totalNumClasses = await classes.countDocuments(query)
return { classesList, totalNumClasses }
} catch (e) {
console.error(
`Unable to convert cursor to array or problem counting documents, ${e}`,
)
return { classesList: [], totalNumClasses: 0 }
}
}
static async getSections() {
let sections = []
try {
sections = await classes.distinct("section")
return sections
} catch (e) {
console.error(`Unable to get sections, ${e}`)
return sections
}
}
static async getBuildings() {
let buildings = []
try {
buildings = await classes.distinct("building")
return buildings
} catch (e) {
console.error('Unable to get buildings, ${e}')
return buildings
}
}
static async getRooms() {
let rooms = []
try {
rooms = await classes.distinct("room")
return rooms
} catch (e) {
console.error('Unable to get rooms, ${e}')
return rooms
}
}
}
classes.controller.js
import ClassesDAO from "../dao/ClassesDAO.js"
export default class ClassesController {
static async apiGetClasses(req, res, next) {
const classesPerPage = req.query.classesPerPage ? parseInt(req.query.classesPerPage, 10) : 20
const page = req.query.page ? parseInt(req.query.page, 10) : 0
let filters = {}
if (req.query.section) {
filters.section = req.query.section
} else if (req.query.course) {
filters.course = req.query.course
} else if (req.query.name) {
filters.name = req.query.name
} else if (req.query.building) {
filters.name = req.query.building
} else if (req.query.room) {
filters.room = req.query.room
}
const { classesList, totalNumClasses } = await ClassesDAO.getClasses({
filters,
page,
classesPerPage,
})
let response = {
classes: classesList,
page: page,
filters: filters,
entries_per_page: classesPerPage,
total_results: totalNumClasses,
}
res.json(response)
}
static async apiGetClassSections(req, res, next) {
try {
let section = await ClassesDAO.getSections()
res.json(section)
} catch (e) {
console.log(`api, ${e}`)
res.status(500).json({ error: e })
}
}
static async apiGetClassBuildings(req, res, next) {
try {
let building = await ClassesDAO.getBuildings()
res.json(building)
} catch (e) {
console.log('api, ${e}')
res.status(500).json({ error: e })
}
}
static async apiGetClassRooms(req, res, next) {
try{
let room = await ClassesDAO.getRooms()
res.json(room)
} catch (e) {
console.log('api, ${e}')
res.status(500).json({ error: e })
}
}
}
classes.route.js
import express from "express"
import ClassesCtrl from "./classes.controller.js"
const router = express.Router()
router.route("/").get(ClassesCtrl.apiGetClasses)
router.route("/sections").get(ClassesCtrl.apiGetClassSections)
router.route("/buildings").get(ClassesCtrl.apiGetClassBuildings)
router.route("/rooms").get(ClassesCtrl.apiGetClassRooms)
export default router
I understand that this is not a platform that spits out exact answers, however that is not what I'm looking for. I have been stuck on this problem for over a week and have noticed that my project seems to be different than others. For my project, all I need to do is fetch data from an already completed database and display it. I do not need update, delete or insert functionality.
If anyone could point me in the right direction, or link any docs that could help me out I would be very grateful.

How to save ternary operator as a value on JavaScript (Node.js)?

I try code to create method in Node.js (Express). I want to using ternary operator for return data and this following code is work perfectly.
const update = async (req, res) => {
try {
const { id } = req.params
const { name, price } = req.body
if (!(name && price)) {
!name ?
res.status(409).json({
status: res.statusCode,
message: "Name must not empty!"
}) :
res.status(409).json({
status: res.statusCode,
message: "Price must not empty!"
})
} else {
await Product.update({ name: name, price: price}, {
where: {
...
}
})
}
} catch (err) {
console.log(err)
}
}
But, can I assigned / save ternary operator as a variable for simple code?
I try to following code to save as a variable but still not working.
const x = !name ? "Name" : "Price"
Thank you.
So, after few minutes later and research from internet. I get solution from ThoughtCo.
const update = async (req, res) => {
try {
const { id } = req.params
const { name, price } = req.body
if (!(name && price)) {
const x = (!name) ? "Name" : "Price"
res.status(409).json({
status: res.statusCode,
message: `${x} must not empty!`
})
} else {
await Product.update({ name: name, price: price}, {
where: {
...
}
})
}
} catch (err) {
console.log(err)
}
}
First, I assign Name/Price to variable using ternary-operator and using template literals.
In my opinion, this following code is more simple than before.

Compare data after sequelize asynchronous query

I have this code :
VerificationKey.getCode = async (visitorData, visitorCode, res) => {
console.log("Verif model visitorCode" + visitorCode);
const data = visitorData;
const testCode = visitorCode;
const findVisitor = await VerificationKey.findOne({ where: { data } })
.catch((err) => {
console.log(err);
})
.then(() => {
if (testCode == findVisitor.key) {
res.status(200).json({ response: true });
}
});
};
What I need is to compare testCode and findVisitor.key values.
If they are equal, I want to return a boolean to the front end.
But I can't write it like this because it is not possible to access findVisitor.key before initialization.
I believe you have to change your code to use async/await syntax only - without then and catch:
VerificationKey.getCode = async (visitorData, visitorCode, res) => {
console.log("Verif model visitorCode" + visitorCode);
const data = visitorData;
const testCode = visitorCode;
try {
const findVisitor = await VerificationKey.findOne({ where: { data } });
if(!findVisitor) {
res.status(404).json({ response: false });
} else if(testCode == findVisitor.key) {
res.status(200).json({ response: true });
} else {
res.status(403).json({ response: false });
}
} catch(err) {
console.log(err);
}
};

Asynchronicity issue: why does the second part of my function run before a loop event finishes?

I have a route on an Express server that updates a User profile. The User profile is updated before I have finished to parse the data to update. How so?
I want to update two const: newProfilePicture & newOtherPictures. They are correctly updated, but after the user has been updated, so it's useless. How to fix this asynchronicity issue?
Here is the function:
router.post("/upload-images", upload.array("image"), async (req, res) => {
const { userId } = req.body;
try {
if (req.files) {
let newProfilePicture = null;
let newOtherPictures = [];
req.files.forEach(({ path, originalname }) => {
cloudinary.uploader.upload(
path,
{
resource_type: "image",
public_id: `myapp/users/${userId}/${originalname}`,
crop: "scale",
quality: "auto",
},
(err, res) => {
if (err) {
return fs.unlinkSync("./" + path);
}
fs.unlinkSync("./" + path);
if (originalname === "main") {
return (newProfilePicture = res.secure_url);
}
return newOtherPictures.push({
id: originalname,
url: res.secure_url,
});
}
);
});
// THIS PART IS COMPLETE BEFORE THE req.files.forEach IS DONE
const user = await User.findById(userId);
const { otherPictures, profilePicture } = updatePictures(
newProfilePicture,
newOtherPictures,
user
);
User.findByIdAndUpdate(
userId,
{ profilePicture, otherPictures },
{ new: true }
);
res.send("upload images success");
}
} catch (err) {
console.log("err", err);
return res.status(500).send("upload images failed");
}
});
It happens because cloudinary.uploader.upload() runs asynchronously. Since you mentioned it doesn't have promise interface, you can convert the callback to promise using NodeJS's util.promise function as it's error first callback.
const { promisify } = require("util");
const fs = require("fs");
const cloudinaryUpload = promisify(cloudinary.uploader.upload.bind(cloudinary.uploader))
router.post("/upload-images", upload.array("image"), async (req, res) => {
try {
if (!req.files) {
return res.send("no images in the request body");
}
let newProfilePicture = null;
let newOtherPictures = [];
for (const { path, originalName } of req.files) {
try {
const response = await cloudinaryUpload(path, {
resource_type: "image",
public_id: `myapp/users/${userId}/${originalName}`,
crop: "scale",
quality: "auto",
});
await fs.promises.unlink("./" + path);
if (originalname === "main") {
newProfilePicture = response.secure_url;
continue;
}
newOtherPictures.push({
id: originalName,
url: response.secure_url,
});
} catch (error) {
//do what you want if there is an error
//throw error if you want
await fs.promises.unlink("./" + path);
}
}
const user = await User.findById(userId);
const { otherPictures, profilePicture } = updatePictures(
newProfilePicture,
newOtherPictures,
user
);
//use User.updateOne() as you don't need the doc back
await User.findByIdAndUpdate(
userId,
{ profilePicture, otherPictures },
{ new: true }
);
return res.send("upload images success");
} catch (error) {
console.log("err", err);
return res.status(500).send("upload images failed");
}
});

Resources