DEV Community

Cover image for MANAGING A TEAM WITH NODE.JS (PART 2)
Candie
Candie

Posted on

1

MANAGING A TEAM WITH NODE.JS (PART 2)

In case you missed the first part of this article, read here to catch up

In the last article, we were able to

  • Create a team
  • Send team invitation,
  • Accept team invitation

In this article, we will be talking about

  • Rejecting Invitation
  • Leaving a team
  • Removing member from a team
  • Managing roles in a team

Quickly,

  • let us create two new users,
  • Send invitation from one user to another,
  • then we continue from there

PAYLOAD

// user 1
{
    "name": "john doe",
    "email": "johndoe@gmail.com",
    "password": "test1234"
}
// user 2
{
    "name": "jane doe",
    "email": "janedoe@gmail.com",
    "password": "test1234"
}
Enter fullscreen mode Exit fullscreen mode

STEP 1

Let us send invitation from john doe to jane doe

PAYLOAD:

{
    "email": "janedoe@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "data": "invitation sent to janedoe@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

STEP 2

Let us take a look at the user object for john and jane

// john
 "data": {
        "user": {
            "_id": "647afd1914c2f48dfc18c846",
            "name": "john doe",
            "email": "johndoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 1,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 5,
            "id": "647afd1914c2f48dfc18c846"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Inside john's object, the team array contains a team with only one member, which is john, and he is the admin

{
    "status": "success",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0N2FmZDQwMTRjMmY0OGRmYzE4Yzg0OSIsImlhdCI6MTY4NTc4MjUwMiwiZXhwIjoxNjkzNTU4NTAyfQ.NABFYJzvEvOfWUwy5cBnecIrHgDflrF3QVaAsYw_rcc",
    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 2,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In the jane user object, the team array is empty, because obviously, jane is not a member of any team yet,

And the invitation array contains one team, nodejs team, the invitation that was sent by john.

STEP 3

REJECTING INVITATION

Now let us compute how to reject the invitation sent by john.

Back to our team controller file

NOTE: Basically the concept is finding the index of the team in the invitation array and deleting it with the Array.splice() method.

// reject team invitation
exports.rejectInvitation = async (req, res) => {
  // find user in db

  const user = await User.findById(req.user.id);

  // find team in db
  // teamID will be gotted as a parameter when we run the request
  const team = await Team.findById(req.params.teamId);

  // check if team exists
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exists, let us find the index of the team in the invitations array
  const teamIndex = user.invitations.indexOf(team.id);

  // remove team id from the invitations array

  user.invitations.splice(teamIndex);

  // save changes
  await user.save();

  return res.status(200).json({
    status: "success",
    message: "invitation rejected",
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 4

Let us create a route for this request and run it on post man,
NOTE: Pass the team ID as a parameter for this request

router.post(
  "/reject-invitation/:teamId",
  middleware.protect,
  teamController.rejectInvitation
);
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "invitation rejected"
}
Enter fullscreen mode Exit fullscreen mode

To confirm the invitation was rejected, let us take a look at the user object for jane again.

"data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 3,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Now the invitation array is empty, because we have successfully deleted the team from the invitation array.

STEP 5

LEAVING A TEAM

To compute this, let us send a new invitation to jane, then login to jane account to accept the invitation, then we can proceed to leave a team.

Because you have to be a member of a team to leave a team.

RESPONSE:

{
    "status": "success",
    "data": "invitation sent to janedoe@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "invitation to nodejs team accepted"
}
Enter fullscreen mode Exit fullscreen mode

Let us take a look at jane user object to be sure she's now a member of nodejs team

"data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        },
                        {
                            "name": "jane doe",
                            "email": "janedoe@gmail.com",
                            "id": "647afd4014c2f48dfc18c849",
                            "role": "member"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 2,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 5,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

As you can see, the node.js team has two members, the admin which is john doe, and a jane doe, a member.

STEP 6

Let us proceed to our controller and compute leaving a team

exports.leaveTeam = async (req, res) => {
  // find user in db
  const user = await User.findById(req.user.id);

  // find team in db
  const team = await Team.findById(req.params.teamId);

  // check if team exists
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exist, check if user is a member of the team
  const member = team.members.find((item) => item.id === user.id);

  // if user is not a member, throw an error and terminate
  if (!member) {
    return res.status(400).json({
      status: "failed",
      message: `you are not a member of ${team.name.toUpperCase()}`,
    });
  }

  // if user is a member, find index of team in the team array on the user object
  const teamIndex = user.teams.indexOf(team.id);

  // delete team from team array
  user.teams.splice(teamIndex);

  // remove user from member array on the team
  const newMembers = team.members.filter((item) => item.id !== member.id);
  team.members = newMembers;

  // save changes
  await team.save();
  await user.save();

  return res.status(200).json({
    status: "success",
    message: `successfully exited ${team.name}`,
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 7

Let us create a route for this request and run it on postman

router.post(
  "/leave-team/:teamId",
  middleware.protect,
  teamController.leaveTeam
);
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "successfully exited nodejs team"
}
Enter fullscreen mode Exit fullscreen mode

To confirm it was successful, let us take a look at the jane user object once again,

    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 6,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

As expected, the teams array is empty, which means the request was successful.

STEP 8

REMOVING MEMBER FROM A TEAM

Let us repeat the process of sending an invitation to jane and accepting the invitation, then logging in back to john account to remove jane from the team

    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        },
                        {
                            "name": "jane doe",
                            "email": "janedoe@gmail.com",
                            "id": "647afd4014c2f48dfc18c849",
                            "role": "member"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 4,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 8,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Jane is already a member of the nodejs team, now let us log back in to john's account to compute removing jane from the nodejs team.

exports.removeMemberFromTeam = async (req, res) => {
  // extract userId and teamId from the request query
  const { userId, teamId } = req.query;

  // find team in db
  const team = await Team.findById(teamId);
  const user = await User.findById(userId);

  // check if team exist
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exist, check if user is a member of the team
  const member = team.members.find((item) => item.id === userId);

  // if user is not a member, throw an error and terminate
  if (!member) {
    return res.status(400).json({
      status: "failed",
      message: `you are not a member of ${team.name.toUpperCase()}`,
    });
  }

  // if user is a member, filter user out of the team
  const newTeamMember = team.members.filter((item) => item.id !== userId);
  team.members = newTeamMember;

  // remove teamId from teams array on user object
  const teamIndex = user.teams.indexOf(team.id);
  user.teams.splice(teamIndex);

  // save changes
  await team.save();
  await user.save();

  return res.status(200).json({
    status: "success",
    message: `${user.name} successfully removed from ${team.name}`,
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 9

Let us create a route for this request and run it in postman

router.post(
  "/remove-member",
  middleware.protect,
  teamController.removeMemberFromTeam
);
Enter fullscreen mode Exit fullscreen mode
// request url
http://localhost:8000/api/v1/team/remove-member?teamId=647aff306dcdb9d9546c7b1b&userId=647afd4014c2f48dfc18c849
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "jane doe successfully removed from nodejs team"
}
Enter fullscreen mode Exit fullscreen mode

To confirm, let us take a look at the nodejs team again

"teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        }
                    ],
Enter fullscreen mode Exit fullscreen mode

Nodejs team only contains john, who is the admin, meaning our logic works too.

So far we have been able to

  • Create a team
  • Send team invitation
  • Accept team invitation
  • Reject team invitation
  • Leave team
  • Remove member from team

In the final part of this article, we will be talking about ROLE MANAGEMENT IN A TEAM

I hope you are excited.

Meanwhile Let me know your thoughts on this in the comments section.

Get my source code here
View my API documentation here

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay