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
5 min read
Sponsored
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:
| Area | Fix | Why it matters |
|---|---|---|
| Crypto | Large DH generator validation | Rejects malformed Diffie-Hellman parameters that could previously slip through |
| Crypto | EdDSA small-order point rejection | Closes a signature-verification edge case in Ed25519/Ed448 |
| Crypto | Large RSA exponent handling in X.509 | Prevents malformed certificates with oversized exponents from causing unexpected behavior |
| Zlib | Reject trailing gzip members in web streams | Matches the behavior of Node’s classic zlib API, closing an inconsistency |
| Streams | Preserve half-open duplexes in async iteration | Fixes 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
More from this category
More from Web Development
R.01 MySQL Now Has a Native VECTOR Type. Should You Drop pgvector For It?
R.02 React 19.2 vs 'React 20': There Is No React 20, and Here's What's Actually New
R.03 Next.js's First Scheduled Security Release Shipped: 9 Advisories, What to Patch
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored