Streaming

How to send streamed responses and Server-Sent Events in Kito

Kito provides built-in support for streaming responses and Server-Sent Events (SSE).
These features allow your server to send data progressively without buffering the entire response, making them ideal for:

  • large payloads
  • real-time updates
  • incremental rendering
  • long-running tasks
  • log or progress streams

Streaming is fully compatible with Node.js’ HTTP layer and works out-of-the-box with Kito.


Basic Streaming Response

A streaming response sends data in chunks as it becomes available.
Kito exposes a simple API through ctx.res.stream().

import { server } from "kitojs";
 
const app = server();
 
app.get("/stream", ({ res }) => {
  const stream = res.stream();
 
  stream.write("Chunk 1\n");
 
  setTimeout(() => {
    stream.write("Chunk 2\n");
  }, 1000);
 
  setTimeout(() => {
    stream.end("Final chunk\n");
  }, 2000);
});
 
app.listen(3000);

How it works

  • ctx.res.stream() starts a chunked HTTP response.
  • stream.write() sends data immediately to the client.
  • stream.end() sends the final chunk and closes the response.
  • When Kito detects a streaming response, it automatically sets the proper headers and flush behavior.

Server-Sent Events (SSE)

SSE provides one-way real-time communication from server to client over an HTTP connection. Kito includes a minimal and convenient SSE API.

import { server } from "kitojs";
 
const app = server();
 
app.get("/events", ({ res }) => {
  const sse = res.sse();
 
  sse.send({ msg: "connected" }, "init");
 
  let count = 0;
 
  const interval = setInterval(() => {
    sse.send({ count }, "update");
    count++;
 
    if (count >= 5) {
      clearInterval(interval);
      sse.send({ msg: "done" }, "complete");
      sse.close();
    }
  }, 1000);
});
 
app.listen(3000);

Key behaviors

  • ctx.res.sse() sets the correct SSE headers automatically.
  • sse.send(data, eventName) emits an SSE event with JSON-encoded data.
  • Events are flushed immediately to the client.
  • sse.close() closes the connection gracefully.

When to Use Streaming

Streaming is useful when:

  • you want to send partial results as they are produced
  • you generate large data sets
  • the process takes time and you want to show incremental progress
  • you need low-latency real-time updates
  • you’re implementing logs, progress bars, AI output streaming, or incremental rendering

If you need bidirectional real-time communication, consider WebSockets or WebTransport (coming soon).


Client Examples

Fetch Streaming

const response = await fetch("/stream");
const reader = response.body.getReader();
 
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(new TextDecoder().decode(value));
}

SSE Client

const events = new EventSource("/events");
 
events.addEventListener("update", (e) => {
  console.log(JSON.parse(e.data));
});
 
events.addEventListener("complete", () => {
  events.close();
});