I am a 7th semester BSCS student from Kasur, Pakistan.
No internship. No mentor. No team. No funding.
Just me, a laptop, and a stubborn refusal to only do assignments.
Six months ago I decided to build something real. Not a todo app. Not a weather app. Something that actual people could open and use right now.
Today, vconnect.fun is live. Real people are using it. And I want to tell you exactly how I built it, what broke, and what I would do differently.
What is VConnect?
VConnect is a real-time web messaging platform. Think WhatsApp Web but free, open, and built by one student from a small city in Punjab.
Features:
- Instant messaging via WebSockets
- Typing indicators and read receipts
- Voice notes and file sharing
- Glassmorphic UI with dark mode
- Online/offline status
- User search
Stack: MongoDB, Express.js, React.js, Node.js , full MERN. Deployed on AWS EC2 with Docker and GitHub Actions for CI/CD.
Why Did I Build This?
Honestly? I wanted proof.
Proof that I could build something production ready. Proof that a student from Kasur could ship something that looked and worked like a funded startup product.
Most of my classmates were building assignment projects that nobody ever opens again. I wanted to build something with a real domain, real users, and real infrastructure.
So I started.
The Architecture
The core of VConnect is WebSocket communication handled by Socket.io.
Here is the basic idea of how real-time messaging works in VConnect:
javascript
// Server side when a message is sent
socket.on("sendMessage", async ({ senderId, receiverId, message }) => {
const newMessage = await Message.create({
senderId,
receiverId,
message,
});
// Emit to the receiver if they are online
const receiverSocket = getReceiverSocketId(receiverId);
if (receiverSocket) {
io.to(receiverSocket).emit("newMessage", newMessage);
}
});
The key insight here is getReceiverSocketId. I maintain a map of userId to socketId so I can target messages to specific users instead of broadcasting to everyone.
javascript
// Tracking who is online
const userSocketMap = {};
io.on("connection", (socket) => {
const userId = socket.handshake.query.userId;
if (userId) userSocketMap[userId] = socket.id;
// Broadcast updated online users to everyone
io.emit("getOnlineUsers", Object.keys(userSocketMap));
socket.on("disconnect", () => {
delete userSocketMap[userId];
io.emit("getOnlineUsers", Object.keys(userSocketMap));
});
});
This is how the green dot works. When a user connects, their ID is stored. When they disconnect, it is removed. Everyone gets notified in real time.
The Hardest Part : Read Receipts
Typing indicators were easy. Read receipts nearly broke me.
The challenge: how do you know a message was "seen"? You need to track when the receiver opens a conversation, not just when they receive a message.
My solution was to emit a "markAsRead" event whenever a user opens a chat window, then update all unread messages for that conversation in the database and emit back a "messagesRead" event to update the sender's UI.
It sounds simple. It took four days and three complete rewrites to get right.
The lesson: real-time state synchronization is genuinely hard. Every edge case you can imagine what if the user is offline when you send? what if they open the chat before the message arrives? you have to handle all of it.
Deployment : AWS EC2 with Docker
This was where I went from "student project" to "production app."
I containerized the backend using Docker so the environment is identical everywhere. Then I set up GitHub Actions to automatically deploy whenever I push to the main branch.
The flow looks like this:
Push to GitHub → GitHub Actions triggers → SSH into EC2 → Pull new image → Restart container
yaml
# Simplified GitHub Actions workflow
- name: Deploy to EC2
run: |
ssh ec2-user@my-ec2-ip '
docker pull my-image:latest &&
docker stop vconnect || true &&
docker run -d --name vconnect my-image:latest
'
Setting this up the first time took a full weekend. Now every deployment takes 90 seconds automatically.
What I Would Do Differently
1. Add a guest/demo mode from day one.
The biggest barrier to new users is registration. If someone could try VConnect instantly without signing up, more people would actually experience it.
2. Write tests earlier.
I have almost no test coverage. Every new feature is a gamble. I am paying for this now.
3. Document the architecture as I built it.
I had to reverse-engineer my own code to write this article. That is embarrassing. Write docs while the context is fresh.
The Real Lesson
Building VConnect taught me more than two semesters of university combined.
You learn WebSockets by breaking WebSocket connections at 2 AM and figuring out why. You learn Docker by spending a Saturday on a port conflict. You learn AWS by accidentally exposing your EC2 instance to the entire internet and then panicking.
The classroom teaches you concepts. Building teaches you reality.
If you are a student reading this, stop waiting until you feel ready. You will never feel ready. Open a new project folder, pick something you wish existed, and start.
The only way to learn to build is to build.
Try VConnect: vconnect.fun
GitHub: github.com/muhammadusman2228
If you found this useful, follow me here on Dev.to . I am documenting everything I build and learn as a CS student in Pakistan. More articles coming on CoCode (collaborative code editor with Docker sandboxing) and deploying MERN apps on AWS from scratch.
Drop a comment with what you are building. I would love to see it.

Top comments (0)