---
title: Streaming responses
description: Use managed typed iterators or native SSE, text, and byte streams.
pagination: false
sidebar:
  icon: radio
---

Return a sequence of values when a response should arrive incrementally instead of as one completed JSON object.

## Choose managed streaming

Declare `output: streamOf(itemSchema)` and return an async generator. The iterator is lazy,
single-consumer, schema-validates each item, and retains invocation scope until completion or
cancellation. The queue is bounded to 32 items and 4 MiB; a fully encoded item over 1 MiB fails
immediately.

```ts title="examples/commerce/src/services/orders/functions/stream-order-report.function.ts"
/** Supplies the report route with ten validated stream items, stopping when its invocation signal aborts. */
import { defineFunction, streamOf } from "@relkit/app/functions";
import { z } from "@relkit/app/schema";

const streamOrderReport = defineFunction({
  input: z.object({ reportId: z.string().min(1) }),
  output: streamOf(z.object({ completed: z.number().int(), total: z.number().int() })),
  handler: async function* (_input, context) {
    for (let completed = 1; completed <= 10; completed += 1) {
      // if (completed > 1) {
      //   await Bun.sleep(1_000);
      // }
      if (context.signal.aborted) return;
      yield { completed, total: completed };
    }
  },
});

export default streamOrderReport;
```

## Use from the frontend

Expose that function through a route before pulling its contract:

```ts title="src/routes/reports/[reportId]/route.ts — backend"
/** Maps a report path identifier to the order stream and exposes its validated items as server-sent events. */
import { defineRoute, http } from "@relkit/app/routes";
import orders from "../../../services/orders/service.js";

export const GET = defineRoute({
  target: orders.streamOrderReport,
  request: http.input({ reportId: http.path("reportId") }),
  client: { operation: "query" },
  stream: { format: "sse" },
});
```

After [frontend setup and pulling the commerce contract](/guides/frontend/connect-react), render
this component under the provider. The `GET /reports/:reportId` route exposes the streaming function:

```tsx title="src/Report.tsx"
"use client";

/** Consumes the commerce report route as a cancellable typed stream inside the client provider. */
import { useRelkitClient, useStream } from "@relkit/client/react";

/** Starts one report explicitly and displays its validated progress items.
 * @returns Start/cancel controls and visible progress or stream failure. */
export function Report() {
  const { status } = useRelkitClient();
  const report = useStream("GET /reports/:reportId");

  return (
    <section>
      <button
        disabled={status !== "ready"}
        onClick={() => void report.start({ reportId: "daily" })}
      >
        Run report
      </button>
      <button onClick={() => report.cancel()}>Cancel report</button>
      <p role="status">Completed: {report.items.map((item) => item.completed).join(", ")}</p>
      {report.error && <p role="alert">{String(report.error)}</p>}
    </section>
  );
}
```

`streamOf` supplies the item type, so `report.items` exposes each item's `completed` field.
`start({ reportId })` begins the request; `cancel()` releases it. Use the domain input even when
REST places a field in the URL. Streaming operations are excluded from finite `useRoute` queries.

Reference: [`useStream`](/docs/api/client#usestream).
Continue with [stream lifecycle and failures](/docs/modules/client/streams).

## Choose a native format

Managed oRPC iterators preserve typed completion and failure. Native SSE emits `relkit-item` and an
explicit `relkit-complete` or sanitized `relkit-error`. Native text and bytes abort on a post-header
application failure; because EOF cannot always distinguish failure, use managed streaming or SSE
when callers need an explicit terminal result. Existing raw Hono routes remain the escape hatch.

{/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

**Reference and source**

- [`@relkit/routes` API](/docs/api/routes)
- [`@relkit/functions` API](/docs/api/functions)
- [`@relkit/client` API](/docs/api/client)

- [`packages/runtime-hono/tests/native-stream.test.ts`](https://github.com/rel-kit/relkit/blob/main/packages/runtime-hono/tests/native-stream.test.ts)
