Mock an SSE endpoint β€” test EventSource against a real streaming URL

You wrote an EventSource client, a fetch-stream reader, or a progressive-rendering list. Now you need a URL that actually streams β€” and the real streaming backend is, naturally, the part that doesn't exist yet. Faking text/event-stream by hand means standing up a server, getting the framing right (event:/data:/blank line, ids, retry), and keeping it running somewhere your teammates and CI can reach.

Mockbird does it with a query parameter: add ?mock_sse=1 to any mock list endpoint and the records stream as Server-Sent Events β€” one event per record, paced on a timer, closed with a done event. No signup, no server, no code.

The 10-second version

Run this against the public demo project (the -N flag turns off curl's buffering so you see events arrive live):

curl -N 'https://HOST/m/demo/products?mock_sse=1&limit=3&mock_stream_interval=300'

You get correctly-framed SSE, one event every 300 ms:

retry: 60000

id: 1
event: products
data: {"id":1,"name":"Money Light Mountain Water Story","price":280.06, …}

id: 2
event: products
data: {"id":2,"name":"World World Bridge","price":901.93, …}

id: 3
event: products
data: {"id":3,"name":"Eye Member Member Area Money","price":478.72, …}

event: done
data: {"total":30,"sent":3}

The event name is the resource name, the id: field increments from 1 (so lastEventId works), and the final done event tells you the total so your client knows the stream ended on purpose.

Consume it with EventSource

const es = new EventSource("https://HOST/m/demo/products?mock_sse=1&limit=10");

es.addEventListener("products", (e) => {
  render(JSON.parse(e.data));          // called once per record, 500 ms apart
});
es.addEventListener("done", (e) => {
  console.log(JSON.parse(e.data));     // {"total":30,"sent":10}
  es.close();                          // important β€” see the reconnect note below
});
es.onerror = () => showRetryBanner();
Close the source in your done handler. Auto-reconnect is EventSource's defining behavior: when the stream ends, the browser will reconnect and replay unless you close(). (The stream sends retry: 60000 so an accidentally-forgotten tab reconnects lazily, not in a hot loop β€” but your client should still close.) That reconnect behavior is itself worth testing: kill the listener mid-stream and verify your UI survives the replay.

NDJSON for fetch-stream readers

Not every streaming client is EventSource. If you're testing a fetch() + ReadableStream reader (the pattern used for newline-delimited JSON APIs), use ?mock_stream=1 instead β€” same records, one JSON object per line, Content-Type: application/x-ndjson:

curl -N 'https://HOST/m/demo/products?mock_stream=1&limit=5'
const res = await fetch("https://HOST/m/demo/products?mock_stream=1&limit=20");
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buf = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += value;
  const lines = buf.split("\n"); buf = lines.pop();
  for (const line of lines) if (line) render(JSON.parse(line));
}

?mock_stream=sse is an alias for mock_sse=1 if you prefer one parameter.

Stream your data, not ours

The demo is read-mostly. Your own project takes one curl and streams the same way:

> curl -s -X POST https://HOST/api/projects \
  -H 'content-type: application/json' \
  -d '{"name":"my-stream","preset":"ecommerce"}'
# β†’ {"id":"abc123xyz9","adminKey":"…", …}

> curl -N 'https://HOST/m/abc123xyz9/orders?mock_sse=1&sortBy=createdAt&order=desc'

Everything composes before streaming: filters (?status=shipped, price_gte=100), full-text q=, sortBy/order, page/limit, field projection (select=name,price), nested routes (/products/1/reviews?mock_sse=1 streams one product's reviews), even snapshot pinning. Whatever the query would have returned as an array is what streams.

Pacing, and testing the ugly paths

What you're testingURL
Slow trickle (skeleton screens, "still loading" states)?mock_sse=1&mock_stream_interval=2000
Firehose (does your renderer batch, or thrash?)?mock_sse=1&mock_stream_interval=0&limit=100
Slow first byte (connection spinner)?mock_sse=1&mock_delay=3000
Server rejects the stream β†’ onerror fires?mock_sse=1&mock_status=503
Stream sometimes fails (reconnect logic)?mock_sse=1&mock_chaos=0.3

mock_stream_interval is the per-record gap in ms (default 500, max 5000; total stream time is capped at 60 s, so the interval shrinks automatically for big pages). mock_status and mock_chaos win over streaming β€” they return a normal JSON error with that status, which is exactly what makes EventSource.onerror and reconnect paths fire. More failure-testing recipes here.

Auth-protected streams

If your project is in protected mode, there's a wrinkle: EventSource can't send an Authorization header β€” the browser API simply has no option for it. So protected projects also accept the JWT as a query parameter on mock endpoints:

new EventSource(`https://HOST/m/<id>/orders?mock_sse=1&mock_token=${jwt}`);

The token value is redacted in the request inspector, and this works on any mock endpoint (handy for <img>-adjacent cases too). Get a JWT from the project's own mock login endpoint.

What else is out there (honest comparison)

MSW added first-class SSE mocking in v2.12.0 (sse() handler) and it's genuinely good β€” type-safe, standards-based, and it can intercept an EventSource pointed at your real URL. If you're writing JS unit tests, use it. The trade-off is the same as all in-process mocking: the mock exists only inside your JS test process. curl, Postman, a mobile app, a backend consumer, a teammate, or CI hitting a deployed preview get nothing β€” and the handlers are code you write and maintain. A hosted URL needs no process at all. (They compose fine: MSW in unit tests, a Mockbird URL in everything else.)

mocksse and similar npm packages stub the EventSource class in tests β€” same in-process scope, EventSource only, no NDJSON, no real network.

Roll your own β€” a 30-line Express/Flask server writing text/event-stream works, and is the right call if you need custom event logic. You're now running and deploying a server so CI and teammates can reach it, which is the thing you were trying to avoid.

Hosted mock services β€” as of July 2026 we could not find hosted SSE mock endpoints in WireMock Cloud, Postman mock servers, mockapi.io, or Mockoon Cloud (Beeceptor's streaming support targets gRPC). If you know of one, tell us and we'll list it here.

Honest limits

Create yours

curl -s -X POST https://HOST/api/projects \
  -H 'content-type: application/json' \
  -d '{"name":"my-api","preset":"ecommerce"}'

…or use the one-click dashboard link β€” no terminal required. Every list endpoint you create streams with ?mock_sse=1 from the first second. See the streaming docs for the full parameter reference.