Skip to content
GigadriveDocs

Streaming

Node and Bun Functions stream server-sent events to the client as they are produced, with no flag to set.

A Node or Bun Function streams server-sent events by default. A text/event-stream response goes out as your code writes it, so live updates and progress events reach the browser as they happen instead of landing in one block at the end. Responses with other content types, such as text/html and text/plain, reach the client in one piece once your Function has finished writing them.

What it looks like

// api/events.js
import http from 'node:http';

const server = http.createServer((req, res) => {
  res.writeHead(200, {
    'content-type': 'text/event-stream',
    'cache-control': 'no-store',
  });

  let tick = 0;
  const timer = setInterval(() => {
    tick += 1;
    res.write(`data: ${JSON.stringify({ tick, at: new Date().toISOString() })}\n\n`);
    if (tick === 10) {
      clearInterval(timer);
      res.end();
    }
  }, 1000);

  req.on('close', () => clearInterval(timer));
});

server.listen(3000);
version: 4

functions:
  api/events.js:
    runtime: node-22
    max_duration: 60

routes:
  - source: ^/events$
    destination: /api/events.js

Each res.write of an event stream is flushed on its own rather than buffered until the handler returns, so the client sees the first event about a second after connecting. Gigadrive Network captures the server your code creates and assigns it a port, so the number you pass to listen is ignored.

Response headers

The response body passes through untouched. Caching and compression headers are handled at the edge:

  • A streamed response you want cached needs an explicit cache-control. A Function that sets none gets cache-control: no-store.
  • Compression is negotiated at the edge, not in your Function. Your code never sees the client's accept-encoding, and the CDN compresses the response and sets its own content-encoding and content-length. Write plain bytes.

Where a stream ends

Set max_duration to something above the longest stream you expect, and close the stream from your own code when a client disconnects. In the example, req.on('close', ...) stops the interval when a browser navigates away; without it the Function keeps producing events nobody reads until the deadline fires.

A WebSocket is the better fit when the client needs to send as well as receive.