STORM

SOFTWARE  DEVELOPER

SYSTEM  INITIALIZING

[ VIEW PORTFOLIO ]

01
Back to Blog
Networking

WebSockets vs Polling: Building Real-Time Features the Right Way

May 2, 2026 6 min read

When you open a chat app and a message appears instantly without refreshing the page, something interesting is happening under the hood. The browser isn't sitting there asking the server 'anything new?' over and over. A persistent connection is open, and the server pushes data the moment it's ready.

Understanding how real-time communication actually works - and the tradeoffs between approaches - will help you make better architecture decisions and avoid over-engineering features that don't need it.


The Problem: HTTP Is Request-Response

Standard HTTP is inherently one-directional: the client asks, the server answers, and the connection closes. This works perfectly for loading a webpage or fetching a list of users. But for real-time data, you need the server to be able to send data without the client asking first.

Three main approaches have emerged to solve this, each with different tradeoffs:


Approach 1 - Short Polling

The simplest solution: just keep asking. The client sends a request every N seconds to check if anything has changed.

js
// Client keeps asking every 3 seconds
setInterval(async () => {
  const res = await fetch('/api/messages/latest')
  const data = await res.json()
  updateUI(data)
}, 3000)

When it works

Data that updates infrequently - a dashboard that refreshes every minute, a job status checker, a notification badge that doesn't need to be real-time.

The problem

If there's nothing new, you've made a wasted HTTP request. At 1 request every 3 seconds, that's 20 requests per minute, 1200 per hour - per user. At any real scale, this hammers your server with mostly-empty responses.


Approach 2 - Long Polling

A smarter version: the client sends a request, but the server holds it open until it has something to send (or a timeout is reached). The client immediately sends another request after receiving a response.

js
// Server holds the request until data is ready
async function pollForUpdate() {
  const res = await fetch('/api/messages/wait') // server waits up to 30s
  const data = await res.json()
  updateUI(data)
  pollForUpdate() // immediately reconnect
}
pollForUpdate()

This reduces wasted requests significantly - the server only responds when there's actual data. But you're still creating a new HTTP connection every time a response is received, which adds overhead.


Approach 3 - Server-Sent Events (SSE)

SSE establishes a persistent one-way connection from server to client over a standard HTTP response. The connection stays open, and the server can push data any time.

js
// Client
const events = new EventSource('/api/stream')
events.onmessage = (e) => updateUI(JSON.parse(e.data))

// Server (Express)
app.get('/api/stream', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')

  const send = (data) => res.write(`data: ${JSON.stringify(data)}\n\n`)

  const interval = setInterval(() => send(getLiveData()), 1000)
  req.on('close', () => clearInterval(interval))
})

SSE is underused. It's simpler than WebSockets, works over standard HTTP/2, handles reconnection automatically, and is perfect for anything that only needs server-to-client data flow: live feeds, notifications, progress updates.


Approach 4 - WebSockets

WebSockets open a full-duplex channel over a single TCP connection. Once established, both client and server can send messages at any time without the overhead of repeated HTTP handshakes.

js
// Client
const ws = new WebSocket('wss://api.example.com/ws')

ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', room: 'general' }))
ws.onmessage = (e) => updateUI(JSON.parse(e.data))
ws.onclose = () => scheduleReconnect()

// Server (Node.js ws library)
wss.on('connection', (socket) => {
  socket.on('message', (raw) => {
    const msg = JSON.parse(raw)
    broadcast(msg) // send to all connected clients
  })
})

WebSockets are the right tool when you need bidirectional, low-latency messaging: chat, multiplayer games, collaborative editing, live cursors, or trading platforms.


Which One Should You Use?

ApproachDirectionBest ForAvoid When
Short PollingClient → ServerInfrequent updates, simple setupData changes frequently
Long PollingServer → ClientModerate frequency, HTTP-only envHigh volume, many concurrent users
SSEServer → Client onlyNotifications, live feeds, progressYou need client-to-server messages too
WebSocketsBidirectionalChat, games, collaborative toolsSimple one-way data streams

A Common Mistake

Reaching for WebSockets for everything is one of the most frequent over-engineering mistakes in frontend development. If you're building a live notification bell or a streaming dashboard where only the server sends data, SSE is simpler, cheaper, and works better with HTTP/2 multiplexing - no WebSocket server infrastructure required.

Save WebSockets for the actual bidirectional use cases. Your ops team will thank you.


Quick Decision Guide

  • Updates less than once per minute → short polling is fine
  • Server pushes data, client only reads → use SSE
  • Both sides send messages in real-time → use WebSockets
  • Need to work in restrictive network environments → SSE or long-polling (both over HTTP)
Back to Blog
PLAYING