AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 06 — Real-Time APIs: WebSockets, SSE, Streaming, and Large-Data Patterns

06 — Real-Time APIs: WebSockets, SSE, Streaming, and Large-Data Patterns

August 13, 20268 min read
Download as Markdown

The first API I built was a simple request/response, and everything past that shape — real-time channels, streaming responses, huge datasets — looked like a bag of unrelated tricks. The model that finally clicked: real-time is a spectrum of "who pushes, and in which direction," and pagination, filtering/sorting, idempotency, and streaming are the engineering controls that keep large-data APIs reliable when the simple model stops scaling. [1] These aren't disconnected features; they're the toolkit for when "one GET, one JSON body" isn't enough.

The connecting thread is the limits of the simple exchange. Polling for live updates wastes resources; one giant response exhausts memory; a retried POST creates duplicates; a slow handler blocks the client. Each node in this section is a targeted fix for one of those limits.

Real-time APIs: why polling isn't enough

A real-time API keeps an open channel so updates reach the client as they happen, instead of the client repeatedly asking "anything new?" [1] The alternative — polling — is simple but wasteful: most polls return nothing, and the real update arrives only on the next poll, adding latency. Real-time APIs trade that simplicity for an open connection and immediate, often bidirectional, data flow.

Three mechanisms sit on this spectrum, and the differences are about direction and complexity:

Polling (not real-time) SSE — server pushes WebSocket — full duplex client server repeated GETs, mostly empty client server 1 request held open, events pushed client server both directions, any time

WebSockets: full duplex

WebSockets upgrade a single HTTP connection into a persistent, bidirectional channel — either side can send messages at any time, with low overhead per message [2]. This is the right tool when the client also needs to push to the server continuously (chat, collaborative editing, multiplayer games), not just receive. The tradeoff is that you leave HTTP's familiar request/response model behind: you're now managing a long-lived connection, reconnect logic, message ordering, and your own framing. WebSockets are powerful but they cost you the conveniences HTTP gives you for free (caching, status codes, intermediaries).

Server-Sent Events: simple one-way push

Server-Sent Events (SSE) are the lighter option when the data flow is one-way — server to client only [3]. The client opens a normal HTTP connection; the server holds it open and pushes a stream of text/event-stream messages. The browser's EventSource API handles reconnection automatically. SSE is the right fit for live updates, notifications, log tails, and AI token streaming — anywhere the server needs to push and the client doesn't need to send back over the same channel. My default: try SSE first, reach for WebSockets only when the client genuinely needs to push back.

event: orderUpdated
data: {"id": 42, "status": "shipped"}

event: orderUpdated
data: {"id": 42, "status": "delivered"}

Streaming responses: sending before you're done

Related but distinct: streaming responses let a server send data incrementally as it becomes available, rather than buffering the whole response [4]. HTTP chunked transfer encoding is the mechanism, and SSE is one shape of it. The win is perceived performance — a large dataset, a file download, or an AI-generated response can start delivering the first chunk while the rest is still being computed. The user sees progress instead of a hung spinner. For any response that's slow to fully assemble, streaming is usually the right call.

Filtering, sorting, and search

Real-time channels solve freshness; filtering, sorting, and search solve selectivity — letting the client ask for exactly the slice of data it needs instead of the whole collection [5]. These are query-parameter conventions:

  • Filtering — GET /orders?status=active narrows by field value.
  • Sorting — GET /orders?sort=created_at&order=desc orders the results.
  • Search — GET /orders?q=blue does free-text or fuzzy matching.

The design discipline is to use predictable parameter names and document which combinations are supported. A consumer who can filter but not sort, or who has to guess whether it's sort or sortBy, is paying the cost of an under-specified API. The goal is the same predictability as the rest of REST: a consumer should be able to guess the filter parameter for a field they just learned exists.

Pagination: never return the whole table

The hardest large-data problem is how much to return. Pagination delivers data in manageable chunks instead of one overwhelming response [6]. The roadmap highlights three strategies, and the differences matter:

  • Offset/limit — ?page=3&limit=20. Simple, but skips records if data changes between pages, and is slow for deep pages (the database still scans everything before the offset).
  • Cursor-based — ?after=cursor_xyz&limit=20. The client passes an opaque cursor; the server returns the next batch after that point. Stable under inserts, fast for deep pagination. This is the right default for anything that grows.
  • Time-based — ?since=2024-01-01. Useful for incremental sync ("give me everything changed since last poll").

The mental shift that helped: offset pagination is a lie when the data is moving. If a new record inserts between page 1 and page 2 of an offset query, you'll either skip or duplicate a row. Cursor pagination sidesteps this because the cursor is a stable position in the data, not a row count. For any list that can change while a client pages through it, I reach for cursors.

Idempotency: safe retries

Real-world networks drop requests. Idempotency means "calling this N times has the same effect as calling it once" — so a client can safely retry without creating duplicates [7]. The HTTP verbs give us some of this for free (GET, PUT, DELETE are idempotent; POST is not). For non-idempotent operations that nonetheless need safe retries — most famously, payments — the pattern is an idempotency key: the client sends a unique key with the request, and the server records "I already processed this key" so a retry returns the original result instead of running again.

POST /payments
Idempotency-Key: 7c8d3f1e-...

// retried with the same key → server returns the first result, doesn't charge twice

This is the bridge between "the network is unreliable" and "the operation must happen exactly once." Any mutating endpoint that handles money, sends email, or triggers side effects should accept an idempotency key.

How I use this

When a screen needs live data, I check the direction first: server-to-client only means SSE; genuine two-way means WebSockets. When a list endpoint exists, I add cursor pagination from day one — retrofitting it is painful, and offset pagination betrays you the moment the data grows. When an operation has external side effects, I add an idempotency key before launch, because the first duplicate charge is the one that teaches the lesson. The unifying habit is recognizing early when the simple exchange isn't enough — a screen that polls every second, a response that returns 10,000 rows, a POST that a flaky client will retry — and reaching for the right control then, not after it breaks in production.

References

[1] Ably, "What are realtime APIs and when to use them?," 2024. [Online]. Available: https://ably.com/topic/what-is-a-realtime-api

[2] Mozilla, "The WebSocket API (WebSockets)," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

[3] Mozilla, "Using server-sent events," MDN Web Docs, 2024. [Online]. Available: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

[4] APIs You Won't Hate, "Streaming Data with REST APIs," 2024. [Online]. Available: https://apisyouwonthate.com/blog/streaming-data-with-rest-apis/

[5] OneUptime, "How to Implement Filtering and Sorting in REST APIs," 2026. [Online]. Available: https://oneuptime.com/blog/post/2026-01-26-rest-api-filtering-sorting/view

[6] Nordic APIs, "Everything you need to know about API pagination," 2024. [Online]. Available: https://nordicapis.com/everything-you-need-to-know-about-api-pagination/

[7] DreamFactory, "What is idempotency?," 2024. [Online]. Available: https://blog.dreamfactory.com/what-is-idempotency

Knowledge check · Question 1 of 5

A screen needs live order-status updates pushed from the server. The client never sends data back. Best mechanism?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!