ReadableStream & TransformStream: BYOB Buffer Zero-Copy Piping
- Last updated
- Prerequisites:
- WHATWG Web Streams standard
- TypedArrays and ArrayBuffer
- bun
- web-streams
- readablestream
- byob
- transformstream
- zero-copy
Read at your depth
The practical view
Bun natively implements the WHATWG Web Streams API: `ReadableStream`, `WritableStream`, and `TransformStream`. You can consume streams using `for await (const chunk of stream)`, pipe streams with `stream.pipeThrough(transform).pipeTo(dest)`, and use BYOB ('Bring Your Own Buffer') readers (`stream.getReader({ mode: 'byob' })`) to read raw binary data into pre-allocated `Uint8Array` buffers with zero garbage collection overhead.
The same idea in other frameworks
Node.js `node:stream/web` (wrapped on top of `EventEmitter` streams).Deno native Web Streams implementation.Bun native Zig Web Streams (`ReadableStream`, `TransformStream`, BYOB).Browser native WHATWG Web Streams API.Legacy vs modern
Node.js Stream Conversion vs Bun Native Web Stream Processing
Node requires bridging legacy streams; Bun pipes WHATWG Web Streams natively with zero-copy transforms.
const webStream = Readable.toWeb(nodeStream); // ❌ Stream wrapper translation overheadconst reader = stream.getReader({ mode: 'byob' }); await reader.read(buffer); // ✅ NEWInterview gotchas
Context
Interviewers test your knowledge of low-level memory allocation in streams.
The mechanical answer
BYOB stands for **'Bring Your Own Buffer'**. In standard streams (`reader.read()`), the stream engine allocates a *new* `Uint8Array` chunk on every single read, creating garbage collection pressure. With BYOB (`stream.getReader({ mode: 'byob' })`), the consumer passes an *existing* `Uint8Array` to `reader.read(buffer)`. The runtime reads bytes directly into that pre-allocated memory, eliminating all per-chunk memory allocations.
Trap
A stream must be a byte stream (backed by a byte source) to support `mode: 'byob'`; calling it on an object stream throws a `TypeError`.
Context
Checks knowledge of response stream piping in Bun servers.
The mechanical answer
Simply pass the `ReadableStream` directly into the `Response` constructor: `return new Response(myReadableStream)`. Bun handles backpressure, chunk encoding, and socket closing automatically.
Trap
Do not buffer the stream with `await stream.text()` before creating the response; pass the stream itself.