Creating streams
Build streams from iterables, effects, paginated APIs, callbacks, and Node readables. Read more
A Stream<A, E, R> is an effectful, pull-based sequence of values. Where an
Effect<A, E, R> produces a single result (or fails with E), a Stream
produces zero, one, or many A values over time — and may also fail with E
or require services R. Streams can be finite (the lines of a file) or infinite
(a polling loop, a socket, a clock tick), and they evaluate lazily: nothing runs
until you run the stream with a destructor like Stream.runForEach or a
Sink.
Because streaming is pull-based, a Stream only does as much work as its
consumer demands. This gives you backpressure for free, bounded memory over huge
or unbounded sources, and the same composability you get from Effect —
typed errors, resource safety via Scope, and structured
concurrency.
import { Effect, Stream } from "effect"
// A pipeline reads numbers, keeps the even ones, doubles them, and logs each.// Nothing executes until `runForEach` pulls values through the pipeline.const program = Stream.range(1, 10).pipe( Stream.filter((n) => n % 2 === 0), Stream.map((n) => n * 2), Stream.runForEach((n) => Effect.logInfo(`value: ${n}`)))
Effect.runFork(program)Effect’s streaming model is built from three cooperating modules. You will spend
almost all of your time with Stream; Sink shows up when you want a reusable
consumer, and Channel is the low-level engine you rarely touch directly.
Stream<A, E, R> — the producer. A pull-based source of A values that
you create, transform, and eventually run. This is the module you reach for
first.Sink<A, In, L, E, R> — the consumer. A composable
description of how a stream is folded into a single result A (a sum, a
count, the first element, a collected array), consuming In elements and
possibly leaving L leftovers. Run one with Stream.run.Channel<…> — the transformer. The lower-level primitive that both
Stream and Sink are implemented on top of. Channels read and write chunks
of values and are what make operators like Stream.pipeThroughChannel (used
for encoding) possible.Put together, they form a pipeline: a Stream produces values, channels
transform them, and a Sink consumes them into a result.
import { Effect, Sink, Stream } from "effect"
// create -> transform -> run (into a Sink)const total = Stream.range(1, 5).pipe( Stream.map((n) => n * n), // 1, 4, 9, 16, 25 Stream.run(Sink.sum) // fold all values into a single number)
Effect.runPromise(total).then(console.log)// => 55Creating streams
Build streams from iterables, effects, paginated APIs, callbacks, and Node readables. Read more
Transforming streams
map, flatMap, filter, mapEffect, scan, grouping, and concurrency.
Read more
Consuming streams
Run streams with runForEach, runCollect, runDrain, and runFold.
Read more
Error handling
Recover, retry, and time out within streams. Read more
Encoding
Decode and encode structured data with Ndjson and Msgpack. Read
more
Sink
Describe how a stream is consumed into a single result. Read more
Use a Stream when a source naturally yields many values over time and you want
to process them incrementally instead of materialising everything in memory:
log lines, paginated HTTP responses, websocket messages, file chunks, database
cursors, or a polling loop. For a single asynchronous result, a plain
Effect is the right tool.