Node ships two stream implementations. node:stream is the original, event-based one used so far. node:stream/web implements the WHATWG Streams standard — ReadableStream, WritableStream, TransformStream and the queuing strategies — the same objects a browser exposes, and globals in Node. The Node documentation marks it Stability 2 – Stable; it left experimental status in Node 21. It signals backpressure through promises such as writer.ready, not a false return and a drain event.
You meet both either way: fetch() response bodies, Blob.stream(), CompressionStream and TextDecoderStream are Web Streams, while fs, net, http and zlib give Node streams. Six conversion helpers bridge them, stable since Node 24.0.0 and 22.17.0.
import { text } from 'node:stream/consumers';
const upper = new TransformStream({ transform: (c, ctl) => ctl.enqueue(c.toUpperCase()) });
const src = ReadableStream.from(['alpha ', 'beta']);
console.log(await text(Readable.fromWeb(src.pipeThrough(upper)))); // Web -> Node
await pipeline( // both kinds in one pipeline
Readable.from(['web streams ', 'and node streams interop']), new TextEncoderStream(),
new CompressionStream('gzip'), Writable.toWeb(createWriteStream('interop.gz')),
);ALPHA BETA
Readable.toWeb(nodeReadable) goes the other way, and node:stream/consumers (text, json, buffer, arrayBuffer, blob) collects either kind into a value, for payloads small enough to hold in memory.
Prefer Node streams for server-side plumbing and Web Streams for code that must also run in a browser or edge runtime — as a Next.js 10,514 route handler streaming a response does (Next.js).