Building a Raw WebSocket Server
🛠️ Building a Raw WebSocket Server
ws library handles the protocol details and hands you a clean event-based API. This chapter builds a real server with it.
Installing ws
A Minimal Server
One Server, Many Sockets
wss is the server itself; socket (often named ws in examples) is created fresh for every individual client that connects — the "connection" handler runs once per client, not once total.
No Manual Framing
The library already did the handshake and frame parsing from Chapter 2 before this handler ever runs — by the time you have a socket, the WebSocket protocol is fully established.
The message Event
Every message a client sends fires this event on that client's own socket object — data is a Buffer by default, so it needs .toString() before treating it as text or parsing it as JSON.
Sending Data Back
The close and error Events
👥 Tracking Multiple Clients
A single socket only knows about itself — broadcasting to everyone means keeping your own collection of connected sockets:
This is deliberately minimal — real rooms, presence tracking, and targeted broadcasting (not "everyone") are the focus of Chapter 6.
readyState
Every socket has a readyState: CONNECTING, OPEN, CLOSING, or CLOSED. Checking for OPEN before sending avoids errors from trying to write to a socket that's already on its way out.
Sharing an HTTP Port
A WebSocketServer can attach to an existing http.Server (e.g. an Express app's server) instead of listening on its own port — common in real deployments where regular HTTP and WebSocket traffic share one port.
Raw ws Today vs Socket.IO (Next Chapter)
What ws Gives You
A thin, direct wrapper around the protocol — connection/message/close events, and nothing else. Rooms, reconnection, acknowledgements: all hand-rolled.
What a Library Like Socket.IO Adds
Automatic reconnection, a room/namespace system, message acknowledgements, and fallback transports — at the cost of a heavier abstraction and its own wire protocol on top of WebSockets.
💻 Coding Challenges
Challenge 1: Build an Echo Server
Using ws, write a complete server that listens on port 8080 and sends every message it receives straight back to the same client that sent it (not broadcast to everyone).
Goal: Practice the basic connection/message/send flow before adding multi-client broadcasting.
Challenge 2: Track Connected Clients With Logging
Extend the echo server to maintain a Set of connected clients, logging the current client count every time a client connects or disconnects.
Goal: Practice managing per-connection state that outlives a single message.
Challenge 3: Defend Against a Bad readyState
Write a broadcast(clients, message) helper function that safely sends message to every socket in a Set, skipping any socket that isn't currently OPEN.
Goal: Practice the defensive readyState check this chapter's tip box warns about.
socket.send() doesn't automatically wait for the connection to be ready, and it doesn't throw a helpful error if the socket has already started closing — depending on timing, a careless call can silently fail or throw an uncaught exception. This bites most often in the broadcast pattern above: by the time a loop reaches a given client, that client may have disconnected moments earlier. Always check readyState === WebSocket.OPEN immediately before calling send() on any socket you didn't just create yourself in the current handler.
🎯 What's Next
With a working raw server in hand, the next chapter steps back to compare this approach against a higher-level library: Socket.IO vs Raw WebSockets — reviewing the Socket.IO coverage from the Node.js and Express courses, and figuring out when raw ws is actually the better choice.