Getting Started with getWritable()
Every workflow run has a default writable stream that steps can write to using getWritable(). Data written to this stream becomes immediately available to clients consuming the workflow’s output.
Consuming the Stream
Use theRun object’s readable property to consume the stream from your API route:
Resuming Streams from a Specific Point
Userun.getReadable({ startIndex }) to resume a stream from a specific position. This is useful for reconnecting after timeouts or network interruptions:
Streams as Data Types
ReadableStream and WritableStream are standard Web Streams API types that Workflow DevKit makes serializable. These are not custom types - they follow the web standard - but Workflow DevKit adds the ability to pass them between functions while maintaining their streaming capabilities.
Unlike regular values that are fully serialized to the event log, streams maintain their streaming capabilities when passed between functions.
Key properties:
- Stream references can be passed between workflow and step functions
- Stream data flows directly without being stored in the event log
- Streams preserve their state across workflow suspension points
How Streams Persist Across Workflow SuspensionsStreams in Workflow DevKit are backed by persistent, resumable storage provided by the “world” implementation. This is what enables streams to maintain their state even when workflows suspend and resume:
- Vercel deployments: Streams are backed by a performant Redis-based stream
- Local development: Stream chunks are stored in the filesystem
Passing Streams as Arguments
Since streams are serializable data types, you don’t need to use the specialgetWritable(). You can even wire your own streams through workflows, passing them as arguments from outside into steps.
Here’s an example of passing a request body stream through a workflow to a step that processes it:
Important Limitation
Streams Cannot Be Used Directly in Workflow ContextYou cannot read from or write to streams directly within a workflow function. All stream operations must happen in step functions.
Namespaced Streams
UsegetWritable({ namespace: 'name' }) to create multiple independent streams for different types of data. This is useful when you want to separate logs, metrics, data outputs, or other distinct channels.
Consuming Namespaced Streams
Userun.getReadable({ namespace: 'name' }) to access specific streams:
Common Patterns
Progress Updates for Long-Running Tasks
Send incremental progress updates to keep users informed during lengthy workflows:Streaming AI Responses with DurableAgent
Stream AI-generated content using DurableAgent from @workflow/ai. Tools can also emit progress updates to the same stream using data chunks with the UIMessageChunk type from the AI SDK:
For a complete implementation, see the flight booking example which demonstrates streaming AI responses with tool progress updates.
Streaming Between Steps
One step produces a stream and another step consumes it:Processing Large Files Without Memory Overhead
Process large files by streaming chunks through transformation steps:Best Practices
Release locks properly:Stream locks acquired in a step only apply within that step, not across other steps. This enables multiple writers to write to the same stream concurrently.
Stream Failures
When a step returns a stream, the step is considered successful once it returns, even if the stream later encounters an error. The workflow won’t automatically retry the step. The consumer of the stream must handle errors gracefully. For more on retry behavior, see Errors and Retries.Stream errors don’t trigger automatic retries for the producer step. Design your stream consumers to handle errors appropriately. Since the stream is already in an errored state, retrying the consumer won’t help - use
FatalError to fail the workflow immediately.Related Documentation
getWritable()API Reference - Get the workflow’s writable streamsleep()API Reference - Pause workflow execution for a durationstart()API Reference - Start workflows and access theRunobjectgetRun()API Reference - Retrieve runs and their streams later- DurableAgent - AI agents with built-in streaming support
- Errors and Retries - Understanding error handling and retry behavior
- Serialization - Understanding what data types can be passed in workflows
- Workflows and Steps - Core concepts of workflow execution