Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.env
138 changes: 88 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,92 +1,130 @@
### Task: Build a Real-Time Chat Application
## Chat Application Backend

In this assignment, your task is to create a real-time chat application using Node.js, Express.js, TypeScript, and Socket.io. This chat application will enable users to communicate with each other by sending text messages and sharing images in real-time within a chat room.
### Introduction

### Requirements:
This documentation summarizes the approach taken for building the backend for a chat application using Node.js, Express, and Socket.IO. The chat application allows users to connect, join rooms, send messages, and leave rooms. This document explains the high-level architecture, the choice of technologies, and key implementation details.

1. **Backend Setup with Node.js, Express.js, TypeScript, and Socket.io:**
### Technologies Used

- Set up a Node.js server using Express.js to handle HTTP requests and Socket.io to enable real-time WebSocket communication.
- Node.js: Chosen for its server-side capabilities, scalability, and extensive package ecosystem.
- Express.js: Used as the web application framework for handling HTTP requests.
- Socket.IO: A WebSocket library for real-time, bidirectional communication between clients and the server.
- Cors: Middleware for enabling Cross-Origin Resource Sharing, allowing communication with different domains.
- TypeScript: Used for server-side scripting and for type safety.
- High-Level Architecture

2. **User Authentication:**
The chat application backend follows a server-client architecture. It uses WebSocket technology to enable real-time communication between clients (users) and the server.

- Implement user authentication to allow users to join the chat room with a unique username. Users should not be able to use the same username simultaneously.
Server: The backend server is responsible for handling HTTP requests, managing WebSocket connections, and facilitating communication between users.
Clients: Users connect to the server via WebSocket connections and interact with each other in real-time.

3. **Chat Room Creation:**
### Key Features and Implementation

- Create a chat room where users can join and exchange messages. Multiple chat rooms may be supported, and users should be able to choose a room to join.
Socket.IO Integration: Socket.IO is integrated into the Express.js application to handle WebSocket connections. When a client connects to the server, a WebSocket connection is established.

4. **Real-Time Text Messaging:**
### User Management:

- Implement real-time text messaging functionality within the chat room. Messages sent by one user should be immediately visible to all other users in the same chat room.
- Users are identified by a unique identifier (id) and a chosen username.

5. **Image Sharing:**
![username](https://imgur.com/yM1wYvo.jpg)

- Enable users to upload and share images within the chat. Images should be uploaded to the server, temporarily stored, and then shared as part of the chat conversation.
![username](https://imgur.com/luqrSwN.jpg)

6. **Error Handling:**
---

- Implement error handling mechanisms to gracefully handle scenarios such as user disconnections, failed image uploads, or other potential issues that may arise during real-time communication.
Message Handling

7. **Documentation:**
- Users can add their username by emitting the "add_user" event. If the username is taken, they receive a notification.

- Create Postman collection or Swagger documentation for your WebSocket API endpoints. The documentation should provide comprehensive information about the WebSocket events, data structures, and message formats used for communication.
![username](https://imgur.com/eyKZfVu.jpg)

### Submission Details:
![username](https://imgur.com/PuKPTmk.jpg)

To submit your assignment, follow these steps:
![username](https://imgur.com/D1Pesks.jpg)

1. **Fork Repository:**
![username](https://imgur.com/Ivk231x.jpg)

- Fork the provided GitHub repository to create your own copy.
---

2. **Create Branch:**
- Users can join rooms by emitting the "join_room" event. If a user attempts to join a room they're already in, they receive a notification.

- Create a new branch in your forked repository with your name (e.g., "yourname_assignment").
![join_room](https://imgur.com/7oXrlZW.jpg)

3. **Commit Regularly:**
![join_room](https://imgur.com/iWVDQ6M.jpg)

- Commit your code regularly to the branch so that your progress can be tracked.
---

4. **Pull Request:**
- Users can send messages to rooms by emitting the "send_message" event. Messages can include text and optional images.
- Messages are broadcast to all users in the same room.

- Once you have completed the assignment, create a pull request from your branch to the main repository. Replace "main repository" with the URL of the original repository.
![send_message](https://imgur.com/1ksXWsL.jpg)

5. **README.md:**
![send_message](https://imgur.com/xp1wOYt.jpg)

- Include a README.md file in your project repository. This README should explain your approach to building the chat application, any challenges you faced, and any additional features or improvements you would have added if given more time.
![send_message](https://imgur.com/5fxVw1s.jpg)

6. **Documentation Link:**
![send_message](https://imgur.com/0DnQPTU.jpg)

- Include the link to your Postman collection or Swagger documentation in the README.md file.
![send_message](https://imgur.com/ipJzxMf.jpg)

### Evaluation Criteria:
---

Your assignment will be evaluated based on the following factors:
- Leaving Rooms:
- Users can leave rooms by emitting the "leave_room" event. When a user leaves a room, a message is broadcast to inform others.

- **Technical Proficiency:**
- Demonstrating your skills in using Node.js, Express.js, TypeScript, and Socket.io to build a functional real-time chat application with image sharing.
![leave](https://imgur.com/fGVm11X.jpg)

- **Code Quality:**
- Writing clean, well-structured, and maintainable code with proper comments and strict typing.
![leave](https://imgur.com/BFDCytf.jpg)

- **WebSocket Implementation:**
- Creating WebSocket endpoints and handling real-time communication effectively.
![leave](https://imgur.com/7UWfLtE.jpg)

- **User Authentication:**
- Implementing a secure user authentication system.
---

- **Image Upload:**
- Allowing users to upload and share images in the chat room.
### Error Handling:

- **Error Handling:**
- Implementing error handling mechanisms for a robust chat application.
The application handles potential errors, such as joining a non-existing room or leaving a room the user never joined which shown in the above images.

- **Documentation:**
- Providing detailed Postman collection or Swagger documentation for the WebSocket API endpoints.
## Conclusion

### Submission Deadline:
The chat application backend provides real-time communication capabilities for users. It's built using Node.js, Express.js, and Socket.IO, making it suitable for chat applications requiring low-latency communication. Proper error handling ensures a smooth user experience, and the application can be easily scaled to accommodate a growing user base.

You have 3-4 days from the date you received this assignment to complete and submit it.
This documentation provides an overview of the approach used to build the chat application backend, including technologies, architecture, key features, and deployment considerations. It serves as a reference for developers and stakeholders involved in the project.

Good luck with the assignment! If you have any questions during the development process, feel free to reach out for clarification. Happy coding!
## Additional Features to Implement in Future:

Frontend Implementaion:

Implement frontend using Reactjs or Nextjs.

User Authentication:

Implement user authentication to ensure that only registered users can access the chat.
Add features like user registration, login, and password reset functionality.

Private Messaging:

Enable users to send private messages to specific users within a room.
Implement a feature for users to create private rooms for one-on-one conversations.

Message History:

Store and retrieve chat message history so that users can view previous messages when joining a room.
Implement pagination or infinite scroll for browsing through older messages.

Notifications:

Implement push notifications to alert users to new messages or mentions when they are not actively using the application.
Provide notification settings to allow users to customize their notification preferences.

User Profiles:

Create user profiles where users can set profile pictures and update their personal information.
Allow users to view the profiles of other users.

User Status:

Display the online/offline status of users in the chat.
Implement a feature to show when a user was last active.

Database:
Setting database for storing messages, users profiles, status etc.
114 changes: 114 additions & 0 deletions backend/actions/socket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Socket } from "socket.io";
import { Message } from "types";

// Import functions for managing rooms and users
import { getRooms, leaveRoom, roomJoin } from "../utils/rooms";
import { getUser, getUsers, userJoin } from "../utils/users";

/**
* Handles a new socket connection and sets up event listeners.
* @param {Socket} socket - The Socket.IO socket representing the connection.
*/
export const handleSocketConnection = (socket: Socket) => {
console.log(`User Connected: ${socket.id}`);

// Handle 'join_room' event
socket.on("join_room", (room: string) => {
handleJoinRoom(socket, room);
});

// Handle 'add_user' event
socket.on("add_user", (username: string) => {
handleAddUser(socket, username);
});

// Handle 'send_message' event
socket.on("send_message", (params: Message) => {
handleSendMessage(socket, params);
});

// Handle 'leave_room' event
socket.on("leave_room", (room: string) => {
handleLeaveRoom(socket, room);
});
};

/**
* Handles the 'join_room' event, allowing a user to join a room.
* @param {Socket} socket - The Socket.IO socket representing the connection.
* @param {string} room - The name of the room to join.
*/
const handleJoinRoom = (socket: Socket, room: string) => {
if (!roomJoin(room, socket.id)) {
// Notify the user that they have already joined the room
socket.emit("username", "User already joined the room");
} else {
// Join the room and notify the user
socket.join(room);
socket.emit("username", "User joined the room");
console.log(getRooms());
}
};

/**
* Handles the 'add_user' event, allowing a user to join with a username.
* @param {Socket} socket - The Socket.IO socket representing the connection.
* @param {string} username - The username to be assigned to the user.
*/
const handleAddUser = (socket: Socket, username: string) => {
if (!userJoin(socket.id, username)) {
// Notify the user that the chosen username is already taken
socket.emit("username", `Username ${username} already taken`);

// console.log(getUsers());
} else {
// Notify the user that their username has been submitted successfully
socket.emit("username", `Username ${username} submitted successfully`);
console.log(getUsers());
}
};

/**
* Handles the 'send_message' event, allowing a user to send a message or image to a room.
* @param {Socket} socket - The Socket.IO socket representing the connection.
* @param {Message} params - The message parameters, including message content, image, and room.
*/
const handleSendMessage = (socket: Socket, params: Message) => {
const { message, img, room } = params;

let username: string | undefined = getUser(socket.id);
if (!username) username = socket.id;

if (!message && !img) {
// Log an error if no message or image is provided
console.error("Please send any message or image");
} else if (!message) {
// Broadcast the image to the room
socket.broadcast.to(room).emit("receive_image", { username, img });
} else {
// Broadcast the message to the room
socket.broadcast.to(room).emit("receive_message", { username, message });
}
};

/**
* Handles the 'leave_room' event, allowing a user to leave a room.
* @param {Socket} socket - The Socket.IO socket representing the connection.
* @param {string} room - The name of the room to leave.
*/
const handleLeaveRoom = (socket: Socket, room: string) => {
// Broadcast a message to the room when a user leaves
if (leaveRoom(socket.id, room)) {
let username: string | undefined = getUser(socket.id);
if (!username) username = socket.id;

socket.broadcast
.to(room)
.emit("receive_message", `${username} has left the chat`);
socket.leave(room);

console.log(getRooms());
} else {
console.error("You cannot leave because you did not join the room before");
}
};
36 changes: 36 additions & 0 deletions backend/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import express from "express";
import http from "http";
import cors from "cors";
import { Socket, Server } from "socket.io";

// Import the function to handle socket connections
import { handleSocketConnection } from "./actions/socket";

// Create an Express application
const app = express();

// Enable Cross-Origin Resource Sharing (CORS)
app.use(cors());

// Create an HTTP server using Express
const server = http.createServer(app);

// Create a Socket.IO server instance, configuring CORS settings
const io = new Server(server, {
cors: {
origin: true, // You can specify the allowed origins here
// origin: "http://locahost:3000",
methods: ["GET", "POST"],
},
});

// Handle incoming socket connections
io.on("connection", (socket: Socket) => {
handleSocketConnection(socket);
});

// Define the server's port, using the specified environment variable or a default value
const PORT = process.env.PORT || 5000;

// Start the server and listen on the specified port
server.listen(PORT, () => console.log(`Server started on PORT ${PORT}`));
25 changes: 25 additions & 0 deletions backend/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Represents a message object containing information about a message, image, username, and room.
*/
export type Message = {
username: string; // The username of the sender
message?: string; // The text message (optional)
img?: string; // The image URL (optional)
room: string; // The room in which the message is sent
};

/**
* Represents a user object with a unique identifier and a username.
*/
export type User = {
id: string; // The unique identifier for the user
username: string; // The username of the user
};

/**
* Represents a room object with a unique identifier and an array of user IDs.
*/
export type Room = {
room: string; // The unique identifier for the room
usersID: string[]; // An array of user IDs in the room
};
Loading