In this blog post, you can explore the latest updates to Nitro, a powerful tool for building platform-agnostic server-side and backend applications. Nitro powers Nuxt and is accessible to everyone!
🌊 Native Response Streaming
Nitro 2.6 includes h3 1.8 for native Web Streams and Response support. It also includes runtime and type-safe request validation utils, event handlers with an object format, and more!
Read more:
H3 1.8 - Towards the Edge of the Web
New h3 release with web and plain adapters, web streams support, object syntax event handlers, typed event handler requests and more!
Web Streams are now supported for Node.js, Bun, Deno, Cloudflare Workers, and Vercel. For incompatible platforms, Nitro automatically reads the full stream (#1624) and converts it to a compatible format. We are closely collaborating with various teams to expand streaming support across all layers.
📦 Runtime-Key Compatible Export Conditions
Nitro 2.6 now automatically adds export conditions based on the Runtime Keys Proposal (#1401).
Using platform export conditions, Nitro can smartly use the appropriate build of your project libraries for each deployment target. Runtime-key compatible conditions have been introduced to the unjs packages, such as unjs/node-fetch-native and unjs/ofetch, with the intention of standardizing this approach across more packages.
For advanced use cases, you can override export conditions using the exportConditions
flag.
🧩 Async Context and Composition API
Nitro 2.6 introduces a new experimental useEvent()
API, enabling request events to be available in all async contexts without the need for explicitly passing them around to all functions as arguments (#1546).
This unblocks a new powerful design pattern, making server events accessible in any async context using useEvent()
. This feature was inspired by the Vue Composition API and is powered by unjs/unctx.
You can enable this feature by setting experimental.asyncContext: true
.
Note: Currently, this feature is supported for Bun and Node.js environments supporting AsyncLocalStorage
. We intend to increase platform coverage following AsyncContext
proposal.
🚀 Bundled Runtime Dependencies
When building applications for production, Nitro uses a tool called vercel/nft to trace the files used from the node_modules
folder and copies them into the .output/server/node_modules
folder. This method has advantages like working well with the native modules that cannot be bundled with rollup. However, there are also some disadvantages with this method:
- Additional data such as
package.json
files is copied to the output directory. - Increased startup overhead due to
node_modules
resolution. - Tracing allows us to optimize tree-shaking output up to the file level, as opposed to per-export tree-shaking.
- Potential issues with wrong export condition resolutions and hoisting when multiple versions are present.
Nitro 2.6 now automatically bundles its own dependencies, such as h3
, defu
, hookable
, ofetch
, etc. While many of these are small and compact, bundling them results in substantial performance improvements!
Measurement / Mode | External Deps (2.5) | Bundled Deps (2.6) |
---|---|---|
Number of Files | 60 | 9 |
Disk Size | 508K | 292K |
Disk Size (gzip) | 100Kb | 72Kb |
Start | 34.7ms | 18ms |
Fetch | 17.2 ms | 14.6ms |
Start to Fetch | 53.2ms | 34.7ms |
(Note: Benchmarks conducted on MBA M2 with Node.js v18.16.1. Refer to #1554 for benchmark script details.)
If you require the previous behavior for any reason, you can set the experimental.bundleRuntimeDependencies: false
configuration.
🪝 Error Capturing and Lifecycle Hooks
Nitro 2.6 introduces support for two new APIs: useNitroApp().captureError()
and event.captureError()
(#1463), and it automatically captures lifecycle errors!
You can use a Nitro plugin to hook into both auto-captured and manually captured errors. This enables easy integration with your custom logging infrastructure.
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('error', async (error, { event }) => {
console.error(`${event.path} Application error:`, error)
})
})
Furthermore, three global hooks have been introduced: request
, beforeResponse
, and afterResponse
(#1545), allowing for global interception of request lifecycles via Nitro plugins.
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
console.log('on request', event.path)
})
nitroApp.hooks.hook('beforeResponse', (event, { body }) => {
console.log('on response', event.path, { body })
})
nitroApp.hooks.hook('afterResponse', (event, { body }) => {
console.log('on after response', event.path, { body })
})
})
💾 Default Persistent Data Storage
Nitro provides a versatile Key-Value storage layer powered by unjs/unstorage. The default storage backend is in-memory, and you can configure and combine multiple storage backends for use in both development and production environments.
Nitro includes a standard cache:
storage, which is internally used for the cache layer and cached route rules. The data will be stored in .nitro/cache
(or .nuxt/cache
) during development. In Nitro 2.6, a new standard persistent storage, data:
(#1352), is introduced. This is available for both development and production modes with Node.js-compatible runtime presets.
To minimize bundle size, we have created a new compact and production-ready fs-lite
driver in unstorage. Data will be stored in the new .data/kv
directory within your project and persist across builds. It's ready to use out of the box with useStorage('data')
and you can always configure the data:
mount point with your desired backend and use it at runtime!
🗺️ Lighter Source Maps
In Nitro 2.6, the legacy source-map-support
polyfill has been removed, and an optimization has been introduced to automatically tree-shake sourcemap entries within the .output/server
directory by removing content maps of libraries from node_modules
. These changes significantly contribute to reducing the production bundle size.
To leverage native sourcemap support for Node.js, you can set the NODE_OPTIONS=--enable-source-maps
environment variable. This option will be automatically enabled for the Nuxt and Nitro CLI in upcoming releases.
If you need to disable new optimizations, you can use the experimental.sourcemapMinify: false
flag.
💻 Better CLI Experience
The Nitro CLI now supports the --preset
argument and --minify
/--no-minify
arguments for nitro build
(#1621).
Latest nitro CLI ships with the latest unjs/listhen, featuring Cloudflare Quick Tunnels (--tunnel
) powered by unjs/untun and QR Support (--host --qr
) powered by unjs/uqr.
The development server has been migrated to utilize unjs/httpxy, a TypeScript fork of http-proxy
. This move aims to enhance performance and stability for the future.
🔥 Firebase 2nd Gen Preset
Thanks to extensive community efforts, Nitro now supports Firebase 2nd gen functions (#1500).
While the default target remains v1, we highly recommend migrating by setting the firebase.gen: 2
config (Learn More).
🔓 Upgrade to 2.6
When upgrading, make sure to renew your project's package manager lockfile and node_modules
dir to avoid hosting issues.
✨ Other Enhancements
- The formatting of console output for the failing prerendering routes has been improved. (#1471).
event.waitUntil
API with Cloudflare integration (#1421).- Support for
ignore
to exclude scanned files (#1430). - Ability to ignore public assets with
ignore
options (#945). - New
iis
server preset (#1436). - New
prerender:config
,prerender:init
, andprerender:done
hooks (#1519). - Support for cached event handlers with variable
host
and headers (#1184). - Support for prerender query link exploration (#1474).
- Support for listening to UNIX sockets using
NITRO_UNIX_SOCKET
(#1201). - Cookie headers are auto-split (#1452).
There are many other improvements and bug fixes. You can find a detailed list of changes in the changelog.