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.