The Streaming API pattern is used when clients need continuous, real-time updates without polling. Instead of repeatedly calling GET /resource for updates, clients maintain a persistent connection over which the server can push:
- Events
- Messages
- State deltas
- Notifications
- Data streams
This pattern includes:
- Server-Sent Events (SSE) - one-way stream from server to client
- WebSockets - bi-directional message stream
- gRPC Streaming - bi-directional, strongly typed message streams (primarily non-HTTP/REST)
12.1. Overview
Streaming APIs:
- Maintain long-lived connections
- Push data as it becomes available
- Reduce latency for UI and systems integrations
- Remove polling overhead
- Are ideal for real-time dashboards, collaboration tools, event feeds, chat systems, sensor streams, and dynamic UIs
They differ by communication model:
| Streaming Style | Direction | Transport | Typical Use Cases |
|---|---|---|---|
| SSE | Server Client | HTTP (text/event-stream) | Event updates, dashboards, logs |
| WebSockets | Bidirectional | WS/WSS | Chat, real-time collaboration, IoT control |
| gRPC Streaming | Bidirectional | HTTP/2 | Typed, high-performance enterprise or microservice communication |
12.2. When to Use the Streaming Pattern
Use streaming when:
- You need sub-second latency for updates
- Polling creates unnecessary load
- Many clients subscribe to frequent small updates
- Data naturally forms a stream (prices, metrics, logs, events)
- You are building interactive or collaborative experiences
Use Streaming APIs when you need:
- Continuous, real-time updates
- Minimal latency
- Push-based notifications
- Bidirectional communication (WebSockets)
- High-frequency event feeds (SSE)
Examples:
- Price tickers
- Live dashboards
- Real-time order/tracking updates
- Device/sensor telemetry
- Chat and messaging
- Pair programming or co-browsing UIs
12.3. When NOT to Use This Pattern
Avoid streaming when:
- Clients do not benefit from real-time updates
- Firewalls or infrastructure do not support long-lived connections
- Message volume is too low to justify streaming
- The backend cannot support persistent connections at scale
- Strong delivery guarantees are required (consider message queues instead)
- The consumer cannot maintain state needed for streaming
Also avoid streaming for:
- Event-triggered notifications that don’t require constant updates (use Webhooks)
- Back-office workflows that don’t need immediacy
- Low-frequency updates
- Highly sensitive updates that require guaranteed delivery
- Long-lived connections that backend infrastructure cannot support at scale
12.4. What the Pattern Looks Like
Server-Sent Events (SSE)
- One-way streaming from server to client
- Uses HTTP with
text/event-stream - Auto-reconnect built into browsers
Example Request:
GET /events/orders
Accept: text/event-stream
Example Server Push:
event: order.shipped
data: {"orderId":"or_12345","when":"2024-05-01T10:00:00Z"}
WebSockets
- Bi-directional messaging
- Useful for chat, game UIs, collaborative apps
- Client upgrades from HTTP to WS/WSS
Example Upgrade:
GET /ws/chat → Upgrade: websocket Then messages flow both ways.
gRPC Streaming
- Multi-stream, type-safe
- Ideal for inter-service communication
- Not well expressed through OpenAPI
- High performance and supports client, server, and bidirectional streaming
12.5. Anti-Patterns to Avoid
- ❌ Replacing every API with streaming: Only use streaming when real-time requirements exist.
- ❌ Using WebSockets when SSE is enough: If clients only need server client messages, SSE is simpler and more scalable.
- ❌ Pushing massive payloads: Streaming is for small, frequent messages.
- ❌ No heartbeat or keep-alive strategy: Leads to broken connections and false “client disconnected” states.
- ❌ Coupling streaming format to backend implementation: Keep message schemas stable and versioned like any other API.
12.6. OpenAPI Examples
OpenAPI 3.x can represent SSE (but not full WebSockets), so we’ll document the SSE version.
openapi: 3.0.3
info:
title: Orders Events Stream API - Streaming Pattern Example
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/events/orders:
get:
summary: Stream order-related events using SSE
tags: [Events]
parameters:
- in: query
name: eventTypes
required: false
schema:
type: array
items:
type: string
example: ["order.shipped", "order.cancelled"]
responses:
'200':
description: SSE stream of order events
content:
text/event-stream:
schema:
$ref: '#/components/schemas/OrderEvent'
examples:
default:
value: |
event: order.shipped
data: {"orderId":"or_12345","when":"2024-05-01T10:00:00Z"}
components:
schemas:
OrderEvent:
type: object
properties:
eventType:
type: string
example: "order.shipped"
data:
type: object
example:
orderId: "or_12345"
when: "2024-05-01T10:00:00Z"
required:
- eventType
- data
Note: SSE does not use JSON payload wrapping — each event is a text block with fields like
event:anddata:.
12.7. Visualizing the Streaming Pattern (Mermaid)
A. SSE (One-Way Server Push)
sequenceDiagram
autonumber
participant C as Client (Browser/App)
participant API as Events API
participant ES as Event Source
Note over C,API: Establish SSE connection
C->>API: GET /events/orders\nAccept: text/event-stream
API-->>C: 200 OK (stream open)
Note over ES,API: Backend emits events
ES->>API: publish(order.shipped)
API-->>C: event: order.shipped\ndata: {...}
ES->>API: publish(order.cancelled)
API-->>C: event: order.cancelled\ndata: {...}
Note over C: Client auto-reconnects if connection drops
B. WebSockets (Bidirectional Streaming)
sequenceDiagram
autonumber
participant C as Client
participant API as WS Gateway
participant BS as Backend Service
Note over C,API: WebSocket upgrade handshake
C->>API: GET /ws/chat\nUpgrade: websocket
API-->>C: 101 Switching Protocols
Note over C,API: Bidirectional messaging begins
C->>API: {"type":"sendMessage","text":"Hello!"}
API->>BS: forward message
BS-->>API: {"type":"messageReceived","user":"Alice","text":"Hello!"}
API-->>C: {"type":"messageReceived","user":"Alice","text":"Hello!"}
Note over C,API: Connection stays open for real-time communication