Skip to content

SYSTEM Cited by 1 source

fast-webstreams

fast-webstreams is Vercel's experimental npm package (experimental-fast-webstreams) reimplementing the WHATWG Streams API surface (ReadableStream, WritableStream, TransformStream) on top of Node.js's older, C++-backed stream.Readable / stream.Writable / stream.Transform internals. Same spec, same error propagation semantics, much less per-chunk Promise + object allocation overhead. Disclosed 2026-04-21 in sources/2026-04-21-vercel-we-ralph-wiggumed-webstreams-to-make-them-10x-faster.

Headline numbers (Node.js v22, 1 KB chunks)

Pattern fast native ratio
start + enqueue (React Flight) 1,600 MB/s 110 MB/s 14.6×
pipeThrough (chained fast-to-fast) 6,200 MB/s 630 MB/s 9.8×
read() loop 12,400 MB/s 3,300 MB/s 3.7×
fetch() → 3 transforms 830 MB/s 260 MB/s 3.2×
Response forwarding 850 MB/s 430 MB/s 2.0×
pipeTo 2,500 MB/s 1,400 MB/s 1.8×

Architecture

Three orthogonal fast paths, selected per-call based on context:

1. Zero-Promise pipe chains (biggest win)

When pipeThrough and pipeTo are called between fast streams, piping does not start immediately. The library records upstream links (source → transform1 → transform2 → ...). When pipeTo() at the end of the chain runs, it walks upstream, collects the underlying Node.js stream objects, and issues a single stream.pipeline() call. One function call, zero Promises per chunk, data flows through Node's optimized C++ path. Canonicalised as patterns/record-pipe-links-resolve-at-sink.

Fallback: if any stream in the chain is not a fast stream (e.g. a native CompressionStream), use native pipeThrough or a spec-compliant pipeTo implementation.

2. Synchronous read() resolution

On reader.read(), the library calls nodeReadable.read() synchronously. If data is there: Promise.resolve({value, done}) — no event-loop round-trip, no ReadableStreamDefaultReadRequest allocation, no pending-Promise machinery. Only if the buffer is empty does the library register a listener and return a pending Promise. Canonicalised as concepts/synchronous-fast-path-streaming.

3. React Flight byte-stream fast path

For new ReadableStream({type: 'bytes'}) with start() + external controller.enqueue() calls (the React Server Components pattern), the library uses LiteReadable — a minimal array-based buffer replacing Node's Readable for byte streams. Direct callback dispatch (no EventEmitter), pull-based demand, BYOB reader support, ~5 µs less per construction. Material when React Flight creates hundreds of byte streams per request.

4. Fetch body deferred resolution

Response.prototype.body is a native byte stream owned by Node's HTTP layer — the library can't swap it out. Instead, when patchGlobalWebStreams() is active, Response.prototype.body returns a fast shell wrapping the native byte stream. pipeThrough() records the link but does not start piping. Only at sink does the library resolve the chain: one Promise at the native boundary to pull data in, bridged into stream.pipeline() for the transform hops, output reads served synchronously.

WPT conformance

Passes 1,100 of 1,116 Web Platform Tests streams tests. Native Node.js passes 1,099. The 16 remaining failures are either shared with native (e.g. unimplemented type: 'owning' transfer mode) or architectural differences that don't affect real apps. This conformance level is what made the project tractable as an AI-assisted reimplementation — the WPT suite is the machine-checkable spec oracle. See patterns/ai-reimplementation-against-conformance-suite and systems/wpt-web-platform-tests.

Installation + activation

npm install experimental-fast-webstreams
import { patchGlobalWebStreams } from 'fast-webstreams';
patchGlobalWebStreams();

patchGlobalWebStreams() replaces global ReadableStream / WritableStream / TransformStream constructors and Response.prototype.body, so unmodified fetch() → pipeThrough() → pipeTo() chains automatically hit the pipeline fast path. Canonicalised as patterns/global-patch-constructors-for-runtime-optimization.

Hard lessons from the implementation

Per the 2026-04-21 post, three recurring surprises during implementation:

  • The spec is smarter than it looks. "We tried many shortcuts. Almost every one of them broke a Web Platform Test, and the test was usually right." The ReadableStreamDefaultReadRequest pattern exists because cancellation during reads + error identity through locked streams + thenable interception are real edge cases.
  • Promise.resolve(obj) always checks for thenables. WPT tests deliberately put .then on read results to verify the stream calls it correctly. The library must be careful where it allocates {value, done} objects in hot paths.
  • stream.pipeline() cannot universally replace WHATWG pipeTo(). Routing everything through pipeline() caused 72 WPT failures on error propagation, stream locking, and cancellation semantics. pipeline() is only safe when the entire chain is fast-stream.
  • Reflect.apply, not .call(). WPT monkey-patches Function.prototype.call to verify implementations don't use it to invoke user callbacks. Reflect.apply is the only safe way.

Upstream trajectory

The project's stated long-term goal is not to exist:

"The goal is for WebStreams to be fast enough that it does not need to."

Two ideas already landed upstream via Node.js TSC member Matteo Collina's PR nodejs/node#61807"stream: add fast paths for webstreams read and pipeTo":

  1. read() fast path — when buffered, return a pre-resolved Promise directly (spec-compliant because resolved-vs-pending is observationally equivalent through the microtask queue).
  2. pipeTo() batch reads — batch multiple reads from the controller queue without per-chunk request objects; backpressure respected via desiredSize check after each write.

Measured upstream: ~17-20 % faster buffered reads, ~11 % faster pipeTo. Applies to every Node.js user free. This is the patterns/upstream-contribution-parallel-to-in-house-integration pattern at the streaming-runtime altitude — library + upstream PR in parallel.

James Snell's tracking issue nodejs/performance#134 enumerates remaining opportunities: C++-level piping for internally-sourced streams, lazy buffering, eliminating WritableStream adapter double-buffering.

Development method

The post discloses the project was "built most of fast-webstreams with AI." Two load-bearing enablers:

  1. WPT's 1,116-test streams suite — immediate, machine-checkable "did we break anything?" oracle.
  2. Locally-built benchmark suite — measure whether each change moved throughput.

The development loop: implement an optimisation → run WPT → run benchmarks. When tests broke, they knew which spec invariant was violated. When benchmarks didn't move, they reverted. Canonical patterns/ai-reimplementation-against-conformance-suite at the streaming-library altitude.

Deployment status

  • npm package: experimental-fast-webstreams (public beta; experimental prefix "intentional")
  • Vercel fleet: "looking at rolling this out across our fleet. We will do so carefully and incrementally. Streaming primitives sit at the foundation of request handling, response rendering, and compression. We are starting with the patterns where the gap is largest: React Server Component streaming, response body forwarding, and multi-transform chains." — no production numbers disclosed yet.

Comparison with adjacent systems

System Approach Status
fast-webstreams Same API, faster implementation via Node streams Vercel; experimental
systems/new-streams New API (AsyncIterable + explicit backpressure) Cloudflare; POC, not shipping
systems/nodejs native Current reference implementation Improving via PR #61807
systems/bun native Zig + JavaScriptCore implementation Ships; avoids this class of cost

fast-webstreams and new-streams are complementary: fast-webstreams improves the current API now; new-streams explores what comes after.

Seen in

Last updated · 476 distilled / 1,218 read