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.

0
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.