Streams

Read, write, and manage real-time data streams for workflow runs.

Stream methods live on world.streams (the streams sub-object of the World instance returned by await getWorld()). Use them to write chunks, read streams, and manage stream lifecycle outside of the standard getWritable() pattern.

For most streaming use cases, use getWritable() inside steps. Direct stream methods are for advanced scenarios like building custom stream consumers or managing streams from outside a workflow.

Import

import { getWorld } from "workflow/runtime";

const world = await getWorld(); 
// Stream methods are called on world.streams — e.g. world.streams.write()

Methods

write()

Write a data chunk to a named stream.

await world.streams.write(runId, "default", chunk); 

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID
namestringThe stream name
chunkstring | Uint8ArrayData to write

writeMulti()

Write multiple chunks in a single operation. Optional optimization — not all World implementations support it. Falls back to sequential write() calls if unavailable.

await world.streams.writeMulti?.(runId, "default", [chunk1, chunk2]); 

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID
namestringThe stream name
chunks(string | Uint8Array)[]Chunks to write, in order

get()

Read data from a named stream as a live ReadableStream that waits for new chunks in real time.

const readable = await world.streams.get(runId, "default"); 

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID
namestringThe stream name
startIndexnumberOptional. Positive values skip chunks from the start (0-based). Negative values read from the tail (e.g. -3 starts 3 chunks from the end). Clamped to 0.

Returns: ReadableStream<Uint8Array>

close()

Close a stream when done writing.

await world.streams.close(runId, "default"); 

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID
namestringThe stream name

list()

List all stream names associated with a workflow run.

const streamNames = await world.streams.list(runId); 

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID

Returns: string[]

getChunks()

Fetch stream chunks with cursor-based pagination. Unlike get() (which returns a live ReadableStream), this returns a snapshot of currently available chunks.

const result = await world.streams.getChunks(runId, "default", { 
  limit: 50,
}); 
// result.data: StreamChunk[], result.cursor, result.hasMore, result.done

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID
namestringThe stream name
options.limitnumberMax chunks per page (default: 100, max: 1000)
options.cursorstringCursor from a previous response

Returns: StreamChunksResponse

FieldTypeDescription
dataStreamChunk[]Chunks in index order. Each has index (0-based) and data (Uint8Array).
cursorstring | nullCursor for the next page
hasMorebooleanWhether more pages of already-written chunks exist
donebooleanWhether the stream is fully closed. When false, new chunks may appear in future requests even after hasMore is false.

getInfo()

Retrieve lightweight metadata about a stream without fetching chunks.

const info = await world.streams.getInfo(runId, "default"); 
// info.tailIndex: last chunk index (-1 if empty), info.done: whether stream is closed

Parameters:

ParameterTypeDescription
runIdstringThe workflow run ID
namestringThe stream name

Returns: StreamInfoResponse

FieldTypeDescription
tailIndexnumberIndex of the last known chunk (0-based). -1 when no chunks have been written.
donebooleanWhether the stream is fully complete (closed).

Examples

Read a Stream as a Response

// app/api/workflow-streams/read/route.ts
import { getWorld } from "workflow/runtime";

export async function GET(req: Request) {
  const url = new URL(req.url);
  const streamName = url.searchParams.get("name") ?? "default";
  const runId = url.searchParams.get("runId")!;
  const world = await getWorld();
  const readable = await world.streams.get(runId, streamName); 

  return new Response(readable, {
    headers: { "Content-Type": "application/octet-stream" },
  });
}

Paginate Through Stream Chunks

import { getWorld } from "workflow/runtime";

const world = await getWorld();
let cursor: string | undefined;

do {
  const result = await world.streams.getChunks(runId, "default", { cursor }); 
  for (const chunk of result.data) {
    console.log(`Chunk ${chunk.index}:`, chunk.data);
  }
  cursor = result.cursor ?? undefined;
} while (cursor);
  • Streaming — Core concepts for streaming data from workflows
  • getWritable() — The standard way to write to streams from within steps
  • Storage — Query runs, steps, hooks, and events