Real-time Chat Architecture
Relevant source files
The following files were used as context for generating this wiki page:
The real-time chat feature facilitates direct communication between users and sellers within the e-commerce platform. The architecture is composed of three main, decoupled services: a React-based frontend for the user interface, a Node.js/Express backend API for data persistence and business logic, and a dedicated Node.js/Socket.io server for handling real-time WebSocket connections and message broadcasting.
This separation of concerns allows the chat system to operate efficiently. The backend API manages conversation and message history, while the dedicated socket server focuses solely on low-latency message delivery and user presence tracking. The frontend integrates both services to provide a seamless and interactive chat experience.
Sources: README.md, socket/index.js, backend/app.js
System Architecture
The overall architecture consists of three primary components that interact to deliver the real-time chat functionality.
This diagram shows the high-level interaction between the frontend, backend API, and the dedicated Socket.io server.
Sources: README.md, frontend/src/pages/UserInboxPage.jsx, backend/controller/message.js:10-11
Backend API
The backend is a standard Node.js/Express application responsible for handling the business logic and data persistence for conversations and messages.
Sources: backend/app.js, backend/controller/conversation.js, backend/controller/message.js
API Endpoints
The chat functionality is exposed through a set of RESTful endpoints for managing conversations and messages. These are registered in the main application file.
| Method | Endpoint | Description | Controller File |
|---|---|---|---|
POST | /api/v2/conversation/create-new-conversation | Creates a new conversation between a user and a seller. | conversation.js |
GET | /api/v2/conversation/get-all-conversation-user/:id | Retrieves all conversations for a specific user. | conversation.js |
GET | /api/v2/conversation/get-all-conversation-seller/:id | Retrieves all conversations for a specific seller. | conversation.js |
PUT | /api/v2/conversation/update-last-message/:id | Updates the lastMessage and lastMessageId fields of a conversation. | conversation.js |
POST | /api/v2/message/create-new-message | Creates and persists a new message. Supports text and image uploads. | message.js |
GET | /api/v2/message/get-all-messages/:id | Retrieves all messages for a specific conversation ID. | message.js |
Sources: backend/app.js:68-69, backend/controller/conversation.js, backend/controller/message.js, frontend/src/components/Products/ProductDetails.jsx:70-77
Message Creation and Notification
When a new message is posted to the /api/v2/message/create-new-message endpoint, the backend performs two key actions:
- It saves the message data (including sender, text, conversation ID, and optional image URL) to the database.
- It emits a global
newMsgevent via Socket.io, signaling to all connected clients that new data is available and they should refetch message/conversation lists.
// backend/controller/message.js:8-30
router.post(
"/create-new-message",
upload.single("images"),
catchAsyncErrors(async (req, res, next) => {
try {
const io = req.app.locals.io; // Access socket.io instance
const messageData = req.body;
// ... image handling logic ...
const message = new Messages({
// ... message properties ...
});
await message.save();
io.emit("newMsg", { success: true }); // Emit global event
res.status(201).json({
success: true,
message,
});
} catch (error) {
return next(new ErrorHandler(error.message), 500);
}
})
);
Sources: backend/controller/message.js:8-30
Socket.io Server
A dedicated Socket.io server, running as a separate Node.js process, manages all real-time aspects of the chat.
Sources: socket/index.js, README.md
Connection and User Presence
The server maintains a list of currently online users.
- When a client connects, it emits an
addUserevent with itsuserId. - The server adds the user and their unique
socketIdto an in-memoryusersarray. - The server then broadcasts the updated list of online users to all clients via a
getUsersevent. - Upon disconnection, the
disconnectevent is triggered, and the server removes the user from theusersarray, again broadcasting the updated list.
This diagram illustrates the user connection and presence flow.
Real-time Events
The socket server listens for and emits several key events to facilitate real-time communication.
| Event Name | Direction | Payload | Description |
|---|---|---|---|
addUser | Client → Server | userId | Registers a connected user as online. |
getUsers | Server → Client | users (array) | Broadcasts the list of online users to all clients. |
sendMessage | Client → Server | { senderId, receiverId, text, images } | Sent by a client to send a message to another user. |
getMessage | Server → Client | { senderId, text, createdAt, images } | The server forwards a received message to the intended recipient client. |
updateLastMessage | Client → Server | { lastMessage, lastMessageId } | A client-side event to trigger an update, though the primary update mechanism is the newMsg event. |
newMsg | Server → Client | { success: true } | A global event emitted from the backend API to notify all clients to refetch data. |
disconnect | Client → Server | - | Standard Socket.io event when a client disconnects. |
Sources: socket/index.js, backend/controller/message.js:24, frontend/src/pages/Shop/ShopInboxPage.jsx, frontend/src/pages/UserInboxPage.jsx
Message Forwarding Flow
The primary role of the socket server is to relay messages between clients in real time.
This flow ensures that messages are delivered instantly without waiting for database polls.
Sources: socket/index.js:32-40, frontend/src/pages/Shop/ShopInboxPage.jsx:119-124, frontend/src/pages/UserInboxPage.jsx:140-145
Frontend Implementation
The frontend is built with React and uses socket.io-client to connect to the socket server and axios to communicate with the backend REST API. The primary components are UserInboxPage.jsx for customers and ShopInboxPage.jsx (which contains DashboardMessages.jsx) for sellers.
Sources: frontend/src/pages/UserInboxPage.jsx, frontend/src/pages/Shop/ShopInboxPage.jsx
Initializing a Chat Session
When a user opens the chat interface, the following sequence occurs:
- The component mounts and establishes a connection to the Socket.io server.
- It sends an
addUserevent to register its presence. - It makes an
axiosGET request to the backend API (/get-all-conversation-user/:idor/get-all-conversation-seller/:id) to fetch the list of existing conversations. - When a conversation is selected, another
axiosGET request is made to/get-all-messages/:idto fetch the message history for that chat.
Sources: frontend/src/pages/Shop/ShopInboxPage.jsx:65-75, 93-99, frontend/src/pages/UserInboxPage.jsx:69-82, 110-116
Sending and Receiving Messages
The process of sending and receiving a message involves both the REST API and the WebSocket server.
This diagram shows the complete flow when a user sends a message.
The frontend handles receiving messages through a useEffect hook that listens for the getMessage event. When a message arrives, it is added to the local state, triggering a re-render of the chat window.
// frontend/src/pages/Shop/ShopInboxPage.jsx:42-51
useEffect(() => {
socketId.on('getMessage', (data) => {
setArrivalMessage({
sender: data.senderId,
text: data.text,
createdAt: Date.now(),
});
});
}, []);
useEffect(() => {
arrivalMessage &&
currentChat?.members.includes(arrivalMessage.sender) &&
setMessages((prev) => [...prev, arrivalMessage]);
}, [arrivalMessage, currentChat]);
Sources: frontend/src/pages/Shop/ShopInboxPage.jsx:42-51, frontend/src/pages/UserInboxPage.jsx:55-64
Summary
The real-time chat architecture effectively separates concerns to provide a robust and scalable messaging solution. It leverages a standard RESTful API for data persistence and state management, ensuring data integrity and history. Simultaneously, it uses a dedicated Socket.io server to handle the real-time aspects, such as message broadcasting and user presence, which guarantees low-latency communication. This hybrid approach combines the reliability of REST with the speed of WebSockets, creating an efficient and responsive user experience.
Sources: README.md, socket/index.js, backend/app.js