Goal
Stream binary data between functions that may run in different worker processes. Channels give you Node.js-style readable and writable streams backed by WebSocket connections through the engine, so you can move large payloads incrementally instead of cramming everything into a single JSON message.What Are Channels
Channels are a streaming primitive built into the iii engine. They let one function write data while another function reads it — in real time, across process and language boundaries. Every channel has two ends:- A writer — exposes a
Writablestream for sending binary data. - A reader — exposes a
Readablestream for receiving binary data.
StreamChannelRef) that you embed inside a trigger() payload. When the receiving function deserializes the ref, the SDK automatically connects it to the engine and materializes a live ChannelWriter or ChannelReader.
Function A creates a channel, sends readerRef to Function B through a normal trigger() call, then writes chunks to the writer stream. The engine pipes each chunk over WebSocket to Function B’s reader stream. When A ends the writer, B’s reader emits end.
For internal details on the WebSocket framing, backpressure handling, and lazy connection behavior, see the Channels architecture reference.
Why Channels Are Necessary
Function invocations in iii pass data as JSON messages. This works for structured payloads, but falls apart when you need to:- Transfer large binary data — files, images, PDFs, datasets. Serializing a 100 MB file as a JSON field is impractical and blows up memory.
- Stream data incrementally — progress updates, partial results, or data that is produced over time. JSON messages are all-or-nothing.
- Stream HTTP responses — serving file downloads, SSE streams, or chunked responses to HTTP clients requires writing data progressively to the response body.
- Pipeline processing — chaining producer and consumer functions where the consumer starts processing before the producer finishes.
When to Use Channels
Rule of thumb: if your data is small enough to fit comfortably in a JSON payload (< 1 MB) and you don’t need incremental delivery, use a regular
trigger() call. Use channels when you need streaming, binary data, or when the payload is too large to serialize at once.
Steps
1
Create a channel
Call Creating a channel is cheap — the WebSocket connection is established lazily on first read or write.
createChannel() on the SDK instance. This returns a channel object containing both local stream objects and their serializable refs.- Node / TypeScript
- Python
- Rust
2
Pass the ref to another function
Embed a ref (either The ref is a plain JSON object with three fields —
readerRef or writerRef) inside the trigger() payload. The SDK on the receiving side automatically materializes it into a live ChannelReader or ChannelWriter.- Node / TypeScript
- Python
- Rust
channel_id, access_key, and direction — so it survives serialization across languages and processes.3
Write data to the channel
Use the writer’s stream to send binary data. Data is automatically chunked into 64 KB frames over the WebSocket.
- Node / TypeScript
- Python
- Rust
4
Read data from the channel
On the receiving side, iterate over the reader stream to consume chunks as they arrive.
- Node / TypeScript
- Python
- Rust
Real-World Examples
Example 1: File Download via HTTP Endpoint
A common use case is serving file downloads from an HTTP endpoint. Thehttp() helper gives you a response object with a writable stream — pipe data directly to it without buffering the entire file in memory.
- Node / TypeScript
- Python
http() wrapper in Node/TypeScript gives you direct access to the underlying channel writer as res.stream, so the file streams byte-by-byte from disk to the HTTP client without ever being fully buffered in memory.
Example 2: Server-Sent Events (SSE) Streaming
Channels power SSE endpoints where you push events to the client over time. Write each SSE frame to the response stream and the client receives them as they arrive.- Node / TypeScript
Example 3: Streaming Data Between Functions
When one function produces data and another processes it, channels let the consumer start working before the producer finishes. This is the classic pipeline pattern.- Node / TypeScript
- Python
- Rust
Example 4: Bidirectional Streaming with Progress
When two functions need to exchange data in both directions, create two channels. The writer’ssendMessage() method provides a side-channel for text-based progress or metadata that doesn’t mix with the binary stream.
- Node / TypeScript
sendMessage(), and writes the transformed result to the output channel. The coordinator collects both progress messages and the binary result.
API Quick Reference
createChannel()
Returns an object with four properties:
ChannelWriter
ChannelReader
StreamChannelRef
A plain JSON object that survives serialization:
Common Pitfalls
Next Steps
Channels Architecture
Internal design: WebSocket framing, backpressure, and lazy connections
Expose an HTTP Endpoint
Register a function as a REST API endpoint
Use Functions & Triggers
Learn how to register and trigger functions across languages
Stream Real-Time Data
Push real-time updates to connected clients