Building the Backend of Chatty - A Real-Time MERN WebSocket Chat App

posted 3 min read

Chatty is a real-time chat application built using the MERN stack and WebSockets to enable seamless and instant communication. A strong backend is essential for ensuring real-time messaging, authentication, and database management, creating a reliable and scalable chat experience.

In this blog, I’ll cover:

  • ✅ Setting up WebSockets with socket.io for real-time updates
  • ✅ JWT authentication for secure user sessions
  • ✅ Storing messagesvefficiently in MongoDB

This is only an overview of the code; for the complete implementation, check out the GitHub repo linked at the end!

1. Setting Up the Backend

The backend is built using Node.js & Express.js. To get started, install the necessary dependencies:

npm init -y
npm install express mongoose dotenv socket.io jsonwebtoken bcryptjs cors

Initializing the server:

import express from "express"; import http from "http"; import { Server } from "socket.io"; import mongoose from "mongoose"; import dotenv from "dotenv"; import cors from "cors"; import authRoutes from "./routes/authRoutes.js"; import chatRoutes from "./routes/chatRoutes.js"; dotenv.config(); const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { origin: "*" } }); app.use(express.json()); app.use(cors()); app.use("/api/auth", authRoutes); app.use("/api/chat", chatRoutes); server.listen(5000, () => console.log("Server running on port 5000"));

2. Implementing WebSocket for Real-Time Messaging

WebSockets allow for bi-directional real-time communication, ensuring that messages are instantly delivered to all connected users.

`io.on("connection", (socket) => {

console.log("User connected:", socket.id);
socket.on("sendMessage", (messageData) => {
    io.emit("receiveMessage", messageData); // Broadcast message to all clients
});
socket.on("disconnect", () => {
    console.log("User disconnected");
});

}); `

3. Implementing JWT Authentication

To ensure user security, we implement JWT-based authentication for login and protected routes.

User Model (models/User.js)

`import mongoose from "mongoose";
import bcrypt from "bcryptjs";
const UserSchema = new mongoose.Schema({

username: { type: String, required: true, unique: true },
password: { type: String, required: true }

});
UserSchema.pre("save", async function (next) {

if (!this.isModified("password")) return next();
this.password = await bcrypt.hash(this.password, 10);
next();

});
export default mongoose.model("User", UserSchema);
`

Auth Controllers (controllers/authController.js)

`import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";
import User from "../models/User.js";
dotenv.config();
export const registerUser = async (req, res) => {

try {
    const { username, password } = req.body;
    const newUser = new User({ username, password });
    await newUser.save();
    res.status(201).json({ message: "User registered successfully" });
} catch (error) {
    res.status(500).json({ error: error.message });
}

};
export const loginUser = async (req, res) => {

try {
    const { username, password } = req.body;
    const user = await User.findOne({ username });
    if (!user || !await bcrypt.compare(password, user.password)) {
        return res.status(401).json({ message: "Invalid credentials" });
    }
    const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });
    res.json({ token, user });
} catch (error) {
    res.status(500).json({ error: error.message });
}

}; `

4. Storing Messages in MongoDB

Chat messages need to be stored persistently so users can retrieve past conversations.

Message Model (models/Message.js)

`import mongoose from "mongoose";
const MessageSchema = new mongoose.Schema({

sender: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
content: { type: String, required: true },
timestamp: { type: Date, default: Date.now }

});
export default mongoose.model("Message", MessageSchema);`

Message Controller (controllers/chatController.js)

`import Message from "../models/Message.js";
export const saveMessage = async (req, res) => {

try {
    const { sender, content } = req.body;
    const message = new Message({ sender, content });
    await message.save();
    res.status(201).json({ message: "Message saved successfully" });
} catch (error) {
    res.status(500).json({ error: error.message });
}`

Final Thoughts & Contribution

That’s how Chatty’s backend is structured! We covered:

  • Real-time WebSockets for instant messaging
  • JWT authentication to enhance security
  • Efficient message storage in MongoDB

This is only an overview of the code; for the complete implementation, check out the GitHub repo! The full repository contains additional optimizations, error handling, and best practices to ensure the app runs smoothly at scale.

Chatty Repo

0 votes

More Posts

Introduction to Chatty - A Real-Time MERN WebSocket Chat App

Mohammad Yasir - Feb 20, 2025

GroqStreamChain: Revolutionizing Real-Time AI Chat with WebSocket and Groq

Promila Ghosh - Jul 18, 2025

Building an AI-Powered Expense Tracker with the MERN Stack

Adithyan - Jan 28

What I Learned While Building My First MERN Stack Project

Anchal Jethliya - Jan 15

Why MERN Stack is Still a Top Choice for Web Development in 2025

Mohammad Yasir - Mar 16, 2025
chevron_left