|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "What's new in Node.js v25.2: Web Storage, V8 14.1, permissions and more" |
| 4 | +description: Node.js v25.2.1 brings V8 14.1, experimental Web Storage, network permissions with --allow-net, and performance optimizations. Learn about the key features and what they mean for your applications. |
| 5 | +date: 2025-11-25 |
| 6 | +cover: /images/blog/nodejs-v25-whats-new/cover.png |
| 7 | +timeToRead: 9 |
| 8 | +author: ebenezer-don |
| 9 | +category: news |
| 10 | +featured: false |
| 11 | +--- |
| 12 | + |
| 13 | +Node.js v25.2.1, the current release as of November 17, 2025, represents the latest evolution in the v25 series. This release brings performance improvements, security features, and web standards alignment, though it also demonstrates how the Node.js team handles breaking changes. |
| 14 | + |
| 15 | +If you've been following the Node.js ecosystem, you know that odd-numbered releases like v25 aren't designated for long-term support (LTS). But that doesn't make them any less important. These releases serve as a testing ground for features that will eventually stabilize in future LTS versions. |
| 16 | + |
| 17 | +In this article, we'll break down what's new in the Node.js v25 series, explore the major features introduced, discuss the localStorage controversy that led to v25.2.1, and examine what these changes mean for your applications. |
| 18 | + |
| 19 | +# V8 14.1 engine upgrade {% #v8-14-1-engine-upgrade %} |
| 20 | + |
| 21 | +Node.js v25 ships with **V8 14.1**, Google's JavaScript and WebAssembly engine that powers Chrome. This upgrade brings several performance improvements that directly impact how your code runs. |
| 22 | + |
| 23 | +## Major JSON.stringify improvements |
| 24 | + |
| 25 | +One of the most significant improvements in V8 14.1 is the optimization of `JSON.stringify`. If you've ever worked with large objects or arrays that need to be serialized to JSON, you'll appreciate this. |
| 26 | + |
| 27 | +According to V8's benchmarks, `JSON.stringify` is now **more than 2x faster** on the JetStream2 benchmark. This matters when you're building APIs that return JSON responses, handling data transformations in serverless functions, or working with any system that relies heavily on JSON serialization. |
| 28 | + |
| 29 | +```javascript |
| 30 | +const largeObject = { |
| 31 | + users: Array.from({ length: 10000 }, (_, i) => ({ |
| 32 | + id: i, |
| 33 | + name: `User ${i}`, |
| 34 | + email: `user${i}@example.com`, |
| 35 | + metadata: { createdAt: new Date(), status: 'active' } |
| 36 | + })) |
| 37 | +}; |
| 38 | + |
| 39 | +// This operation is now significantly faster in Node.js v25 |
| 40 | +const jsonString = JSON.stringify(largeObject); |
| 41 | +``` |
| 42 | + |
| 43 | +## WebAssembly and JIT pipeline optimizations |
| 44 | + |
| 45 | +V8 14.1 also brings ongoing improvements to WebAssembly support and the Just-In-Time (JIT) compilation pipeline. These changes might not be immediately visible in your day-to-day code, but they contribute to better overall runtime performance. |
| 46 | + |
| 47 | +# Native Web Storage API support {% #native-web-storage-api-support %} |
| 48 | + |
| 49 | +One of the challenges when building full-stack JavaScript applications has been the gap between browser and server-side APIs. Node.js v25 narrows this gap by enabling **Web Storage API** by default. |
| 50 | + |
| 51 | +This means you can now use `localStorage` and `sessionStorage` directly in Node.js, just like you would in the browser. |
| 52 | + |
| 53 | +```javascript |
| 54 | +// Store data in localStorage |
| 55 | +localStorage.setItem('user', JSON.stringify({ name: 'Jane Doe' })); |
| 56 | + |
| 57 | +// Retrieve data |
| 58 | +const user = JSON.parse(localStorage.getItem('user')); |
| 59 | +console.log(user.name); // Output: Jane Doe |
| 60 | + |
| 61 | +// Clear storage |
| 62 | +localStorage.removeItem('user'); |
| 63 | +``` |
| 64 | + |
| 65 | +## Why this matters |
| 66 | + |
| 67 | +For developers working with frameworks like Next.js, Remix, or Astro, this standardization makes it easier to share code between client and server. You can write utilities that work in both environments without needing to check which context you're in or use different APIs. |
| 68 | + |
| 69 | +## The localStorage controversy |
| 70 | + |
| 71 | +Here's where things get interesting. The Web Storage API in Node.js is still marked as **experimental**, and v25.2.0 introduced a change that caused significant problems in the ecosystem. |
| 72 | + |
| 73 | +In v25.2.0, Node.js started throwing errors when accessing `localStorage` without a configured storage path. The intent was to align with web specifications, but this broke popular tools like Jest, Vite, and other frameworks that relied on the previous behavior. |
| 74 | + |
| 75 | +Developers upgrading to v25.2.0 encountered errors like: |
| 76 | + |
| 77 | +``` |
| 78 | +SecurityError: Cannot initialize local storage without a `--localstorage-file` path |
| 79 | +``` |
| 80 | + |
| 81 | +This broke test suites and build processes across the ecosystem. The Node.js team received enough feedback that they decided this change was too breaking for a semver-minor release. |
| 82 | + |
| 83 | +**Node.js v25.2.1** (released November 17, 2025) reverted this behavior. The throwing behavior has been postponed to Node.js v26.0.0, giving developers more time to adapt to the change. |
| 84 | + |
| 85 | +This episode highlights an important aspect of non-LTS releases: they're testing grounds for features, and sometimes those features need to be adjusted based on real-world feedback. If you're using Web Storage in Node.js v25, it works, but be prepared for stricter behavior in future versions. |
| 86 | + |
| 87 | +# Network permissions with --allow-net {% #network-permissions-with-allow-net %} |
| 88 | + |
| 89 | +Node.js has traditionally been permissive when it comes to what your code can access. If you run a script, it can read files, make network requests, and interact with the system without restrictions. This flexibility is convenient, but it also poses security risks, especially when running untrusted code. |
| 90 | + |
| 91 | +Node.js v25 expands the **permission model** with a new `--allow-net` flag, giving you finer control over network access. |
| 92 | + |
| 93 | +## How it works |
| 94 | + |
| 95 | +The `--allow-net` flag allows you to explicitly grant network permissions to your application. This is similar to Deno's security model, which requires explicit permissions for different operations. |
| 96 | + |
| 97 | +```bash |
| 98 | +# Allow network access to specific hosts (requires --permission flag) |
| 99 | +node --permission --allow-net=api.example.com,cdn.example.com server.js |
| 100 | + |
| 101 | +# Allow all network access (requires --permission flag) |
| 102 | +node --permission --allow-net server.js |
| 103 | +``` |
| 104 | + |
| 105 | +This approach encourages **secure-by-default** applications. By requiring explicit permission for network operations, you reduce the risk of unintended behavior or vulnerabilities in dependencies that might make unauthorized network requests. |
| 106 | + |
| 107 | +For production systems, multi-tenant environments, or any scenario where you need to limit what your code can do, this is a valuable addition. |
| 108 | + |
| 109 | +# Built-in Uint8Array base64/hex conversion {% #built-in-uint8array-base64-hex-conversion %} |
| 110 | + |
| 111 | +Working with binary data has always been a bit cumbersome in JavaScript. Converting between `Uint8Array` and formats like base64 or hex usually required either using the `Buffer` API or pulling in a third-party library. |
| 112 | + |
| 113 | +Node.js v25 changes this by adding **built-in methods** to convert `Uint8Array` directly to base64 or hex strings. |
| 114 | + |
| 115 | +```javascript |
| 116 | +// Create a Uint8Array |
| 117 | +const data = new Uint8Array([72, 101, 108, 108, 111]); |
| 118 | + |
| 119 | +// Convert to base64 |
| 120 | +const base64 = data.toBase64(); |
| 121 | +console.log(base64); // Output: SGVsbG8= |
| 122 | + |
| 123 | +// Convert to hex |
| 124 | +const hex = data.toHex(); |
| 125 | +console.log(hex); // Output: 48656c6c6f |
| 126 | +``` |
| 127 | + |
| 128 | +## Practical use cases |
| 129 | + |
| 130 | +This is useful when working with: |
| 131 | + |
| 132 | +- **File uploads:** Encoding binary data for transmission |
| 133 | +- **Cryptographic operations:** Converting hash outputs to readable formats |
| 134 | +- **API integrations:** Many APIs expect base64-encoded binary data |
| 135 | +- **WebSocket communication:** Encoding binary frames |
| 136 | + |
| 137 | +By making these conversions native, Node.js removes the dependency on external libraries and improves performance since these operations are now handled at the engine level. |
| 138 | + |
| 139 | +# WebAssembly JSPI support {% #webassembly-jspi-support %} |
| 140 | + |
| 141 | +WebAssembly continues to evolve, and Node.js v25 enables **JavaScript Promise Integration (JSPI)** for WebAssembly modules. |
| 142 | + |
| 143 | +JSPI allows synchronous WebAssembly code to interact with asynchronous JavaScript APIs. This is important because WebAssembly is inherently synchronous, but JavaScript's ecosystem is heavily async-based. |
| 144 | + |
| 145 | +## Why this is useful |
| 146 | + |
| 147 | +Before JSPI, if you wanted to call an async JavaScript function from WebAssembly, you had to use complex workarounds involving callbacks or state machines. JSPI simplifies this by letting WebAssembly suspend and resume execution while waiting for async operations. |
| 148 | + |
| 149 | +This is valuable for: |
| 150 | + |
| 151 | +- **High-performance computing:** Running CPU-intensive operations (like scientific simulations or video encoding) in WebAssembly while interacting with async I/O |
| 152 | +- **Game engines:** Integrating WebAssembly-based game logic with async network calls for multiplayer features or asset loading |
| 153 | +- **Image and video processing:** Using WebAssembly for fast image manipulation (filters, compression, format conversion) while reading/writing files asynchronously |
| 154 | +- **Data processing:** Combining WebAssembly's speed for parsing large datasets with JavaScript's async file system or database operations |
| 155 | + |
| 156 | +# Portable compile cache {% #portable-compile-cache %} |
| 157 | + |
| 158 | +Node.js compiles JavaScript code to bytecode before executing it. This compilation step takes time, especially for large applications. To speed up subsequent runs, Node.js caches the compiled bytecode. |
| 159 | + |
| 160 | +Node.js v25 introduces **portable compile cache**, which allows caches to be reused even when you move your project directory. Previously, compile caches used absolute file paths, meaning if you moved your project or deployed it to a different environment, the cache couldn't be reused. |
| 161 | + |
| 162 | +You can enable portable caching in two ways: |
| 163 | + |
| 164 | +```bash |
| 165 | +# Enable portable compile cache via environment variable |
| 166 | +NODE_COMPILE_CACHE_PORTABLE=1 node app.js |
| 167 | +``` |
| 168 | + |
| 169 | +Or programmatically: |
| 170 | + |
| 171 | +```javascript |
| 172 | +// Enable portable compile cache via API |
| 173 | +module.enableCompileCache({ |
| 174 | + directory: '/path/to/cache/dir', |
| 175 | + portable: true |
| 176 | +}); |
| 177 | +``` |
| 178 | + |
| 179 | +## Impact on serverless and containers |
| 180 | + |
| 181 | +This feature is especially useful for: |
| 182 | + |
| 183 | +- **Serverless functions:** Reuse caches across different function invocations |
| 184 | +- **Docker containers:** Share caches between container builds |
| 185 | +- **CI/CD pipelines:** Speed up test runs by reusing compilation artifacts |
| 186 | + |
| 187 | +# Type stripping marked as stable {% #type-stripping-marked-as-stable %} |
| 188 | + |
| 189 | +Node.js v25.2 marks **type stripping** as stable. This feature allows you to run TypeScript-like syntax directly in Node.js without needing a separate build step. |
| 190 | + |
| 191 | +Type stripping removes type annotations from your code at runtime, but it doesn't perform type checking. You still need the TypeScript compiler (`tsc`) for that. |
| 192 | + |
| 193 | +```typescript |
| 194 | +// This works natively in Node.js v25.2 |
| 195 | +function greet(name: string): void { |
| 196 | + console.log(`Hello, ${name}!`); |
| 197 | +} |
| 198 | + |
| 199 | +greet('World'); |
| 200 | +``` |
| 201 | + |
| 202 | +## When to use it |
| 203 | + |
| 204 | +Type stripping is ideal for: |
| 205 | + |
| 206 | +- **Rapid prototyping:** Write TypeScript without setting up a build pipeline |
| 207 | +- **Small scripts:** Run one-off TypeScript files without compilation |
| 208 | +- **Learning TypeScript:** Experiment with TypeScript syntax without tooling overhead |
| 209 | + |
| 210 | +For production applications, you'll likely still want a proper TypeScript setup with full type checking, but type stripping removes friction for simpler use cases. |
| 211 | + |
| 212 | +# Performance improvements {% #performance-improvements %} |
| 213 | + |
| 214 | +Beyond the headline features, Node.js v25.2 includes several performance optimizations: |
| 215 | + |
| 216 | +## Buffer concatenation speedup |
| 217 | + |
| 218 | +The implementation of buffer concatenation has been improved using `TypedArray#set`, which reduces the time needed to combine multiple buffers. This is useful when working with streams or assembling data from multiple sources. |
| 219 | + |
| 220 | +## Console logging optimization |
| 221 | + |
| 222 | +Single-string logging in the `console` module has been optimized. If you're doing a lot of logging (especially in development), you'll notice faster output. |
| 223 | + |
| 224 | +## Crypto argument validation |
| 225 | + |
| 226 | +The crypto module now performs argument validation more efficiently in fast paths, reducing overhead for common cryptographic operations. |
| 227 | + |
| 228 | +# What's specific to v25.2.1 {% #whats-specific-to-v25-2-1 %} |
| 229 | + |
| 230 | +While most of the features we've discussed came with v25.0.0 or v25.2.0, v25.2.1 includes a few specific changes worth noting. |
| 231 | + |
| 232 | +## Crypto fix: RSA-PSS saltLength default |
| 233 | + |
| 234 | +v25.2.1 fixed an issue where the documented RSA-PSS `saltLength` default wasn't being used correctly. If you're working with RSA-PSS signatures in the crypto module, this ensures the implementation matches the documented behavior. |
| 235 | + |
| 236 | +```javascript |
| 237 | +const crypto = require('crypto'); |
| 238 | + |
| 239 | +// The default saltLength now matches documentation |
| 240 | +const sign = crypto.createSign('RSA-SHA256'); |
| 241 | +// ... |
| 242 | +``` |
| 243 | + |
| 244 | +## V8 engine backport |
| 245 | + |
| 246 | +The release includes a critical V8 engine patch that was backported to improve stability and performance. While the specific details are technical, these backports typically address edge cases or bugs discovered in production environments. |
| 247 | + |
| 248 | +## Documentation clarity on Web Storage |
| 249 | + |
| 250 | +Beyond reverting the throwing behavior, v25.2.1 also clarified the experimental status of Web Storage across documentation, source code, and library code. This makes it clearer to developers that while the feature is available, it's still evolving and may change in future releases. |
| 251 | + |
| 252 | +# Deprecated API removals {% #deprecated-api-removals %} |
| 253 | + |
| 254 | +Node.js v25 continues the tradition of cleaning up deprecated APIs. Several long-deprecated features have been removed: |
| 255 | + |
| 256 | +## SlowBuffer removal |
| 257 | + |
| 258 | +The `SlowBuffer` object has been completely removed due to security vulnerabilities. If you were still using it, you should migrate to `Buffer.allocUnsafeSlow()`. |
| 259 | + |
| 260 | +```javascript |
| 261 | +// Old (removed in v25) |
| 262 | +const buf = new SlowBuffer(10); |
| 263 | + |
| 264 | +// New |
| 265 | +const buf = Buffer.allocUnsafeSlow(10); |
| 266 | +``` |
| 267 | + |
| 268 | +## Deprecated crypto options |
| 269 | + |
| 270 | +Certain crypto options, like default lengths for `shake128` and `shake256`, are now deprecated at runtime and will be removed in future versions. |
| 271 | + |
| 272 | +## Filesystem constants |
| 273 | + |
| 274 | +Filesystem permission constants (`fs.F_OK`, `fs.R_OK`, `fs.W_OK`, `fs.X_OK`) have been removed. These were already marked as deprecated, and you should use the corresponding constants from the `fs.constants` object instead. |
| 275 | + |
| 276 | +```javascript |
| 277 | +// Old (removed in v25) |
| 278 | +fs.access(path, fs.R_OK, callback); |
| 279 | + |
| 280 | +// New |
| 281 | +fs.access(path, fs.constants.R_OK, callback); |
| 282 | +``` |
| 283 | + |
| 284 | +# When should you use Node.js v25? {% #when-should-you-use-nodejs-v25 %} |
| 285 | + |
| 286 | +Node.js v25 is a **Current** release, not an LTS release. This means it's designed for developers who want to experiment with the latest features and provide feedback, but it's not recommended for production applications that require long-term stability. |
| 287 | + |
| 288 | +## Use Node.js v25 if: |
| 289 | + |
| 290 | +- You're building side projects or prototypes |
| 291 | +- You want to test new features before they land in LTS |
| 292 | +- You're contributing to the Node.js ecosystem and need to validate changes |
| 293 | +- You're working on applications that can tolerate breaking changes |
| 294 | + |
| 295 | +## Stick with LTS if: |
| 296 | + |
| 297 | +- You're running production applications |
| 298 | +- You need long-term support and security updates |
| 299 | +- Your team relies on ecosystem stability |
| 300 | +- You're working in enterprise environments with strict upgrade policies |
| 301 | + |
| 302 | +The next LTS release is expected in October 2026 with Node.js v26, which will incorporate many of the features tested in v25. |
| 303 | + |
| 304 | + |
| 305 | +# Conclusion |
| 306 | + |
| 307 | +Node.js v25.2.1 represents a meaningful step forward for the runtime, though it also demonstrates the value of iterative development. The combination of performance improvements, security features, and web standards alignment shows that Node.js continues to evolve in response to developer needs. |
| 308 | + |
| 309 | +While v25 isn't an LTS release, it gives us a preview of what's coming and an opportunity to provide feedback before these features stabilize. Whether you're working with high-performance JSON operations, building cross-platform applications that benefit from Web Storage, or experimenting with WebAssembly, there's something here worth exploring. |
| 310 | + |
| 311 | +If you're interested in learning more about modern JavaScript runtimes, you might enjoy reading about [Deno vs Bun](https://appwrite.io/blog/post/deno-vs-bun-javascript-runtime) or exploring [how Deno 2.0 works with Appwrite Functions](https://appwrite.io/blog/post/deno-2-appwrite-functions). |
| 312 | + |
| 313 | +You can reach out to us on [Discord](https://appwrite.io/discord) if you have any questions or feedback. |
| 314 | + |
| 315 | +# More resources |
| 316 | + |
| 317 | +- [Node.js v25.2.1 release notes](https://nodejs.org/en/blog/release/v25.2.1) |
| 318 | +- [Node.js v25.2.0 release notes](https://nodejs.org/en/blog/release/v25.2.0) |
| 319 | +- [Why you need to try the new Bun function runtime](/blog/post/why-you-need-to-try-the-new-bun-runtime) |
| 320 | +- [Announcing Deno support on Appwrite Cloud](/blog/post/deno-runtime-announcment) |
0 commit comments