Node.js 26.10 Explained: New APIs, Practical Examples, and What to Adopt
Node.js 26.10 Explained: New APIs, Practical Examples, and What to Adopt
By Neel Shah · September 28, 2026
Node.js 26.10.0 landed on September 22, and it's a quietly useful release for backend developers. Three new APIs stand out — each one deletes a workaround you've probably written before: reading a .p12 certificate bundle in pure JavaScript, turning a file into a Web Blob synchronously, and measuring latency over a rolling window instead of since process start.
Below is what each API solves, a working example you can drop into a real backend, and an honest call on whether to reach for it — plus the part people skip when they chase the Current release: how to decide if you should upgrade at all.
crypto.parsePKCS12() — read .p12 / .pfx bundles in JS
PKCS#12 bundles (the .p12 / .pfx files you get from a bank, a payment processor, or a government mTLS API) pack a private key and its certificate chain into one password-protected blob. Node's TLS stack has always been able to consume them via the pfx option — but you could never get the key and certificates into JavaScript. If you needed to inspect the cert's expiry, read its subject, or convert it to PEM for another tool, you were shelling out to the openssl CLI or pulling in node-forge.
crypto.parsePKCS12() closes that gap. It returns the materials as real KeyObject and X509Certificate instances:
import { parsePKCS12 } from 'node:crypto';
import { readFileSync } from 'node:fs';
const { privateKey, certificate, additionalCertificates } = parsePKCS12(
readFileSync('./partner-client.p12'),
{ passphrase: process.env.P12_PASSPHRASE },
);
Working use case — fail fast when a client cert is about to expire. A silently expired mTLS certificate is a classic 3 a.m. outage. Now you can check it at boot:
// Inspect the end-entity certificate — not possible before without a userland dep
console.log('Subject:', certificate.subject);
console.log('Valid to:', certificate.validTo);
console.log('Chain certs:', additionalCertificates.length);
const daysLeft = (new Date(certificate.validTo) - Date.now()) / 86_400_000;
if (daysLeft < 14) {
throw new Error(`Client cert expires in ${Math.round(daysLeft)} days — rotate it now`);
}
// Export to PEM for a library or an https.Agent that wants PEM, not pfx
const keyPem = privateKey.export({ type: 'pkcs8', format: 'pem' });
const certPem = certificate.toString(); // X509Certificate → PEM
The shape is { privateKey: KeyObject | null, certificate: X509Certificate | null, additionalCertificates: X509Certificate[] } — the first private key, its matching end-entity certificate, and the rest of the chain. The bundle argument accepts a Buffer/ArrayBuffer/TypedArray; omit passphrase and it's treated as an empty string.
Verdict: high value if you touch mTLS or client certificates. It removes a genuinely annoying dependency and unlocks things you couldn't do at all — like asserting on cert expiry at startup. Niche, but a real quality-of-life win where it applies.
fs.openAsBlobSync() — a file-backed Blob, synchronously
Node has had fs.openAsBlob(path) (async) since v20 — it hands you a Web Blob that's lazily backed by the file on disk, so the bytes aren't read into memory until something consumes them. 26.10 adds the synchronous sibling, fs.openAsBlobSync().
Why does a sync version matter? Because plenty of code that needs a Blob isn't async — startup/config code, a synchronous factory, a CLI script, or object construction where you can't await.
Working use case — forward a file to an upstream service without buffering it. A Blob plugs straight into FormData and fetch, so you can proxy an upload through your server while the file streams off disk rather than sitting in a Buffer:
import { openAsBlobSync } from 'node:fs';
// Lazily file-backed — no full-file Buffer in memory
const blob = openAsBlobSync('./invoice.pdf', { type: 'application/pdf' });
const form = new FormData();
form.append('file', blob, 'invoice.pdf');
await fetch('https://documents.example.com/upload', {
method: 'POST',
body: form, // the file streams through on send
});
The { type } option sets the Blob's MIME type. From there you get the full Web Blob surface: arrayBuffer(), text(), stream(), slice(), size, type.
Verdict: handy, with one caveat. Reach for the sync form in startup code and scripts; in a hot request path, prefer the async
openAsBlob()— any syncfscall blocks the event loop while it opens the file. Great convenience, used in the right place.
SlidingWindowHistogram — latency that reflects now
This is the one I'd call underrated. The existing perf_hooks.createHistogram() accumulates samples forever — so an hour into a process, your "p99" is diluted by ancient data and barely moves when latency spikes right now. That's the opposite of what you want for alerting or autoscaling.
createSlidingWindowHistogram() keeps only a bounded, rolling window. It's chunk-based (a ring buffer of sub-histograms), supports count- or time-based windows, uses no timers, and drops whole chunks as the window slides:
import { createSlidingWindowHistogram } from 'node:perf_hooks';
// 6 chunks × 10s each = roughly the last 60 seconds of samples
const latency = createSlidingWindowHistogram({ chunks: 6, chunkDuration: 10_000 });
latency.record(20_000_000); // record a value (nanoseconds here)
const snapshot = latency.snapshot(); // materialize the window as a regular Histogram
console.log('p99:', snapshot.percentile(99));
Working use case — a /metrics endpoint that reports rolling p99. Drop this middleware into an Express app and you get live, recent latency percentiles — ideal for a health probe, a dashboard scrape, or an autoscaling signal:
import express from 'express';
import { createSlidingWindowHistogram } from 'node:perf_hooks';
const app = express();
const latency = createSlidingWindowHistogram({ chunks: 6, chunkDuration: 10_000 });
app.use((req, res, next) => {
const start = process.hrtime.bigint();
res.on('finish', () => latency.record(Number(process.hrtime.bigint() - start)));
next();
});
app.get('/metrics', (req, res) => {
const h = latency.snapshot();
res.json({
windowSeconds: 60,
samples: h.count,
p50_ms: +(h.percentile(50) / 1e6).toFixed(2),
p99_ms: +(h.percentile(99) / 1e6).toFixed(2),
max_ms: +(h.max / 1e6).toFixed(2),
});
});
app.listen(3000);
Note the design: snapshot() returns an independent regular Histogram (with percentile(), max, mean, count, …). The sliding-window instance itself can't be cloned or transferred across a MessagePort, and can't be constructed with new. 26.10 also added confidence intervals (percentileCI) and statistical hypothesis testing to histograms, if you want to get rigorous about comparing distributions.
Verdict: adopt for any service that monitors its own latency. Recent-window percentiles are simply the correct tool for alerting and autoscaling, and until now you'd have hand-rolled the windowing. This is the feature I'd actually put into production first.
Quick hits worth knowing
A few other semver-minor additions in 26.10 that may matter to you:
node:sqlitebindsundefinedtoNULL. The built-in SQLite driver no longer throws when you passundefinedas a parameter — it treats it asNULL, matching how most SQL clients behave.- Post-quantum in Web Crypto. Hybrid KEMs were added to the Web Cryptography API — early groundwork for post-quantum key exchange.
net: send aBoundSocketto workers/children. Useful for custom load-balancing across threads or processes.util.markPromiseAsHandled(). Suppress an unhandled-rejection warning for a promise you're intentionally handling later.- FFI loading from a mounted VFS. More flexibility for native-library loading in bundled/SEA deployments.
Current vs LTS — and how to decide on upgrading
26.10 is a Current release, and this is where the versioning matters. Node ships a new major every six months. Even-numbered majors (like 26) enter Active LTS in the October after release; odd-numbered majors never do.
- Current gets the newest features (every semver-minor, like the three above) but a shorter support window, and it's where APIs are still settling.
- LTS receives no new features — only bug and security fixes — and is supported for roughly 30 months. It's what you run in production.
The timely detail: Node 26 is even, so it becomes Active LTS this October. Everything in 26.10 is about to land in the line you'd run in production — so adopting 26.x now is low-risk, not bleeding-edge.
Before you bump the version, a short checklist:
- Pin and test in CI first. Use
nvm/Volta to run your full test suite on 26.x before touching production; add it to your CI matrix. - Check native addons. A major-version bump changes the ABI (
NODE_MODULE_VERSION) — any native module (bcrypt,sharp, database drivers) must have a build for Node 26. This is the most common upgrade blocker. - Check your dependencies'
enginesand your ownpackage.jsonenginesfield for a supported range. - Scan for deprecations in your startup logs after upgrading on a branch.
- For a framework (Next.js, etc.), upgrade through a framework release that supports the Node version rather than forcing it.
Verdict: safe to adopt for new work now; move production once 26 hits Active LTS in October (and your native addons are green on it).
What to adopt
| API | Verdict | When to reach for it |
|---|---|---|
crypto.parsePKCS12() | High value (where it applies) | You handle .p12/.pfx bundles, mTLS, or client certs |
SlidingWindowHistogram | Adopt | Any service that tracks its own latency for alerting/autoscaling |
fs.openAsBlobSync() | Handy | Startup/script code that needs a file as a Blob (not hot paths) |
node:sqlite undefined→NULL | Free win | You use the built-in SQLite driver |
The theme of 26.10 is small, sharp, practical: core keeps absorbing the things we used to reach for a dependency or a shell-out to do. None of it changes how you build a backend — all of it removes a little friction you'd stopped noticing.
Sources & further reading
Neel Shah
Contract full-stack developer building e-commerce and SaaS products with Next.js, Node.js and MongoDB from Ahmedabad, India.
Work with me →// contact
Working on something similar?
Happy to compare notes or help out — say hello.