Kritim Yantra
Aug 03, 2025
📱 “Can you build a chat app?”
That’s a question you’ve probably heard—or Googled—if you’re diving into full-stack development.
Here’s the catch: Chat apps look easy on the outside, but under the hood, they need to be real-time, secure, scalable, and efficiently structured in the database.
Whether you're building a Slack-style team chat, a social DMs feature, or a customer support inbox, you’ll need a solid plan.
In this post, you’ll learn how to design a real-time chat system using:
✅ MongoDB for storing messages and user metadata
✅ Express.js for handling the API
✅ Socket.IO for real-time messaging
We’ll walk through schema design, API structure, real-time event handling, and basic authentication—step by step.
⚠️ This is NOT just theory. Everything here is designed to help you ship faster and avoid common architecture mistakes.
{
_id: ObjectId,
username: "john_doe",
email: "john@example.com",
password: "<hashed>",
avatarUrl: "/img/1.jpg",
status: "online" | "offline",
lastSeen: ISODate
}
{
_id: ObjectId,
senderId: ObjectId,
chatId: ObjectId,
text: "Hey, how are you?",
readBy: [ObjectId], // users who have read this message
createdAt: ISODate
}
{
_id: ObjectId,
name: "Team Alpha", // for group chats
isGroup: true,
participants: [ObjectId], // user IDs
lastMessage: ObjectId, // reference to last message
createdAt: ISODate
}
💡 Pro Tip: Store
lastMessage
inside thechat
document for performance, especially for chat previews.
Method | Route | Description |
---|---|---|
POST | /api/register |
Register a new user |
POST | /api/login |
Authenticate + JWT |
GET | /api/users |
List users |
Method | Route | Description |
---|---|---|
POST | /api/chats |
Create 1-on-1 or group chat |
GET | /api/chats/:userId |
Get user’s chats |
POST | /api/chats/:id/message |
Send a message to a chat |
GET | /api/chats/:id |
Get message history |
const http = require('http');
const socketIO = require('socket.io');
const server = http.createServer(app);
const io = socketIO(server, {
cors: { origin: "*" }
});
io.on('connection', socket => {
console.log("User connected:", socket.id);
socket.on('joinChat', (chatId) => {
socket.join(chatId);
});
socket.on('sendMessage', async ({ chatId, senderId, text }) => {
const message = await Message.create({ chatId, senderId, text });
io.to(chatId).emit('receiveMessage', message);
});
socket.on('disconnect', () => {
console.log("User disconnected");
});
});
🔄 Clients join chat rooms using
join(chatId)
so messages broadcast only to relevant users.
const jwt = require('jsonwebtoken');
const login = async (req, res) => {
const user = await User.findOne({ email: req.body.email });
const isMatch = await bcrypt.compare(req.body.password, user.password);
if (!isMatch) return res.status(401).send("Invalid credentials");
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: "1d" });
res.json({ token });
};
const auth = (req, res, next) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return res.status(401).send("No token");
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch {
res.status(401).send("Invalid token");
}
};
const onlineUsers = new Map();
io.on('connection', socket => {
const userId = socket.handshake.query.userId;
onlineUsers.set(userId, socket.id);
socket.on('disconnect', () => {
onlineUsers.delete(userId);
});
});
io.emit('onlineUsers', Array.from(onlineUsers.keys()));
📌 You can use this to show green dots beside user avatars, or trigger "user is typing" indicators.
socket.on('typing', ({ chatId, userId }) => {
socket.to(chatId).emit('typing', userId);
});
socket.on('markAsRead', async ({ messageId, userId }) => {
await Message.updateOne({ _id: messageId }, { $addToSet: { readBy: userId } });
});
Designing a chat system isn’t just about throwing messages into a database. You need clear structure, secure auth, and real-time interactions that scale.
Let’s recap:
✅ MongoDB handles flexible user/message data
✅ Express.js provides a clean API
✅ Socket.IO keeps everything real-time
✅ JWT keeps users secure
✅ The schema supports 1-on-1, groups, history, and presence
🎯 Next Step: Try building this system with basic UI using React or Vue, and hook into Socket.IO for real-time messages.
A: Socket.IO is built on top of WebSockets and adds features like reconnection, rooms, and fallback—perfect for beginners.
A: Use MongoDB. For massive scale, archive old messages to a cold-storage collection.
A: Use NGINX + PM2 for Express, and Redis Adapter for scaling Socket.IO across multiple instances.
Would you add voice messages? File sharing? Emoji reactions?
Drop your ideas in the comments—let’s brainstorm the perfect chat experience. 👇
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google