Core Building Blocks
Stream
Transform continuous data pipelines in real-time
A stream is a long-running request/response handler that emits multiple frames (start, chunk, complete, error, cancel) instead of a single payload. Use streams for incremental delivery, token-by-token AI responses, progress feeds, or any flow that maps well to Server-Sent Events (SSE).
Streams live inside services alongside commands and subscriptions. The service builder collects stream definitions, and the event bridge routes incoming stream requests to the right handler.
Request/response with multiple frames
Unlike a command which returns a single response, a stream emits a sequence of frames. If you need fire-and-forget behavior, use a subscription instead.
In this section
What is a Stream?
Understand the stream lifecycle, frame types, writer API, and when to use streams.
The Stream Builder
Define typed chunk/final schemas, guards, SSE endpoints, and stream consumers.
Testing
Unit test handlers with context mocks or validate runtime frame delivery with harnesses.
Quick reference
Stream lifecycle
flowchart LR
A[Client Opens Stream] --> B[Input Validation]
B --> C[Before Guards]
C --> D[Stream Function]
D --> E[writer.write chunk]
E --> F{More data?}
F -->|Yes| E
F -->|No| G[writer.close final]
D -.->|Cancel| H[writer.cancelled = true]
D -.->|Error| I[writer.fail error]
Scaffold a new stream
purista add stream
Key principles
- Streams are for long-running, stateful flows that emit incremental results rather than one final response.
- Streams are request/response with multiple frames — they emit
start,chunk,complete,error, orcancel. - The connection is held open for the duration of the stream — the client waits for the terminal frame.
- Schemas drive TypeScript inference for input, chunk, and final payloads.
- Use
async function, not arrow functions, to preservethiscontext. - Always emit a terminal frame (
writer.close(),writer.fail(), or handle cancellation). - Expose via REST with
.exposeAsHttpStreamEndpoint()which setstext/event-stream. - Check
writer.cancelledin loops to handle client disconnects gracefully. - If the caller should disconnect but work should continue, use a queue instead.