This repository contains the final project for SOLVD's Node.js development course.
- Task
- System Requirements
- Setup and Authentication
- Database Schema
- Security and Authentication
- API Endpoints
- Socket Events
- Testing
- CI/CD
- FAQ
Develop the Alias game, a multiplayer game built with Node.js. It includes chat functionality and a feature to check for similar words.
Alias is a word-guessing game where players form teams. Each team takes turns where one member describes a word and others guess it. The game includes a chat for players to communicate and a system to check for similar words.
Teams try to guess as many words as possible from their teammates' descriptions.
Each turn is timed. Describers cannot use the word or its derivatives.
Points are awarded for each correct guess. Similar words are checked for validation.
The game concludes after a predetermined number of rounds, with the highest-scoring team winning.
- Backend: Node.js
- Database: CouchDB
To install the project you must follow these steps:
- Install Node.js
- Clone this repository by running
git clone https://github.com/danielDeVita/solvd.laba.aliasProject.git
wherever you want to store the repository - Run
npm install
inside the repository directory to install all project dependencies. - To run the app locally run
npm start
To setup and run the database you must follow these steps:
- Install Docker
- Make sure the Docker Daemon is running. You can check this using the
docker info
command. - Run
docker compose up -d
inside the repository directory to build the database docker container - You can now access the database user interface here
Contains information about users
Property name | Type | Description |
---|---|---|
string |
user's email address | |
password | string |
the user's hashed password |
firstName | string |
the user's first name |
lastName | string |
the user's last name |
role | enum [user, admin, inactive] |
the user's role |
createdAt | date |
the date the user entry was created |
updatedAt | date |
the date when the user was last updated |
Contains information about a game room and its configuration
Property name | Type | Description |
---|---|---|
_id | string |
the user id generated by the db |
teamNumberOfPlayers | number |
the max amount of players each team can have |
roundTime | number |
The amount of time each round will take (in seconds) |
teamAPlayers | string[] |
the emails of the users playing in team A |
teamBPlayers | string[] |
the emails of the users playing in team B |
teamAPoints | number |
the amount of points scored by team A |
teamBPoints | number |
the amount of points scored by team B |
createdAt | date |
the date the room entry was created |
updatedAt | date |
the date when the room was last updated |
Contains messages sent to the chat
Property name | Type | Description |
---|---|---|
_id | string |
the message id generated by th db |
message | string |
the message sent |
roomId | string |
the id of the room the message was sent in |
createdBy | string |
the id of the user that created the room |
createdAt | date |
the date when the message was sent |
This project uses JWT tokens for authentication, clients must log in to receive a token which they must include as a header (named Authorization
) in every HTTP request they make (other than register and login).
Returns all chat messages
Example response:
[
{
"_id": "47471e5c-f695-43a3-883f-24eb133096b6",
"message": "Hello World!",
"roomId": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAT": "2007-11-09 T 11:20 UTC"
},
...
]
Returns all chat messages sent to a given room
Request param | type | description |
---|---|---|
roomId | string |
the id of the room we want to retrieve the messages from |
Example:
GET chat/c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1/messages/
[
{
"_id": "47471e5c-f695-43a3-883f-24eb133096b6",
"message": "Hello World!",
"roomId": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAT": "2007-11-09 T 11:20 UTC"
},
...
]
If roomId is not found
HTTP 404 NOT FOUND
{
"error": "Room not found"
}
Creates a room
Request body must include max. number of players for each team, round time, and number of rounds to play. Example:
{
"teamNumberOfPlayers": 2,
"roundTime": 60,
"roundsToPlay": 4
}
It returns the created room
{
"teamNumberOfPlayers": 2,
"roundTime": 60,
"roundsToPlay": 4,
"_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC",
"teamAPlayers": [],
"teamBPlayers": [],
"teamAPoints": 0,
"teamBPoints": 0
}
Returns a room by its id
Request param | type | description |
---|---|---|
roomId | string |
the id of the room we want to retrieve |
Example:
GET room/c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1
{
"teamNumberOfPlayers": 2,
"roundTime": 60,
"roundsToPlay": 4,
"_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC",
"teamAPlayers": [],
"teamBPlayers": [],
"teamAPoints": 0,
"teamBPoints": 0
}
If roomId is not found
HTTP 404 NOT FOUND
{
"error": "Room not found"
}
Retuns all created rooms. Example response:
GET room/
[
{
"teamNumberOfPlayers": 2,
"roundTime": 60,
"roundsToPlay": 4,
"_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC",
"teamAPlayers": [],
"teamBPlayers": [],
"teamAPoints": 0,
"teamBPoints": 0
},
{
"teamNumberOfPlayers": 4,
"roundTime": 50,
"roundsToPlay": 7,
"_id": "6bd5462f-4726-4d76-a3ef-4b4bc41cfc7d",
"createdBy": "janedoe@example.com",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC",
"teamAPlayers": [],
"teamBPlayers": [],
"teamAPoints": 0,
"teamBPoints": 0
},
...
]
Returns rooms created by a given user
Request param | type | description |
---|---|---|
userId | string |
the id of the user |
Example:
GET room/user/johndoe@example.com
{
"teamNumberOfPlayers": 2,
"roundTime": 60,
"roundsToPlay": 4,
"_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC",
"teamAPlayers": [],
"teamBPlayers": [],
"teamAPoints": 0,
"teamBPoints": 0
}
If userId is not a string
HTTP 400 BAD REQUEST
{
"error": "Invalid userId"
}
Joins a user to a room
Request param | type | description |
---|---|---|
id | string |
the id of the room we want to retrieve |
Body must include what team they want to join
{
"team": "teamA"
}
Returns info of the room they joined Example:
{
"teamNumberOfPlayers": 2,
"roundTime": 60,
"roundsToPlay": 4,
"_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
"createdBy": "johndoe@example.com",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC",
"teamAPlayers": [],
"teamBPlayers": [],
"teamAPoints": 0,
"teamBPoints": 0
}
If roomId is not found
HTTP 404 NOT FOUND
{
"error": "Room not found"
}
If user is already in a team
HTTP 403 FORBIDDEN
{
"error": "User is already in a team"
}
If selected team is already complete
HTTP 403 FORBIDDEN
{
"error": "Selected team is complete"
}
Registers a new user Body must include email, password, first name, last name and role. EXample:
{
"email": "johndoe@gmail.com",
"firstName": "john",
"lastName": "doe",
"role": "user",
"password": "12345678"
}
Returns the created user.
{
"email": "johndoe@example.com",
"firstName": "john",
"lastName": "doe",
"role": "user",
"password": "12345678",
"createdAt": "2007-11-09 T 11:20 UTC",
"updatedAt": "2007-11-09 T 11:20 UTC"
}
If user already exists
HTTP 409 CONFLICT
{
"error": "User already exists"
}
Logs a user in. Request body must contain email and password. Example:
{
"email": "johndoe@example.com",
"password": "12345678"
}
Returns the generated JWT token.
GET room/user/johndoe@example.com
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJFbWFpbGxAZ21haWwuY29tIiwicm9sZSI6InVzZXJhIiwiaWF0IjoxNzAwODM1MjU3LCJleHAiOjE3MDA4Mzg4NTd9.N_Nei7roLTAYbMAMUMwNGlvH2mpS-EZQi7zgp1ksHUc"
}
If user is not found
HTTP 404 NOT FOUND
{
"error": "User not found"
}
If password is incorrect
HTTP 400 BAD REQUEST
{
"error": "Invalid password"
}
In order for the chat and game to work across multiple clients in real time, this project uses socket.io to communicate different events across clients joined in the same room.
All events must include:
Event headers | type | description |
---|---|---|
roomId | string |
the id of the room |
Authorization | string |
users jwt token |
Joins a player to a chat room.
Removes a player from the chat room.
Sends a message to the chat room. Event message should look like:
{
"message": "Hello World!"
}
Indicated that a user is ready to play the game.
A user tries to guess the word. Event message should look like:
{
"word": "humus"
}
A user sends a hint to help his teammates guess the word. Event message should look like:
{
"hint": "A dip made of chickpeas"
}
The game begins. Event message should look like:
{
"roomId": "{{roomId}}"
}
This project uses jest for unit testing, to run said tests execute npm test
This repository uses GitHub actions for continuous integration. This action builds, tests and lints the project's code. The workflow file can be fount in .github/workflows/pipeline.yml
-
How do I start a game?
To start a game you must first posses a user, you can create a new one through the register functionality. Once you are a valid user, you must log in to obtain you JWT authentication token (Remember you must include this token in all HTTP request and Socket events as a header). The next step is to create a room, indicating the configuration for your game. Now all you have to do is wait for other users to join your room, once both teams have at least 2 members and everyone has sent the ready socket event, the start game socket event can be sent and the game will begin!
-
What happens if my team does't guess a word?
If your team hasn't guessed a particular word before the time runs out and a hint for that word has already been sent the other team will get 30 seconds to try and guess that same word using the hints your teammate has already sent. Once they guess the word or the 30 seconds are up, their normal turn will begin.
-
Why isn't my game starting?
Make sure you created the room with all the correct configurations, also check that the roomId fellow teammates are using to join the room is correct. Remember at least 2 players of each team mush emit the ready socket event to start the game.