This article covers what I learned or maybe didn't really learn.
The Problem With Traditional HTTP
Most web applications use HTTP.
The flow looks like this:
Client → Request → Server
Client ← Response ← Server
Once the server sends the response, the connection is closed.
This works perfectly for:
- Authentication
- CRUD operations
- Fetching data
- Form submissions
But what happens when the server needs to send information without being asked?
For example:
- A new task is assigned
- Someone comments on a task
- A project status changes
- A teammate updates a board
With traditional HTTP, the browser would need to keep asking:
"Anything new?"
"Anything new?"
"Anything new?"
This technique is called polling, and it's inefficient.
That's where Socket.io comes in.
What Socket.io Actually Does
Socket.io creates a persistent connection between the client and server.
Instead of repeatedly opening and closing connections, the connection stays alive.
Now communication becomes two-way:
Client ↔ Server
The client can send data whenever it wants.
The server can also send data whenever it wants.
This is what makes real-time applications possible.
Why Express Alone Isn't Enough
One thing that confused me initially was why Socket.io couldn't simply be attached directly to my Express app.
The answer lies in how Express works.
When you write:
app.listen(5000);
Express creates the HTTP server internally.
You don't have direct access to it.
Socket.io, however, needs access to the raw HTTP server.
So instead of:
app.listen(PORT);
The flow becomes:
const httpServer = createServer(app);
Then Socket.io attaches to that server:
const io = new Server(httpServer);
Finally:
httpServer.listen(PORT);
This architecture allows Socket.io and Express to share the same server.
Understanding Events
Socket.io is event-driven.
Everything revolves around two methods:
socket.emit()
and
socket.on()
Think of them as:
emit = send
on = listen
For example:
Client:
socket.emit("join-project", projectId);
Server:
socket.on("join-project", (projectId) => {
// handle join
});
The client sends.
The server listens.
Simple.
The Concept That Made Everything Click: Rooms
The feature that finally made Socket.io make sense to me was rooms.
Imagine a walkie-talkie.
People on Channel 1 hear messages sent to Channel 1.
People on Channel 2 hear messages sent to Channel 2.
Socket.io rooms work similarly.
In my project management application:
Project A = Room A
Project B = Room B
Project C = Room C
When a user opens a project:
socket.join(projectId);
Now they're listening to updates from that project.
When someone creates a task:
io.to(projectId).emit("task-updated");
Only users in that project receive the event.
Everyone else remains unaffected.
This prevents unnecessary updates and scales much better.
Where Real-Time Events Should Be Triggered
One thing I initially misunderstood was where to emit events.
My first instinct was to put everything inside the Socket.io connection handler.
But that's not where the business logic happens.
The actual changes occur inside route handlers.
For example:
POST /tasks
creates a task.
After successfully creating the task:
- Save task to database
- Get Socket.io instance
- Find project room
- Emit update
Conceptually:
Create Task
↓
Database Success
↓
Emit Event
↓
Clients Update UI
This sequence is important.
You never want to emit an event before the database operation succeeds.
Making Socket.io Available Everywhere
Another clever pattern I learned was storing the Socket.io instance on the Express app.
app.set("io", io);
Then inside any route:
const io = req.app.get("io");
This prevents:
- Circular imports
- Global variables
- Messy architecture
Every route can access Socket.io cleanly.
Real-World Example: Task Creation
Imagine a user creates a task.
Without Socket.io:
User A creates task
↓
Database updated
↓
User B sees nothing
↓
Must refresh page
With Socket.io:
User A creates task
↓
Database updated
↓
Server emits event
↓
User B instantly sees update
No refresh required.
That's the power of real-time communication.
Thanks for reading.
Top comments (0)