Documentation menu

Streaming responses through the tunnel

Serve Server-Sent Events and chunked responses from localhost through a public tunnel URL — relayed live, chunk by chunk, with the timeouts you need to know.

Updated

A tunnel used to mean buffered responses: the whole body assembled on your machine, then shipped to the caller in one piece — fine for JSON, useless for a stream. SchemaClient tunnels now relay streaming responses live: an SSE endpoint on localhost pushes events through the public URL as they happen.

What streams, what buffers

Two kinds of response are relayed chunk by chunk instead of buffered:

  • Server-Sent Events — anything with a text/event-stream content type.
  • Chunked responses — anything with no declared length, which covers long-lived chunked transfers and most streaming framework output.

Everything else still buffers, and buffered responses cap at 5 MB. Responses without a declared length are no longer subject to that cap, since they stream through instead of assembling in memory.

This is what makes it practical to test an SSE feature from a real phone, hand a teammate a live event feed, or point an actual consumer at your dev server before anything is deployed.

Try it with a local SSE endpoint

Run any local server with an SSE route, then start a tunnel from the Tunnel tab (see exposing localhost for the full walkthrough). From another machine or network:

curl -N https://u1-48213977.schemaclient.com/events

-N disables curl's own buffering. You should see events print the moment your local server emits them — not all at once when the connection closes. If they arrive in a burst at the end, the buffering is in your local stack (a proxy or framework default), not the tunnel.

How the relay works

The desktop app reads the response from your local server and forwards each chunk over its control connection to the gateway, which passes it straight through to the public caller. There is no setting — streaming activates automatically when both the app and the gateway support it, and falls back to buffered relay otherwise. One long-lived stream does not block other requests through the same tunnel; they are handled independently.

Timeouts

Two limits govern a streamed response:

  • Start window (~40 seconds). Your local server must begin responding within the normal tunnel request window. A stream that takes a minute to send its first byte times out like any other slow response.
  • Idle limit (~2 minutes). Once the stream is open, it stays open as long as chunks keep arriving. Two minutes of silence closes it. For SSE, keepalive comment lines (: ping) count as data — if your event source can go quiet for long stretches, emit one every 15-30 seconds.

There is no overall duration cap: a stream that keeps flowing keeps relaying.

What does not stream

  • WebSockets. The upgrade handshake is not relayed, so WebSocket endpoints behind a tunnel do not connect. If you need live updates through a tunnel today, serve them over SSE.
  • Native gRPC. Not supported through the tunnel. gRPC-Web works, because it is ordinary chunked HTTP — which streams.

Streamed traffic still appears in the tunnel's request log alongside everything else — see monitoring API traffic.

Frequently asked questions

Can I use Server-Sent Events through a localhost tunnel?

Yes. SSE responses are relayed live through a SchemaClient tunnel, chunk by chunk, so a browser or phone hitting the public URL sees each event as your local server emits it. The response must start within about 40 seconds, and the stream stays open as long as something arrives at least every 2 minutes — SSE keepalive comments count.

Do WebSockets work through a SchemaClient tunnel?

No. The WebSocket upgrade is not relayed, so WebSocket endpoints behind the tunnel will not connect. SSE is the supported way to push live updates through a tunnel today — it is plain HTTP, so it streams through without special handling on the client.

Why does my streaming response stop after a couple of minutes of silence?

The tunnel closes a stream if no data arrives for about 2 minutes. Quiet streams should send periodic keepalives — for SSE, a comment line (a line starting with a colon) every 15-30 seconds is enough and is ignored by every SSE client.