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