Skip to content

Web Development · Backend

Node.js 26.5.0: ReadableStreamTee, blob.textStream(), and What Else Shipped

Node.js 26.5.0 landed July 8, 2026 with a handful of small but useful additions: an exposed ReadableStreamTee, streaming Blob text reads, TLS group reporting, and a batch of crypto hardening fixes. Here's what's worth adopting now.

Abhishek Gupta

Abhishek Gupta

5 min read

Node.js 26.5.0: ReadableStreamTee, blob.textStream(), and What Else Shipped

Sponsored

Share

Node.js 26.5.0 shipped on July 8, 2026, and it’s the kind of release that’s easy to skim past. No headline feature, no breaking change, nothing that forces a migration guide. But three or four of the additions in this release are worth adopting immediately if you’re doing any nontrivial stream or Blob handling, and the security-adjacent fixes are worth taking even if you never touch the new APIs directly.

Here’s what actually matters.

ReadableStreamTee is no longer hidden

The Web Streams spec has always had a concept of “teeing” a stream, splitting one ReadableStream into two independent readers that each see the full sequence of chunks. Node’s internal implementation used this to support things like piping a response body to both a cache and a client simultaneously. Until 26.5.0, it wasn’t exposed as something you could call yourself.

Now it is:

import { ReadableStreamTee } from 'node:stream/web';

const response = await fetch('https://api.example.com/large-dataset.json');
const [forCache, forClient] = ReadableStreamTee(response.body);

// Write one copy to disk while streaming the other to the client
await Promise.all([
  writeStreamToCache(forCache, 'dataset.json'),
  pipeToHttpResponse(forClient, res),
]);

Before this, achieving the same result meant manually reading from the source stream and writing matching chunks into two separate TransformStream or PassThrough instances, tracking backpressure on both sides yourself. ReadableStreamTee handles that bookkeeping. If you’ve ever written a caching proxy, a request-logging middleware that needs to inspect a body without consuming it, or anything that needs to fan a stream out to multiple consumers, this removes a real chunk of boilerplate.

One caveat: teeing buffers whichever branch is read more slowly, so it’s not a substitute for actual backpressure-aware fan-out when the two consumers have wildly different processing speeds. It’s a convenience for the common case, not a scheduling primitive.

Streaming text out of a Blob

blob.text() has always been available, but it returns a single promise that resolves once the entire Blob has been decoded into a string. For a small config file that’s fine. For a multi-hundred-megabyte log export, it means holding the whole decoded string in memory before you can do anything with it.

blob.textStream() fixes that:

const blob = new Blob([largeBuffer], { type: 'text/plain' });

for await (const chunk of blob.textStream()) {
  processLogLine(chunk);
}

This mirrors what blob.stream() already did for binary data, just with UTF-8 decoding handled incrementally instead of requiring you to pipe through a TextDecoderStream yourself. It’s a small API, but it closes a gap that showed up whenever people mixed Blob and streaming text processing in the same code path.

TLS group reporting

Connections established via node:tls now expose which key exchange group was negotiated during the handshake. If you’re running hybrid or post-quantum key exchange in front of a service (a growing number of teams are, given the NIST timelines pushing classical-only key exchange toward deprecation), you can now assert on the negotiated group programmatically instead of relying on packet captures:

const socket = tls.connect(443, 'example.com', () => {
  console.log('Negotiated group:', socket.getTLSGroup?.());
});

This is a small but genuinely useful addition for anyone auditing TLS configuration rollout across a fleet of services, especially if your compliance requirements are starting to ask about post-quantum readiness.

The quieter fixes worth knowing about

A handful of changes in this release don’t come with new APIs but are worth taking regardless:

AreaFixWhy it matters
CryptoLarge DH generator validationRejects malformed Diffie-Hellman parameters that could previously slip through
CryptoEdDSA small-order point rejectionCloses a signature-verification edge case in Ed25519/Ed448
CryptoLarge RSA exponent handling in X.509Prevents malformed certificates with oversized exponents from causing unexpected behavior
ZlibReject trailing gzip members in web streamsMatches the behavior of Node’s classic zlib API, closing an inconsistency
StreamsPreserve half-open duplexes in async iterationFixes a subtle bug where async-iterating a duplex stream could close it prematurely

None of these are dramatic on their own, but they’re the kind of fix that prevents a very confusing debugging session six months from now when someone finally hits the edge case in production.

Should you upgrade?

Yes, and there’s no real reason to wait. This is a semver-minor release: nothing is removed, nothing changes behavior for existing code, and the new APIs are additive. If you’re on an active LTS or Current line already, bumping to 26.5.0 is a routine npm install -g away with no migration checklist attached.

If you’re evaluating what runtime to build a new service on in the first place rather than just patching an existing one, it’s worth reading how Deno 2.9’s desktop app support has been quietly expanding into territory Node doesn’t touch, or our broader Node.js vs Deno vs Bun comparison if you’re picking a stack from scratch. For teams already committed to Node, though, 26.5.0 is exactly what a healthy runtime release should look like: small, safe, and worth taking on day one.

Frequently asked questions

Is Node.js 26.5.0 a security release?
It contains security-relevant hardening (DH generator validation, EdDSA small-order point rejection, RSA exponent handling in X.509 certificates) but the Node.js project did not publish it under a dedicated security advisory. Treat it as a routine release worth taking promptly, not an emergency patch.
What is ReadableStreamTee and why does it matter?
It is the underlying utility the Streams spec uses to let a single ReadableStream be consumed by two independent readers, each getting its own copy of the data without you manually buffering and re-emitting chunks. Node.js 26.5.0 exposes it directly instead of keeping it as an internal implementation detail.
Do I need to change anything to upgrade from 26.4.x?
No. This is a semver-minor release. Existing code keeps working; the new APIs are additive and the experimental flags are opt-in.

Sources

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored