asynchronous issues adding to a map in mongoose - javascript

I am trying to add my googleID (object) to a map in another mongoose schema. I stringify my user id object, and during my test code I get thrown this error:
(node:293528) UnhandledPromiseRejectionWarning: ValidationError: collection validation failed: likes.5dbf6205bdcd5e5f78536447: Cast to ObjectId failed for value "110840542851551561690" at path "likes.$*"
at new ValidationError (C:\files\node_modules\mongoose\lib\error\validation.js:30:11)
at model.Document.invalidate (C:\files\node_modules\mongoose\lib\document.js:2333:32)
at Map.set (C:\files\node_modules\mongoose\lib\types\map.js:71:26)
at C:\files\app.js:123:30
at C:\files\node_modules\mongoose\lib\model.js:4589:16
at C:\files\node_modules\mongoose\lib\query.js:4323:12
at process.nextTick (C:\files\node_modules\mongoose\lib\query.js:2805:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
(node:293528) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:293528) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
My Post request from client.js:
const likesForm = document.getElementById("test");
likesForm.addEventListener("submit", function (evt) {
evt.preventDefault();
console.log(event.target.id)
const [likeString, itemId] = evt.target.id.split("-");
vote(
likeString === "like"
? "1"
: "-1",
itemId
);
}, false);
async function vote (voteInc, itemId) {
const route = `/like/${itemId}`;
const response = await fetch(route, {
method: 'POST',
body: JSON.stringify({"like": voteInc}),
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
console.log(result);
}
Server-side post:
app.post("/like/:id",(req,res)=>{
const id = req.params.id;
const like = req.body.like;
console.log(like);
console.log(id);
if(req.isAuthenticated()){
linkscr.user.findById(req.user.id, (e,foundUser)=>{
if(e){
console.log(e);
}else{
//if like = 1
if(like==1){
linkscr.collection.findById(id, function(error,result){
if(error){
console.log(error);
}else{
result.likes.set(foundUser._id.toString(),foundUser.googleId);
result.save();
}
})
console.log("not voted yet. Add 1 and added to like");
}else{
linkscr.collection.findById(id, function(error,result){
if(error){
console.log(error);
}else{
result.dislikes.set(foundUser._id.toString(),foundUser.googleId);
result.save();
}
})
console.log("not voted yet. Add 1 and added to dislike");
};
};
});
}else{
res.redirect("/");
}
});
My likes/dislikes schema:
likes : {
type: Map,
of: {
type: mongoose.Schema.Types.ObjectId,
ref: "User" }
},
dislikes : {
type: Map,
of: {
type: mongoose.Schema.Types.ObjectId,
ref: "User" }
},
How the Schema is initially saved:
likes : {[User._id]:User},
dislikes : {},
Thanks for any and all help. You're my only hope Stack!
I first thought this was a mongoose issue with how i was treat my objects but it looks like this is more to do with the async stuff. Admittedly that's my biggest weakness in programming at the time writing this.
EDIT: From what I'm looking at from documentation mongoose can't set objects only strings. Mongo can not mongoose.
Edit 2: My bad I put in the wrong Id in the wrong place. foundUser._id.toString() ,foundUser

In order to create maps in mongoose, the key **Must be in the form of a string. Additionally Your schema (which wasn't initially shared) wanted the object_id not the google passportID. For the sake of helping out future generations I will provide an edit in the original post. Simplifying with foundUser will work in this case.
result.likes.set(foundUser._id.toString(), foundUser);

Related

field in mongoose model is required : true but it is being created in postman

i am trying to give only name in the body and want error in the postman ...but for the status response in postman is 201 created but it is throwing error in console as
UnhandledPromiseRejectionWarning: ValidationError: User validation failed: password: Path password is required., email: Path email is required.
at model.Document.invalidate (C:\projects\MERN\backend\node_modules\mongoose\lib\document.js:2564:32)
at C:\projects\MERN\backend\node_modules\mongoose\lib\document.js:2386:17
at C:\projects\MERN\backend\node_modules\mongoose\lib\schematype.js:1181:9
at processTicksAndRejections (internal/process/task_queues.js:79:11)
(node:6524) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:6524) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
why there is no error in postman???????????
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
name:{
type : String,
required : true
},
email:{
type : String,
required : true,
unique:true,
},
password:{
type : String,
required : true,
minlength: 7
},
date:{
type :Date,
default: Date.now
}
})
const User = mongoose.model('User',userSchema)
module.exports = User
router.post("/", async (req, res) => {
try {
const user = await new User(req.body);
user.save();
res.status(201).send({user});
} catch (e) {
res.status(500).send(e);
}
});
consider that your node application throws some error right and crashes as you describe well above. Because your node app is interfacing with the internet you need to devise a way to interpret the error from you app into to an error that is known by the internet also, that way postman will be able to tell that an error has occured...So how do we achieve this, the answer is error handling...
We will use your User model as you have described, and consider the code below it...
router.post("/", async (req, res) => {
try {
const user = await new User(req.body);
// One important thing to note is that the return of this function call below is
// a Promise object which means that it executes asynchrounously and from the error
// log you have above, it is the reason your app is crashing...
user.save();
res.status(201).send({user});
} catch (e) {
res.status(500).send(e);
}
});
So then lets fix it...
router.post("/", async (req, res) => {
const user = await new User(req.body);
return user.save()
// the then call simply accepts a callback that is executed after the async is complete
.then((result) => res.status(201).send({user}))
// this catch will be called in case the call encounters an error during execution
.catch((error) => res.status(500).send(error));
});
Note now we handle the error in the catch by responding to the HTTP request as you have with a 500 code...and also sending the error along with the response

Trouble to destroy a model with Sequelize

What is happening?
I am trying to destroy one model via params. But when I try to destroy, it appears this error at the console.
(node:13350) UnhandledPromiseRejectionWarning: TypeError: results.map is not a function
at Query.handleSelectQuery (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/sequelize/lib/dialects/abstract/query.js:261:24)
at Query.formatResults (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/sequelize/lib/dialects/mysql/query.js:118:19)
at /home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/sequelize/lib/dialects/mysql/query.js:71:29
at tryCatcher (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/promise.js:547:31)
at Promise._settlePromise (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/promise.js:604:18)
at Promise._settlePromise0 (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/promise.js:649:10)
at Promise._settlePromises (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/promise.js:729:18)
at _drainQueueStep (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/async.js:93:12)
at _drainQueue (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/async.js:86:9)
at Async._drainQueues (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/async.js:102:5)
at Immediate.Async.drainQueues [as _onImmediate] (/home/vagnerwentz/Documents/freelance/autoparanaiba-api/node_modules/bluebird/js/release/async.js:15:14)
at processImmediate (internal/timers.js:439:21)
at process.topLevelDomainCallback (domain.js:130:23)
(node:13350) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:13350) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
The route that call the function
router.delete('/agricultural/announce/:id', passport.authenticate(), (req, res) => {
AnnouncementAgricultural.destroy(req, res);
})
The function
exports.destroy = async (req, res) => {
if (!await authorize(req, res, true)) {
return res.status(400).json({ success: false, errors: "unauthorized" })
}
await sequelize.query('SET FOREIGN_KEY_CHECKS=0;', { type: sequelize.QueryTypes.SELECT });
await Annoucement.destroy({
where: { id: req.params.id }
}
).then((result) => {
console.log(result);
res.status(200).json({ success: true })
}).catch((err) => {
console.log(err)
res.status(400).json({ success: false, errors: err.errors })
});
}
The QueryType you send tells Sequelize how to format the results. If you are performing SET and send QueryType.SELECT you will get an error because it tries to use .map() on an object:
const results = await sequelize.query("SET NAMES utf8mb4;", {
type: sequelize.QueryTypes.SELECT }
);
// -> TypeError: results.map is not a function
Sadly, in many places the docs confuse Raw Query (sending SQL in plain text) and using QueryTypes.RAW (which only should be used to format the results of queries that are not SELECT, UPDATE, etc.). Thus, one could assume that if you are making a Raw Query you should use the same QueryType to make the query "raw". At the very least, we should be able to assume it only affects how data is returned. The Sequelize documentation:
If you are running a type of query where you don't need the metadata,
for example a SELECT query, you can pass in a query type to make
sequelize format the results
Confusingly, if you are using a SELECT then none of these examples cause issues:
sequelize.query("SELECT * FROM table");
sequelize.query("SELECT * FROM table", { type: sequelize.QueryTypes.SELECT });
sequelize.query("SELECT * FROM table", { type: sequelize.QueryTypes.UPDATE });
sequelize.query("SELECT * FROM table", { type: sequelize.QueryTypes.RAW });
But if you use RAW on an UPDATE Sequelize tries to map an object 🙄
sequelize.query("UPDATE table SET createdAt = NOW();", {
type: sequelize.QueryTypes.RAW }
);
There was an uncaught error TypeError: results.map is not a function
at Query.handleSelectQuery ([...]/node_modules/sequelize/lib/dialects/abstract/query.js:261:24)
at Query.formatResults ([...]/node_modules/sequelize/lib/dialects/mysql/query.js:123:19)
at Query.run ([...]/node_modules/sequelize/lib/dialects/mysql/query.js:76:17)
at processTicksAndRejections (node:internal/process/task_queues:94:5)
at async [...]/node_modules/sequelize/lib/sequelize.js:619:16
So, because you are using SET you could, as #Anatoly says, change from QueryTypes.SELECT to QueryTypes.RAW to avoid the error. But if you don't need the results then don't pass a QueryType at all.
await sequelize.query('SET FOREIGN_KEY_CHECKS=0;');
// -> keep on keepin' on

How do you solve the "Cannot set headers after they are sent to the client" error in node js?

I've rewritten the following function about 6 different times and am still getting a "Cannot set headers after they are sent to the client" error. I have found several posts on the topic of promises but still cant figure it out:
Error: Can't set headers after they are sent to the client
Cannot set headers after they are sent to the client
Error: Setting header after it is sent - Help me understand why?
The following function is for a forum and is triggered when a comment is submitted. It check to see that the forum post exists, than if a parent comment exists (in the case it is a subcomment). I am using firestore.
index.js
const functions = require('firebase-functions');
const app = require('express')();
const {postOneForumComment,
} = require('./handlers/forumPosts');
app.post('/forumPost/:forumPostId/:parentId/comment', FBAuth, postOneForumComment);
exports.api = functions.https.onRequest(app);
forumPosts.js
// submit a new comment
exports.postOneForumComment = (req, res) => {
if (req.body.body.trim() === '')
return res.status(400).json({ comment: 'Must not be empty' });
const newComment = {
body: req.body.body,
forumPostId: req.params.forumPostId,
parentId: req.params.parentId
};
db.doc(`/forumPosts/${req.params.forumPostId}`) //check to see if the post exists
.get()
.then((doc) => {
if (!doc.exists) {
return res.status(404).json({ error: 'Post not found' });
}
else if (req.params.forumPostId !== req.params.parentId) { //check to see if the comment is a subcomment
return db.doc(`/forumComments/${req.params.parentId}`) //check to see if the parent comment exists
.get();
}
return "TopLevelComment";
})
.then((data) => {
if (data === 'TopLevelComment' || data.exists) {
return db.collection('forumComments').add(newComment); //post the comment to the database
}
return res.status(500).json({ error: 'Comment not found' });
})
.then(() => {
res.json(newComment);
})
.catch((err) => {
console.log(err.message);
res.status(500).json({ error: 'somethign went wrong' });
});
};
ERROR:
(node:29820) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting
a promise which was not handled with .catch(). (rejection id: 1)
(node:29820) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with
a non-zero exit code.
There are two ways of using promises. Either you use the then/catch callbacks or you can use async/await to allow you to write them synchronously.
then/catch method
// Some code before promise
somePromise.then(() => {
// Some code after promise action is successful
}).catch(err => {
// Some code if promise action failed
})
// Some code after promise definition you think should run after the above code
// THIS IS WHAT IS HAPPENING WITH YOUR CODE
async/await method
// Some code before promise
await somePromise;
// Some code after promise action is successful
The latter approach was introduces to avoid the callback hell problem and it seems that's where your error is arising from.
When using callback callbacks you must make sure that nothing is defined after the promise definition else it will run before the promise resolves (Which is counter-intuitive since placing code B after code B should make A run before B)
Your error is because your callbacks are probably running AFTER the response has been sent and express does not allow you to send multiple responses for a request.
You should make sure that where ever res.send or res.json is being called exist within the callback.
This article should help you understand promises much better...
Hope this helps...
For anyone who stumbles upon this here is a working solution using Promise.all to make sure all promises are fulfilled before moving on. It is not the prettiest function and I plan on going back and turning it into an async/await ordeal per #kwame and #Ajay's recommendation... but for now it works.
// post a comment
// TODO: turn into async await function
exports.postOneForumComment = (req, res) => {
if (req.body.body.trim() === '') return res.status(400).json({ comment: 'Must not be empty' });
const newComment = {
body: req.body.body,
createdAt: new Date().toISOString(),
forumPostId: req.params.forumPostId,
parentId: req.params.parentId,
username: req.user.username,
userImage: req.user.imageUrl,
likeCount: 0
};
const parentPost =
db.doc(`/forumPosts/${req.params.forumPostId}`).get()
.then((doc) => {
if (!doc.exists) {
res.status(404).json({ error: 'Post not found' });
return false;
}
return true;
})
.catch((err) => {res.status(500).json({ error: 'something went wrong while checking the post' });});
const parentComment =
req.params.forumPostId === req.params.parentId ? true :
db.doc(`/forumComments/${req.params.parentId}`).get()
.then((doc) => {
if (!doc.exists) {
res.status(404).json({ error: 'Comment not found' });
return false;
}
if (doc.forumPostId !== req.params.forumPostId) {
res.status(404).json({ error: 'Comment is not affiliated with this post' });
return false;
}
return true;
})
.catch((err) => {res.status(500).json({ error: 'something went wrong while checking the comment' });});
Promise.all([parentPost, parentComment])
.then((values) => {
if (values[0] && values[1]) {
return db.collection('forumComments')
.add(newComment)
.then(() => {
res.json(newComment);
});
}
return console.log("there was an error");
})
.catch((err) => {
res.status(500).json({ error: 'somethign went wrong with the submission' });
});
};

Hapi.js UnhandledPromiseRejectionWarning: Error: reply interface called twice?

when I running my project, I get the error:
(node:5795) UnhandledPromiseRejectionWarning: Error: reply interface called twice
at Object.exports.assert (/Users/labikemmy/Downloads/React-Native-FriendChat/api/node_modules/hoek/lib/index.js:736:11)
at Function.internals.response (/Users/labikemmy/Downloads/React-Native-FriendChat/api/node_modules/hapi/lib/reply.js:164:10)
at bound (domain.js:301:14)
at Function.runBound (domain.js:314:12)
at reply (/Users/labikemmy/Downloads/React-Native-FriendChat/api/node_modules/hapi/lib/reply.js:72:22)
at bound (domain.js:301:14)
at runBound (domain.js:314:12)
at result.then (/Users/labikemmy/Downloads/React-Native-FriendChat/api/node_modules/hapi/lib/handler.js:105:36)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
(node:5795) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:5795) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
null
I don't know it is bug or my code error? I'm screen hapi.js issues, and someone said the error is bug, another said 'reply() times is limit in same request'? if it is limited, how to change the code at below?
```
export default async function (request, reply) {
if (request.auth.credentials.email !== request.payload.email) {
await User.findOne({ email: request.auth.credentials.email }).then(
(user) => {
if (user) {
User.findOne({ email: request.payload.email }).then(
(friend) => {
if (friend) {
const stringId = `${friend._id}`;
const friendExists = user.friends.filter(f => `${f}` === stringId).length > 0;
if (!friendExists) {
user.friends.push(friend);
user.save();
reply({ friend: { fullName: friend.fullName, _id: friend._id } });
} else {
reply(Boom.conflict('You have added already this friend'));
}
} else {
reply(Boom.notFound(`Friend ${request.payload.email} doesn't exist`));
}
},
);
} else {
reply(Boom.notFound('Cannot find user'));
}
},
);
} else {
reply(Boom.conflict('Cannot add yourself as a friend'));
}
}
Hapi#16.4.1
Do you have any other plugins or lifecycle hooks like onPreHandler or something? Maybe there is some point your code that throws this error because you (or your code somehow) are calling reply interface before your actual response.
Also, I refactored your code. You are already utilizing JavaScript async interface, so you don't need to put "then" calls to your promises.
Try this and watch that what will come out:
export default async function (request, reply) {
if (request.auth.credentials.email === request.payload.email) {
return reply(Boom.conflict('Cannot add yourself as a friend'))
}
// I belive this is mongoose model
const user = await User.findOne({email: request.auth.credentials.email}).exec();
if (!user) {
return reply(Boom.notFound('Cannot find user'));
}
const friend = await User.findOne({email: request.payload.email}).exec();
if (!friend) {
return reply(Boom.notFound(`Friend ${request.payload.email} doesn't exist`));
}
const stringId = `${friend._id}`;
const friendExists = user.friends.filter(f => `${f}` === stringId).length > 0;
if (!friendExists) {
// hmmm shouldn't it be friend._id? user.friends.push(friend._id.toString());
user.friends.push(friend);
// better use this statement
// ref: http://mongoosejs.com/docs/api.html#document_Document-markModified
user.markModified('friends');
await user.save();
return reply({friend: {fullName: friend.fullName, _id: friend._id}});
} else {
return reply(Boom.conflict('You have added already this friend'));
}
}

Express route hanging async await

I am converting my application to use async/await instead of callbacks for query request made on the backend. It's been going good so far, but I am on a bit of a snag. My page is hanging on getting a get route and not rendering the ejs page. The console from the server also displays,
(node:7036)
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ReferenceError: qryFindLocalID is not defined
(node:7036) .
[DEP0018] DeprecationWarning: Unhandled promise rejections are depreca
ted. In the future, promise rejections that are not handled will terminate the Nod
e.js process with a non-zero exit code.
Any help would be appreciated.
The code so far is,
router.get("/secure", async (req, res) => {
let user = req.session.passport.user;
try {
if (user.chkUserStatus) {
if (user.lWaterLabID == 0 || user.lWaterlabID == -9999) {
// in both cases sLabID is set to 0
// assign the correct value for sLabName
user.sLabName = user.lWaterLabID == 0 ? "Site Admin" : "Uber Admin";
} else {
let labName = await request.query(
"Some Query"
);
user.sLabName = labName[0].sLabName;
}
} else {
// Houston we've got a problem ...
// user.chkUserStatus is not truthy
req.flash("loginMessage", "User Status is invalid.");
res.redirect("/");
}
const qryFindLocalID = await request.query(
`Some Query`
);
if (user.lWaterLabID == 0) {
const listRecentReports = await request.query(Some Query);
} else {
const listRecentReports = await request.query(Some Query);
}
} catch (err) {
// ... error checks
}
res.render("secure/index", {
qryFindLocalID: qryFindLocalID,
user: user,
listRecentReports: listRecentReports
});
});
The error message talks about an unhandled promise, but that's just wrapping the actual error, which is: ReferenceError: qryFindLocalID is not defined.
So where are you using qryFindLocalID? Ah, right at the bottom in the res.render call.
res.render("secure/index", {
qryFindLocalID: qryFindLocalID,
user: user,
listRecentReports: listRecentReports
});
Now why is qryFindLocalID undefined here? You defined it above in the try-catch block. But there's your problem -- You used const, so qryFindLocalID only exists in the try-catch. Outside of that, it doesn't exist.
You can fix that by using var in the try-catch (var is scoped to the function), or define qryFindLocalID using let ABOVE the try-catch.

Categories