What happens in Express JS when the group owner leaves?
I want to talk about this feature, and that’s for make database clean.
what happens if the group owner decides to leave?
if(userToDelete.id === groupOwner._id)
- User that he trying to leave is the same group owner
- First, note that the group owner cannot be listed among the members. Therefore, a professional database design separates the group owner from the members.
const groupSchema = new mongoose.Schema({
groupName: {
type: String,
required: true,
},
userId: {
type: String,
ref: 'User',
required : true,
},
privacyStatus : {
type: String,
enum: ['public', 'private'],
default: 'public'
},
postStatus : {
type : String,
enum : ['pending', 'rejected', 'accepted'],
default : 'accepted',
},
}, { timestamps: true });
groupSchema.virtual('posts', { // posts is a parameter to get related objects.
ref: 'Post',
localField: '_id',
foreignField: 'groupId' // This is in post model.
});
groupSchema.virtual('users', { // users is a parameter to get related objects.
ref: 'Membership',
localField: '_id',
foreignField: 'groupId' // This is in user model.
});
groupSchema.set('toObject', { virtuals: true });
groupSchema.set('toJSON', { virtuals: true });
const Group = mongoose.model('Group', groupSchema);
- Group Model
const membershipSchema = new mongoose.Schema({
userId: {
type: String,
ref: 'User',
required: true,
},
role : {
type : String,
enum : ['user', 'admin'],
default : 'user'
},
groupId: {
type: String,
ref: 'Group',
required: true
},
status : {
type: String,
enum: ['pending', 'accepted', 'rejected'],
default: 'accepted'
},
}, { timestamps: true });
const Membership = mongoose.model('Membership', membershipSchema);
- Membership Model
With this way, group owner become in same object of group. By (userId) field. That’s clean and simple. No additional data in queries.
- If the owner decides to leave, who will the group belong to? I will check the group members.
if(isMembershipsNotEmpty.length !== 0)
Check members in the target group
- If there are members, the first member to join the group will become the new owner.
if(isMembershipsNotEmpty.length !== 0){
// When group owner leaving, the first member in group will be the owner.
const newOwner = await Membership.findOne({groupId: req.params.groupId}).sort({createdAt: 1})
await Group.findOneAndUpdate({userId: groupOwner._id}, {userId: newOwner.userId});
await Membership.findOneAndDelete({
userId: req.params.userId,
groupId: req.params.groupId,
status: 'accepted'})
- If there is members is group; the oldest member will be the new owner
- If there are no members, the group will be deleted to maintain a clean database and user experience free from empty group.
await Group.findOneAndDelete({userId: groupOwner.id})
return res.status(200).send('You leaved the group. Group deleted successfully.')
- Group owner leave and group deleted successfully
- The endpoint
router.delete('/:groupId/:userId/delete', ensureAuth(), getUserOrMembershipOrGroup, async(req, res) => {
try{
const groupOwner = await User.findById(req.user.id);
const isAdmin = await Membership.findOne({userId : req.user.id, groupId: req.params.groupId, role : 'admin'})
if(!groupOwner || (!groupOwner && !isAdmin)){
return res.status(403).send('Access denied. Only the group owner or an admin can perform this action.');
};
const group = await req.targetGroup;
// Is authenticated user not the group owner or admin?
if((groupOwner.id !== group.userId && !isAdmin) || (groupOwner.id !== group.userId && !isAdmin)){
return res.status(403).send('Access denied. Only the group owner or an admin can perform this action.');
};
// Is user to delete in group?
const userToDelete = await req.targetUser;
const isMembership = await req.targetMembership;
// Is group owner tying to delete him self?
if(userToDelete.id === groupOwner._id){
const isMembershipsNotEmpty = await Membership.find();
if(isMembershipsNotEmpty.length !== 0){
// When group owner leaving, the first member in group will be the owner.
const newOwner = await Membership.findOne({groupId: req.params.groupId}).sort({createdAt: 1})
await Group.findOneAndUpdate({userId: groupOwner._id}, {userId: newOwner.userId});
await Membership.findOneAndDelete({
userId: req.params.userId,
groupId: req.params.groupId,
status: 'accepted'})
}else{
// If there is no members in group, owner will leaving and group will delete.
await Group.findOneAndDelete({userId: groupOwner.id})
return res.status(200).send('You leaved the group. Group deleted successfully.')
}
return res.status(200).send(`You leaved your group. You are not the owner any more.`)
};
if(!userToDelete || !isMembership){
return res.status(404).send('User not exists.');
};
if(isMembership.role === 'admin' && !isMembership.userId === req.params.userId){
return res.status(400).send('You can\'t remove admins.')
};
if(isMembership.userId === req.params.userId){
await Membership.findOneAndDelete({
userId: req.params.userId,
groupId: req.params.groupId,
status: 'accepted'
});
return res.status(200).send(`${userToDelete.id} deleted successfully.`)
};
}catch(err){
console.log(err);
return res.send('Server error.')
}
});
The source code: https://github.com/AbdelrahmanMahmoud92/Social-Network-Express-JS/blob/main/src/routes/groups/removeUser.js
The whole project: https://github.com/AbdelrahmanMahmoud92/Social-Network-Express-JS
Thank you if reached here.
Subscribe to my newsletter
Read articles from Abdelrahman Mahmoud directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Abdelrahman Mahmoud
Abdelrahman Mahmoud
Hello. I'm a Backend developer with Django and Node JS. Sharing my journey.