Skip to main content

Real-Time Transports & RPC

Plain HTTP request/response works when the client always initiates. Several patterns exist for when that assumption breaks - the server needs to push data, both sides need to stream continuously, or a client needs to call a server function directly rather than fetch a resource. This page compares the options and when to reach for each; see the WebSockets page for the Upgrade handshake and frame mechanics, which are not repeated here.

The four options

  • Long polling - the client sends a normal HTTP request, but the server holds it open without responding until it has new data (or a timeout elapses), then responds and the client immediately reopens a new request. It is plain HTTP underneath - no new protocol, works through any proxy - but every "push" costs a full request/response round trip and a fresh connection.
  • Server-Sent Events (SSE) - the client opens one long-lived HTTP connection (Content-Type: text/event-stream) and the server writes a stream of text events down it indefinitely. One-way, server-to-client only; the browser's built-in EventSource API auto-reconnects on drop and even replays missed events via a Last-Event-ID header.
  • WebSocket - a persistent, full-duplex connection: either side sends at will, independent of the other. See the dedicated WebSockets page for how the connection is established and framed.
  • gRPC - an RPC framework built on HTTP/2. Instead of "fetch a resource," the client calls a named method with typed (protobuf) arguments and gets a typed response. Built on HTTP/2 streams, it supports four call shapes: unary (one request, one response), server streaming, client streaming, and full bidirectional streaming - all multiplexed over one connection.

Comparing them

DirectionTransportMessage shape
Long pollingServer to client (via repeated requests)Plain HTTP/1.1, one request per updateWhatever the app defines (usually JSON)
SSEServer to client onlyOne long-lived HTTP connectionText event stream, auto-reconnect built in
WebSocketFull duplexUpgraded persistent TCP connectionBinary or text frames, app-defined
gRPCUnary, or streaming in either/both directionsHTTP/2 multiplexed streamsTyped protobuf messages, RPC methods

When to pick each

Decision

Choose long polling. It is the least efficient option, but it is just HTTP, so nothing in the path needs to understand anything new.

Pick this when: you need occasional server pushes and must work through infrastructure that only understands plain request/response (old proxies, restrictive corporate networks)
Decision

Choose SSE. It is simpler to operate than WebSocket (still plain HTTP, no special proxy/load-balancer handling), and the browser gives you reconnect-and-resume for free.

Pick this when: the server needs to push a stream of updates - notifications, live scores, log tails - and the client never needs to send anything back over the same channel
Decision

Choose WebSocket. It is the only one of these four with true, low-overhead full duplex.

Pick this when: both sides need to send messages at any time, independent of each other - chat, multiplayer, collaborative editing, live trading
Decision

Choose gRPC. It fits service-to-service (and increasingly client-to-service) calls where "fetch a resource" is the wrong mental model and "invoke this method" is the right one.

Pick this when: you are calling a well-defined function on another service, want strong typing and code generation across languages, or need to stream in either direction as part of a structured RPC

Why gRPC leans on HTTP/2

gRPC could not offer streaming and multiplexing this cheaply on HTTP/1.1: one request per connection would mean one gRPC stream per TCP connection, with all the head-of-line blocking and connection overhead that HTTP/2 was built to remove (see the Modern HTTP page). HTTP/2's independent, multiplexed streams let gRPC open many concurrent calls - including long-running streaming calls - over a single connection, each call mapped to one HTTP/2 stream.

info

None of these four transports "replace" HTTP - long polling and SSE run entirely inside it, WebSocket starts as an HTTP request before upgrading, and gRPC is layered directly on HTTP/2. The choice is about which push/duplex model fits the problem, not about abandoning HTTP.