MANAGING A TEAM WITH NODE.JS (PART 2)
Candie
Posted on June 3, 2023
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"
}
STEP 1
Let us send invitation from john doe to jane doe
PAYLOAD:
{
"email": "janedoe@gmail.com"
}
RESPONSE:
{
"status": "success",
"data": "invitation sent to janedoe@gmail.com"
}
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"
}
}
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"
}
}
}
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",
});
};
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
);
RESPONSE:
{
"status": "success",
"message": "invitation rejected"
}
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"
}
}
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"
}
RESPONSE:
{
"status": "success",
"message": "invitation to nodejs team accepted"
}
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"
}
}
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}`,
});
};
STEP 7
Let us create a route for this request and run it on postman
router.post(
"/leave-team/:teamId",
middleware.protect,
teamController.leaveTeam
);
RESPONSE:
{
"status": "success",
"message": "successfully exited nodejs team"
}
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"
}
}
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"
}
}
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}`,
});
};
STEP 9
Let us create a route for this request and run it in postman
router.post(
"/remove-member",
middleware.protect,
teamController.removeMemberFromTeam
);
// request url
http://localhost:8000/api/v1/team/remove-member?teamId=647aff306dcdb9d9546c7b1b&userId=647afd4014c2f48dfc18c849
RESPONSE:
{
"status": "success",
"message": "jane doe successfully removed from nodejs team"
}
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"
}
],
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.
Posted on June 3, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.