DEV Community

Cover image for 💬 Building a Real-time Chat Feature for Virtual Gift Store Using Socket.IO with MERN Stack 🚀
Md. Sajjad Hosen Shohan
Md. Sajjad Hosen Shohan

Posted on

💬 Building a Real-time Chat Feature for Virtual Gift Store Using Socket.IO with MERN Stack 🚀

Chat feature in virtual gift store

Project Overview:

Giftly is a virtual gift store management platform where users can communicate with the admin in real-time using a built-in chat feature. This feature enables seamless interaction between normal users and the admin, making the customer support process faster and more interactive. The chat functionality is built using Node.js, React.js, Mongoose, Socket.IO, and Express.js.

Key Features of the Chat System

  1. - Real-time Messaging: Communication happens instantly between users and the admin using Socket.IO.
  2. - User Roles:
  3. - Admin: Can view and respond to all users.
  4. - Normal users: Can only chat with the admin.
  5. - Persistent Chat History: Chat messages are stored in MongoDB and retrieved upon reconnecting.
  6. - Notifications: Real-time toast notifications for new messages.

Technologies Used

  • Backend: Node.js, Express.js, Mongoose (MongoDB)
  • Frontend: React.js, CSS
  • WebSocket: Socket.IO for real-time communication
  • Database: MongoDB (Atlas)

System Architecture

Frontend (React.js)

Main components for this feature include:

  1. ChatContainer: Manages the chat interface, handling authentication and message rendering.
  2. ChatList: Displays messages based on whether the user is the sender or receiver.
  3. Socket.IO Client: Initializes the connection and handles real-time updates.
  4. Backend (Node.js, Express.js)
  5. Routes: Handles HTTP requests and interacts with the MongoDB database.
  6. Socket.IO Server: Manages chat rooms and message broadcasting.
  7. Mongoose Models: Defines the schema for storing chat data.

Benefits of Using Socket.IO

  1. Real-Time Communication: Socket.IO allows instant message delivery without delays, creating a seamless chat experience.
  2. Cross-Browser Support: It provides real-time connections that work across different browsers and platforms.
  3. Automatic Reconnection: In case of network disruptions, Socket.IO automatically reconnects the user without losing the chat session.
  4. Broadcasting to Rooms: Messages are broadcasted only to users in specific chat rooms, optimizing the flow of communication.

Backend Code - Mongoose Schema Definition

This schema captures sender and receiver details, message content, profile images, and timestamps.

const mongoose = require('mongoose');

const chatSchema = new mongoose.Schema({
    senderUsername: String,
    receiverUsername: String,
    message: String,
    profileImage: String,
    timestamp: { type: Date, default: Date.now }
});

const Chat = mongoose.model('chat-message', chatSchema);
module.exports = Chat;
Enter fullscreen mode Exit fullscreen mode

Backend Setup (Express.js & Socket.IO)

The backend is configured to handle real-time message exchanges and store chat data in MongoDB.

const express = require("express");
const cors = require("cors");
require("dotenv").config();
const mongoose = require("mongoose");
const http = require("http");
const { Server } = require("socket.io");
const Chat = require("./model/chatModel");

const app = express();
const server = http.createServer(app);

// Middleware
app.use(cors({ origin: process.env.CLIENT_URL, credentials: true }));
app.use(express.json());

// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI, { dbName: 'Giftly-server-db' })
    .then(() => console.log("Giftly DB connected"))
    .catch((err) => console.log(err));

// Socket.IO Setup
const io = new Server(server, { cors: { origin: process.env.CLIENT_URL } });

io.on("connection", (socket) => {
    console.log("User connected");

    socket.on('joinRoom', ({ sender, receiver }) => {
        const room = [sender, receiver].sort().join('-');
        socket.join(room);
        loadMessages(socket, sender, receiver);  // Load previous chats
    });

    socket.on('newMessage', async (msg) => {
        const newMessage = new Chat(msg);
        await newMessage.save();
        io.to([msg.senderUsername, msg.receiverUsername].sort().join('-')).emit('message', msg);
    });

    socket.on("disconnect", () => {
        console.log("User disconnected");
    });
});

server.listen(process.env.PORT, () => {
    console.log(`Giftly server is running on port ${process.env.PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Frontend (React.js) - ChatContainer Component

The ChatContainer component renders the chat interface and manages sending/receiving messages via Socket.IO.

import React, { useContext, useEffect, useState } from "react";
import socketIOClient from "socket.io-client";
import ChatLists from './ChatList';
import './style.css';
import toast from "react-hot-toast";
import { AuthContext } from "../../Provider/AuthProvider";

const socket = socketIOClient(process.env.REACT_APP_SERVER_URL, { autoConnect: false });

const ChatContainer = () => {
    const { user } = useContext(AuthContext);
    const [text, setText] = useState('');
    const [userInfo, setUserInfo] = useState(() => JSON.parse(localStorage.getItem("userInfo")));
    const [receiver, setReceiver] = useState(() => localStorage.getItem("receiver"));
    const [chats, setChats] = useState([]);

    // Fetch previous chats
    const fetchPreviousChats = async (sender, receiver) => {
        const response = await fetch(`${process.env.REACT_APP_SERVER_URL}/chat/getChats?sender=${sender}&receiver=${receiver}`);
        const previousChats = await response.json();
        setChats(previousChats);
    };

    // Initialize Socket
    useEffect(() => {
        socket.connect();
        if (userInfo && receiver) {
            socket.emit("joinRoom", { sender: userInfo.userName, receiver });
        }
        socket.on("message", (msg) => setChats(prev => [...prev, msg]));
        return () => socket.disconnect();
    }, [userInfo, receiver]);

    const handleSend = () => {
        if (text.trim()) {
            const newChat = { senderUsername: userInfo.userName, receiverUsername: receiver, message: text };
            socket.emit("newMessage", newChat);
            setText('');
        }
    };

    return (
        <div className="chat-container">
            <div className="chat-header">Chat with {receiver}</div>
            <ChatLists chats={chats} />
            <div className="chat-input">
                <input type="text" value={text} onChange={(e) => setText(e.target.value)} />
                <button onClick={handleSend}>Send</button>
            </div>
        </div>
    );
};

export default ChatContainer;
Enter fullscreen mode Exit fullscreen mode

Notification Handling in Frontend

Using react-hot-toast for real-time notifications when a new message arrives.

socket.on("notification", (notification) => {
    if (notification.receiver === userInfo.userName) {
        toast.success(notification.message, { duration: 6000, position: 'top-center' });
    }
});
Enter fullscreen mode Exit fullscreen mode

Socket.IO Methods Explained

  1. io.on('connection'): Establishes a WebSocket connection when a user connects.
  2. socket.emit('joinRoom'): Joins a chat room created between the sender and receiver.
  3. socket.on('newMessage'): Receives a new message from the client and stores it in the database.
  4. io.to(room).emit('message'): Broadcasts the message to users in a specific chat room.
  5. socket.on('disconnect'): Handles user disconnection and cleanup.

Future Enhancements

  1. File Sharing: Allow users to send images and files.
  2. Typing Indicators: Display real-time typing status for a better interaction experience.
  3. Push Notifications: Integrate push notifications to alert users when offline.

Setup Instructions

Backend Setup

Clone the repository:

git clone https://github.com/your-repo-url/giftly-chat-app.git
cd giftly-chat-app/server
npm install

Enter fullscreen mode Exit fullscreen mode

Create a .env file in the server directory:

MONGO_URI=mongodb+srv://<username>:<password>@cluster0.mongodb.net/giftly-server-db
PORT=<your-server-port>
CLIENT_URL=http://localhost:5173

Enter fullscreen mode Exit fullscreen mode

*Run the backend server:
*

npm run start
Enter fullscreen mode Exit fullscreen mode

Frontend Setup (React.js)

Navigate to the client folder:

cd ../client
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Create a .env file in the client folder:

REACT_APP_SERVER_URL=http://localhost:<your-server-port>
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

The Giftly chat feature is a scalable real-time chat solution, enabling instant communication between users and admins. Socket.IO simplifies the WebSocket management, ensuring smooth and efficient message exchanges with real-time notifications. Future enhancements will bring more interaction to users, including file sharing, typing indicators, and offline notifications.

Happy coding! 🚀

Top comments (0)