Reputation: 39
I am attempting to add a following/followers feature for a nodejs/mongoose driven website. I am having issues with getting the ID's stored properly using the methods below. Not quite sure what is going wrong but it appears to be saving only the ID properly to the following portion but not updating the followers for the first portion.
I know it would be easy if the user ID is just passed to the post request, but I thought storing the user ID on the front-end is kind of a security issue so just using the username to get the ID would be better.
// Handles the post request for following a user
router.post('/follow-user', function(req, res, next) {
// First, find the user from the user page being viewed
User.findOne({ username: req.body.username }, function(err, user) {
// Add to users followers with ID of the logged in user
user.followers = req.user._id;
// Create variable for user from page being viewed
var followedUser = user._id;
// Save followers data to user
user.save();
// Secondly, find the user account for the logged in user
User.findOne({ username: req.user.username }, function(err, user) {
// Add the user ID from the users profile the follow button was clicked
user.following = followedUser;
// Save following data to user
user.save();
});
});
});
The user model looks as such
var userSchema = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true },
avatar: { type: String },
bio: { type: String },
following: [{ type: Schema.ObjectId, ref: 'User' }],
followers: [{ type: Schema.ObjectId, ref: 'User' }],
});
Any insight on this would be greatly appreciated.
Upvotes: 2
Views: 4596
Reputation: 1135
I managed to implement the follow/unfollow
feature on my Node REST API by setting two routes instead of one:
For the Follow request:
following
arrayfollowers
array of the user you want to followFor the Unfollow request:
following
arrayfollowers
array of the user you want to unfollow
authenticate
is my custom middleware
id
params is the ID of the user you're trying to follow/unfollowres.user is returned from the "authenticate" middleware, please do not my answer.
router.patch('/follow/:id', authenticate, async (req, res) => {
try {
const id = new ObjectID(req.params.id)
// check if the id is a valid one
if (!ObjectID.isValid(req.params.id)) {
return res.status(404).json({ error: 'Invalid ID' })
}
// check if your id doesn't match the id of the user you want to follow
if (res.user._id === req.params.id) {
return res.status(400).json({ error: 'You cannot follow yourself' })
}
// add the id of the user you want to follow in following array
const query = {
_id: res.user._id,
following: { $not: { $elemMatch: { $eq: id } } }
}
const update = {
$addToSet: { following: id }
}
const updated = await User.updateOne(query, update)
// add your id to the followers array of the user you want to follow
const secondQuery = {
_id: id,
followers: { $not: { $elemMatch: { $eq: res.user._id } } }
}
const secondUpdate = {
$addToSet: { followers: res.user._id }
}
const secondUpdated = await User.updateOne(secondQuery, secondUpdate)
if (!updated || !secondUpdated) {
return res.status(404).json({ error: 'Unable to follow that user' })
}
res.status(200).json(update)
} catch (err) {
res.status(400).send({ error: err.message })
}
})
router.patch('/unfollow/:id', authenticate, async (req, res) => {
try {
const { id } = req.params
// check if the id is a valid one
if (!ObjectID.isValid(id)) {
return res.status(404).json({ error: 'Invalid ID' })
}
// check if your id doesn't match the id of the user you want to unfollow
if (res.user._id === id) {
return res.status(400).json({ error: 'You cannot unfollow yourself' })
}
// remove the id of the user you want to unfollow from following array
const query = {
_id: res.user._id,
following: { $elemMatch: { $eq: id } }
}
const update = {
$pull: { following: id }
}
const updated = await User.updateOne(query, update)
// remove your id from the followers array of the user you want to unfollow
const secondQuery = {
_id: id,
followers: { $elemMatch: { $eq: res.user._id } }
}
const secondUpdate = {
$pull: { followers: res.user._id }
}
const secondUpdated = await User.updateOne(secondQuery, secondUpdate)
if (!updated || !secondUpdated) {
return res.status(404).json({ error: 'Unable to unfollow that user' })
}
res.status(200).json(update)
} catch (err) {
res.status(400).send({ error: err.message })
}
})
Upvotes: 7
Reputation: 9268
From what i can see in your schema
, following
and followers
and an array of ObjectId's
and not ObjectId
itself, so you need to push
the _id
into the array and not set its value to _id
.
Also, do the second update
in the callback
of the save
. this way you can send response back to the frontend after both the updates
are done successfully.
Try this:
User.findOne({ username: req.body.username }, function(err, user) {
user.followers.push(req.user._id);
var followedUser = user._id;
user.save(function(err){
if(err){
//Handle error
//send error response
}
else
{
// Secondly, find the user account for the logged in user
User.findOne({ username: req.user.username }, function(err, user) {
user.following.push(followedUser);
user.save(function(err){
if(err){
//Handle error
//send error response
}
else{
//send success response
}
});
});
}
});
});
I hope this helps you!
Upvotes: 4