Mongoose findOne() method and array map() method - javascript

Today I'm a little bit confused... I'm trying to get a list of posts of logged user by _id numbers included in user mongo document. When I call promise inside map method - I'm still receiving Array of Promise{}. I'm looking for info about this behavior but I don't understand why it behaves like this. It seems coded like it should be (in promise with async and await). getListOfThoughts method is created for posts download.
Thank you in advance
export const addThought = async (req: Request, res: Response) => {
const { email, thoughtContent } = req.body;
const getListOfThoughts = async (user: IUser) => {
const result = user.posts.map(async (el) => {
return await ThoughtModel.findOne({ _id: el });
});
return result;
};
if (email) {
try {
const user = await UserModel.findOne({ eMail: email });
const newThought = await ThoughtModel.create({
textContent: thoughtContent,
author: {
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
userPic: user.pic,
},
});
await UserModel.updateOne(
{ eMail: email },
{ $push: { posts: newThought._id } }
);
const updatedUser = await UserModel.findOne({ eMail: email });
const newListOfThoughts = await getListOfThoughts(updatedUser);
console.log("new List", newListOfThoughts);
res.status(201).send({
message: "Thought added",
userData: newListOfThoughts,
});
} catch (error) {
console.log(error);
res.json(error.message);
}
}
};
mongoose javascript mongodb async promise
I tried:
export const addThought = async (req: Request, res: Response) => {
const { email, thoughtContent } = req.body;
const getListOfThoughts = async (user: IUser) => {
const result = user.posts.map(async (el) => {
const post = await ThoughtModel.findOne({ _id: el });
return post;
});
return result;
};
if (email) {
try {
const user = await UserModel.findOne({ eMail: email });
const newThought = await ThoughtModel.create({
textContent: thoughtContent,
author: {
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
userPic: user.pic,
},
});
await UserModel.updateOne(
{ eMail: email },
{ $push: { posts: newThought._id } }
);
const updatedUser = await UserModel.findOne({ eMail: email });
const newListOfThoughts = await getListOfThoughts(updatedUser);
res.status(201).send({
message: "Thought added",
userData: newListOfThoughts,
});
} catch (error) {
console.log(error);
res.json(error.message);
}
}
};

Related

Getting Empty Object during Put request to the server

I created an API for following and followers user for Social Media Application, while request from postman getting empty object: {} But it seems to me to be correct.
Model:
const mongoose = require("mongoose");
const UserSchema = mongoose.Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false,
},
profilePicture: String,
coverPicture: String,
about: String,
livesin: String,
workAt: String,
relationship: String,
followers: [],
following: []
},{
timestamps:true
}
);
const UserModel = mongoose.model("Users", UserSchema);
module.exports = UserModel;
UserControler:
const UserModel = require("../Models/Usermodel");
const bcrypt = require("bcryptjs");
const followUser = async (req, res) => {
const id = req.params.id.trim();
const { currentUserId } = req.body;
if (currentUserId === id) {
res.status(403).send("Action forbiden");
} else {
try {
const followUser = await UserModel.findById(id);
const followingUser = await UserModel.findById(currentUserId);
if (!followUser.followers.includes(currentUserId)) {
await followUser.updateOne({ $push: { followers: currentUserId } });
await followingUser.updateOne({ $push: { following: id } });
res.status(200).send({message:"User Followed"});
} else {
res.status(403).send("User alredy followed by you!");
}
} catch (error) {
res.status(500).send(error);
}
}
};
module.exports = { getUser, updateUser, userDelete, followUser };
UserRoute:
const express = require("express");
const {getUser,updateUser, userDelete, followUser} = require("../Controller/userControler");
const router = express.Router()
router.get("/:id",getUser)
router.put("/:id",updateUser)
router.delete("/:id", userDelete)
router.put("/:id/follow", followUser)
module.exports=router;
index.js:
app.use("/user",UserRoute)
Here is the complete details regarding the error, let me know what happens in the code, thank you.
i assume that you have all the other functions other than followUser in your controller.js
The thing is that you must first specify the field name on the basis of which you want to update the document.
Here is what you need to do;
const UserModel = require("../Models/Usermodel");
const bcrypt = require("bcryptjs");
const mongoose = require("mongoose");//updated line
const followUser = async (req, res) => {
const id = req.params.id.trim();
const { currentUserId } = req.body;
if (currentUserId === id) {
res.status(403).send("Action forbiden");
} else {
try {
const followUser = await UserModel.findById({_id: mongoose.Types.ObjectId(id)});
const followingUser = await UserModel.findById({_id: mongoose.Types.ObjectId(currentUserId)});
if (!followUser.followers.includes(currentUserId)) {
await followUser.updateOne({_id: mongoose.Types.ObjectId(*id of the user you want to update*)},{ $push: { followers: currentUserId } });
await followingUser.updateOne({_id: mongoose.Types.ObjectId(*id of the user you want to update*)}{ $push: { following: id } });
res.status(200).send({message:"User Followed"});
} else {
res.status(403).send("User alredy followed by you!");
}
} catch (error) {
res.status(500).send(error);
}
}
};
module.exports = { getUser, updateUser, userDelete, followUser };
And while hitting the api pls make sure that your route should be
localhost:port-number/user/12345789/follow
and also make sure that the API type in postman must be same as in the backend e.g; PUT
please try findByIdAndUpdate query insted of using updateOne

In a MERN and Axios app, using the mongoose populate function, the field populates in the server/terminal but not on the front end

I am developing a MERN app with axios and trying to populate a field(songList) in a model (User) that is referencing the Schema.Types.ObjectId of another schema (Song).
The _id populates when I create a new Song appropriately.
I can see the entire referenced field is populated in the terminal server side with a console.log but the I cannot get the field to populate on the client side.
My Model; I am trying to populate songList.
const { Schema, model } = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = new Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
songList: [
{
type: Schema.Types.ObjectId,
ref: 'Song'
}
],
});
userSchema.pre('save', async function (next) {
if (this.isNew || this.isModified('password')) {
const saltRounds = 10;
this.password = await bcrypt.hash(this.password, saltRounds);
}
next();
});
userSchema.methods.isCorrectPassword = async function (password) {
return bcrypt.compare(password, this.password);
};
const User = model("User", userSchema);
module.exports = User;
My server side query, console.log(userSongs) and console.log(user.songList) shows the array of songs appropriately in the terminal:
//login
router.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
User.findOne({ username: username })
.populate({path: "songList"})
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
res.status(404).json({ message: 'User Not Found' });
}
const passwordIsValid = bcrypt.compareSync(
password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({ message: "Invalid Password" });
}
let userSongs = [];
for (let i = 0; i < user.songList.length; i++) {
userSongs.push(user.songList[i])
}
const accessToken = sign(
{ username: user.username, _id: user._id },
"importantsecret");
// res.json({
// token: accessToken,
// username: username,
// _id: user._id,
// songList: user.songList
// });
res.status(200).send({
token: accessToken,
username: username,
_id: user._id,
userSongs: userSongs
});
console.log(userSongs)
});
});
The client side request for the user information where I am hoping to see a populated songList with console.log(singleUser):
const [singleUser, setSingleUser] = useState({})
const [userSongs, setUserSongs] = useState([])
useEffect(() => {
Axios.get(`http://localhost:3001/api/user/${id}`).then((response) => {
setSingleUser(response.data)
})
}, [authState])
Client side login request.
const login = () => {
const data = { username: username, password: password };
Axios
.post("http://localhost:3001/api/user/login", data)
.then((response) => {
if (response.data.error) {
console.log(response.data.error)
} else {
localStorage.setItem('accessToken', response.data.token)
setAuthState({
username: response.data.username,
_id: response.data._id,
status: true
});
window.location.replace('/')
}
})
}
Here is where I create a new Song and add it to the user that is logged in.
router.post('/insert', (req, res) => {
const id = req.body.id;
const songTitle = req.body.songTitle;
const artist = req.body.artist;
const album = req.body.album;
Song.create({ songTitle: songTitle, artist: artist, album: album })
.then((song) => {
return User.findOneAndUpdate(
{ _id: id },
{ $addToSet: { songList: song._id } },
{ new: true }
)
})
.then((user) =>
!user
? res.status(404).json({
message: 'Song created, but found no user with that ID',
})
: res.json('Created the song')
)
.catch((err) => {
console.log(err);
res.status(500).json(err)
})
});
Any suggests on how I can get songList to populate using the populate() mongoose function is much appreciated.
Thank you,
Brandon
I've read articles on stack overflow, 'Mongoose 'populate' not populating",
"Mongoose .populate() not working correctly". Medium articles, and the mongoose documentation.
I've tried sending the user songs in the response back as res.json() and res.send(). The field shows up but is not populated.
I've tried being more specific with songList.songTitle and {path: "songList")
All of these show the field populated in the terminal but not on the front side.

How to make your code wait for execution of loop

Following is my getUser function
const getUsers = async (req, res) => {
try {
ddb.get({
TableName: "Tablename",
Key: { Username: req.query.username }
},(err,user) => {
if(err || Object.keys(user).length === 0) {
return res.status(404).json("Something went wrong")
} else {
const userSociety = Object.keys(user.Item.Societies)[0]
ddb.get({
TableName: "Tablename",
Key: { SocietyCode: userSociety }
},(err, society) => {
if(err || Object.keys(society).length === 0) {
return res.status(400).json({ message: "Could not fetch society members" })
} else {
const users = Object.keys(society.Item.SocietyMembers)
const usersData = []
users.forEach(async u => {
ddb.get({
TableName: "TestMyMohallaUsers",
Key: { Username: u }
},async (err,user) => {
if(err || Object.keys(user).length === 0) {
} else usersData.push({
Username: user.Item.Username,
Firstname: user.Item.Name
})
})
})
return res.status(200).json({ message: "Data detched successfully", Users: usersData })
}
})
}
})
} catch (error) {
return res.status(500).json({ error: "Internal Server Error" })
}
}
I want to wait for the execution of forEach and then send back the data via return statement but as of now the return statement gives empty array of users.
Clearly my code in not waiting for the execution of forEach and then returning the data. How can I do that someone help me?
Edit: ddb is an instance of DynamoDB
You'll have a better time if you
use the DynamoDB Promise API instead of a pyramid of callbacks
refactor your code to a couple of functions
Finally, awaiting for all user fetches to complete requires Promise.all for all of those promises.
async function getUser(ddb, username) {
const user = await ddb
.get({
TableName: "TestMyMohallaUsers",
Key: { Username: username },
})
.promise();
if (!user.Item) {
throw new Error(`User ${username} not found`);
}
return user.Item;
}
async function getSociety(ddb, societyCode) {
const society = await ddb
.get({
TableName: "Tablename",
Key: { SocietyCode: societyCode },
})
.promise();
if (!society.Item) {
throw new Error(`Society ${societyCode} not found`);
}
return society.Item;
}
const getUsers = async (req, res) => {
try {
const user = await getUser(ddb, req.params.username);
const userSocietyCode = Object.keys(user.Societies)[0];
const society = await getSociety(ddb, userSocietyCode);
const societyUsers = Object.keys(society.SocietyMembers);
const usersData = await Promise.all(
societyUsers.map(async (member) => {
const user = await getUser(ddb, member);
return {
Username: user.Username,
Firstname: user.Name,
};
}),
);
return res
.status(200)
.json({
message: "Data detched successfully",
Users: usersData,
});
} catch (e) {
return res
.status(400)
.json({ message: `Could not fetch information: ${e}` });
}
};

Error in hashing password with bcrypt-nodejs

Have been trying to create a password hash in my nodejs code
But it not working and not showing any error message for me to debug.
And my user is not creating also.
I dont know if there is a better way to do it.
This is my file which is responsible for the code...Model/User.js
const Promise = require('bluebird')
const bcrypt = Promise.promisifyAll(require('bcrypt-nodejs'))
function hashPassword (user) {
const SALT_FACTOR = 8
if (!user.changed('password')) {
return;
}
return bcrypt
.genSaltSyncAsync(SALT_FACTOR)
.then(salt => bcrypt.hashAsync(user.password, salt, null))
.then(hash => {
user.setDataValue('password', hash)
})}
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
beforeCreate: hashPassword,
beforeUpdate: hashPassword,
beforeSave: hashPassword
}
})
User.prototype.comparePassword = function (password) {
return bcrypt.compareAsync(password, this.password)
}
return User }
Does the following snippet help in any way?
const bcrypt = require('bcryptjs');
const userAbc = {
email: 'user#user.com',
password: '1234'
}
async function hashPassword(user) {
try {
const hashedPassword = await bcrypt.hash(user.password, 12);
user.password = hashedPassword;
console.log(user);
} catch (err) {
console.log(err);
}
}
hashPassword(userAbc);
I did change the bcrypt-nodejs to bcryptjs then replace genSaltSyncAsync to genSaltSync and everyother Async to Sync and it worked.

TypeError: verifyJsonWebToken is not a function

I created two model (I use MongoDB):
User: name, email, password, isBanned, blogPosts[].
BlogPost: title, content, date, author.
with author: {type:ObjectId, ref: 'User'}
I want insert a new blog post with Json Web Token. But I received error: : TypeError: verifyJWT is not a function
Here is my code:
const { verifyJWT } = require('./User')
const insertBlogPost = async (title, content, tokenKey) => {
try {
//Check Login with tokenKey
let signedInUser = await verifyJWT(tokenKey) //<-Code not run here
let newBlogPost = await BlogPost.create({
title, content,
date: Date.now(),
author: signedInUser
})
await newBlogPost.save()
await signedInUser.blogPosts.push(newBlogPost._id)
await signedInUser.save()
return newBlogPost
} catch(error) {
throw error
}}
module.exports = {
BlogPost,
insertBlogPost,
}
Here's verifyJWT from User
const jwt = require('jsonwebtoken')
const verifyJWT = async (tokenKey) => {
try {
let decodedJson = await jwt.verify(tokenKey, secretString)
if(Date.now() / 1000 > decodedJson.exp) {
throw "Token timeout, please Login again"
}
let foundUser = await User.findById(decodedJson.id)
if (!foundUser) {
throw "Can not find user with this token"
}
if(foundUser.isBanned === 1) {
throw "User looked"
}
return foundUser
} catch(error) {
throw error
}}
module.exports = { User, verifyJWT }
And here's my router:
router.post('/insertBlogPost', async (req, res) =>{
let {title, content} = req.body
//Client must send tokenKey
let tokenKey = req.headers['x-access-token']
try {
let newBlogPost = await insertBlogPost(title, content, tokenKey)
res.json({
result: 'ok',
message: 'Create BlogPost successfully!',
data: newBlogPost
})
} catch(error) {
res.json({
result: 'failed',
message: `${error}`
})
}})
Github: https://github.com/vantuan1303/authentication

Categories

Resources