Course:Node.js & Express/
Lesson

You learned that native WebSockets give you a persistent connection with four events. That's the foundation, but building a real app on top of raw WebSockets means reinventing a lot of wheels: reconnection logic, room management, fallback handling. Socket.io builds those wheels for you.

What native WebSockets are missing

The browser WebSocket APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. is low-level by design. It sends strings and fires events. That's it. Everything else, rooms, reconnection, reliable delivery, fallbacks for corporate firewalls, you have to write yourself.

Socket.io adds all of that out of the box:

FeatureNative WebSocketSocket.io
Automatic reconnectionNo, you build itYes, built in
Fallback if WebSocket is blockedNoYes (long polling)
Rooms / channelsNo, you track manuallyYes, built in
Send to all except senderNo, loop through clientsYes (socket.broadcast)
Message delivery confirmationNoYes (acknowledgments)
Middleware supportNoYes
NamespacesNoYes
Good to know
Some corporate networks and proxies block WebSocket connections entirely. Socket.io silently falls back to HTTP long polling in those cases, so your app keeps working. Once the environment allows WebSocket, it upgrades automatically, no code change required.
02

Setting up Socket.io

Socket.io has two parts: a server package and a client package. Both must be installed, and their major versions should match.

Installation

# Server
npm install socket.io

# Client (or use the CDN the server exposes automatically)
npm install socket.io-client

Server setup with Express

Socket.io attaches to a Node.js HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. server, not directly to Express. You create the HTTP server from Express, then wrap it:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST']
  }
});

// Every new client connection triggers this callback
io.on('connection', (socket) => {
  console.log('A user connected:', socket.id);

  socket.on('disconnect', () => {
    console.log('User disconnected:', socket.id);
  });
});

server.listen(3001, () => {
  console.log('Server running on :3001');
});

Each connected client gets a unique socket.id. The io object represents the entire server; socket represents one specific client.

Client setup

import { io } from 'socket.io-client';

const socket = io('http://localhost:3001');

socket.on('connect', () => {
  console.log('Connected! My ID:', socket.id);
});

socket.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
});
Good to know
If your client is served from the same origin as your Socket.io server, you can omit the URL entirely: const socket = io(). Socket.io resolves the server automatically from the current page's origin.
03

Sending and receiving events

Socket.io is entirely event-based. You emit named events with data, and listen for named events on the other end. The pattern is identical on both client and server.

// CLIENT: send a chat message
socket.emit('chat-message', {
  text: 'Hello everyone!',
  room: 'general'
});

// SERVER: receive it and broadcast to the room
io.on('connection', (socket) => {
  socket.on('chat-message', (data) => {
    io.to(data.room).emit('chat-message', {
      user: socket.username,
      text: data.text,
      timestamp: new Date().toISOString()
    });
  });
});

// CLIENT: listen for incoming messages
socket.on('chat-message', (data) => {
  appendMessage(data.user, data.text);
});

The different ways to send from the server

This is where people get confused first. There are several targets when emitting from the server side:

MethodWho receives it?
socket.emit('event', data)Only this specific client
socket.broadcast.emit('event', data)Every client except this one
io.emit('event', data)Every connected client, including this one
io.to('room').emit('event', data)Everyone in a specific room
socket.to('room').emit('event', data)Everyone in the room except this client
04

Rooms

Rooms are named channels that sockets can joinWhat is join?A SQL operation that combines rows from two or more tables based on a shared column, letting you query related data in one request. and leave freely. They're the core tool for organizing users, chat channels, game lobbies, document sessions, notification groups.

io.on('connection', (socket) => {
  // Client requests to join a room
  socket.on('join-room', (roomName) => {
    socket.join(roomName);

    // Notify everyone else already in the room
    socket.to(roomName).emit('user-joined', {
      user: socket.username,
      room: roomName
    });
  });

  // Client leaves a room
  socket.on('leave-room', (roomName) => {
    socket.leave(roomName);
    socket.to(roomName).emit('user-left', {
      user: socket.username
    });
  });
});

A socket can be in multiple rooms simultaneously. Every socket is also automatically in a private room identified by its own socket.id, useful for direct messages between users.

Good to know
You can inspect a socket's current rooms with socket.rooms (a JavaScript Set). To count how many clients are in a room: io.sockets.adapter.rooms.get('general')?.size ?? 0.
05

Quick reference

OperationCode
Send to one clientsocket.emit('event', data)
Send to everyone except sendersocket.broadcast.emit('event', data)
Send to everyoneio.emit('event', data)
Join a roomsocket.join('room-name')
Leave a roomsocket.leave('room-name')
Send to room (excluding sender)socket.to('room').emit('event', data)
Send to room (including sender)io.to('room').emit('event', data)
Get socket IDsocket.id
Disconnect a clientsocket.disconnect()
javascript
// Complete chat server with Socket.io

// ============ SERVER (server.js) ============
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, { cors: { origin: '*' } });

const users = new Map(); // socket.id → username

io.on('connection', (socket) => {
  console.log('🟢 New connection:', socket.id);

  // User sets their username
  socket.on('set-username', (username) => {
    socket.username = username;
    users.set(socket.id, username);
    socket.broadcast.emit('user-joined', { user: username });
    io.emit('user-count', users.size);
  });

  // Join a room
  socket.on('join-room', (room) => {
    socket.join(room);
    socket.currentRoom = room;
    socket.to(room).emit('notification', {
      text: `${socket.username} joined ${room}`
    });
  });

  // Send a chat message
  socket.on('chat-message', ({ text, room }) => {
    const message = {
      id: Date.now(),
      user: socket.username,
      text,
      timestamp: new Date().toISOString()
    };
    io.to(room).emit('chat-message', message);
  });

  // Typing indicator
  socket.on('typing', ({ room, isTyping }) => {
    socket.to(room).emit('user-typing', {
      user: socket.username,
      isTyping
    });
  });

  socket.on('disconnect', () => {
    users.delete(socket.id);
    socket.broadcast.emit('user-left', { user: socket.username });
    io.emit('user-count', users.size);
    console.log('🔴 Disconnected:', socket.id);
  });
});

server.listen(3001, () => console.log('🚀 Server on :3001'));


// ============ CLIENT (chat.js) ============
import { io } from 'socket.io-client';

const socket = io('http://localhost:3001');

socket.on('connect', () => {
  socket.emit('set-username', 'Alice');
  socket.emit('join-room', 'general');
});

socket.on('chat-message', (message) => {
  console.log(`${message.user}: ${message.text}`);
});

socket.on('user-joined', ({ user }) => console.log(`${user} joined`));
socket.on('user-left', ({ user }) => console.log(`${user} left`));

socket.on('user-typing', ({ user, isTyping }) => {
  if (isTyping) console.log(`${user} is typing...`);
});

function sendMessage(text) {
  socket.emit('chat-message', { text, room: 'general' });
}

let typingTimeout;
function onKeyPress() {
  socket.emit('typing', { room: 'general', isTyping: true });
  clearTimeout(typingTimeout);
  typingTimeout = setTimeout(() => {
    socket.emit('typing', { room: 'general', isTyping: false });
  }, 1000);
}