mrkeyoor.com_
Sun 20 Sept 17:50 UTC
npmWeb Backendupdated 20 Sept 2026

nitro review

Nitro 3.0.260610-beta turns a Vite project into a server application and then emits an artifact for Node, serverless, or edge deployment presets. Files become H3 request handlers, middleware, plugins, tasks, and websocket endpoints. Its runtime also includes route rules, response and function caches, key-value storage, runtime configuration, and prerendering. This beta moves prerender work into an isolate, builds Vite service entries from an explicit module graph, and makes development fetch errors match production behavior.

Verdict

Nitro 3 beta took 46.4 seconds and 31 MB to install on our box, loaded through both module systems, bundled to 3 KB gzipped in the browser probe, and returned zero audit findings. Adopt it when Vite plus multiple deployment presets justify beta churn; use a smaller router for one ordinary Node service.

We installed it

Lab card: what happened when we installed nitroScreenshot of nitro documentation
Install✓ · 46.4s22 packages on disk · 31 MB
ImportESM import works · require() works · ESM package with exports map
Browser3 KBgzipped (7.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does nitro install cleanly?

Yes. In a fresh container with an empty cache, npm install nitro finished in 46 seconds, leaving 22 packages and 31 MB on disk. npm audit reported no known vulnerabilities.

How much does nitro add to a browser bundle?

3 KB gzipped (7.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does nitro work with both ESM and CommonJS?

Yes. Both import 'nitro' and require('nitro') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does nitro include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

nitro or h3: which should you use?

h3: Choose it for Nitro's web-standard handler model without the builder, presets, storage, and prerender layers. Nitro 3 beta took 46.4 seconds and 31 MB to install on our box, loaded through both module systems, bundled to 3 KB gzipped in the browser probe, and returned zero audit findings.

When should you not use nitro?

Your production dependency policy excludes prereleases; npm's latest Nitro package is explicitly 3.0.260610-beta

API stability2/5Nitro 3.0.260610-beta is a prerelease, and the official version 2 migration page tracks changes across package names, request handling, error construction, runtime imports, configuration, and preset identifiers. The June 10 beta also changed prerender execution and Vite service graph behavior. File-based routing remains recognizable, but framework integrations should expect more adjustments before a stable 3.0 release.
Docs4/5The Nitro 3 site documents quick start, file and configured routes, middleware, plugins, cache keys, storage drivers, runtime configuration, tasks, OpenAPI, websockets, Node output, and many provider presets. Its cache guide explicitly warns that unlisted headers are removed and that production cache storage defaults to memory. Some pages cover experimental or provider-specific behavior, so examples still need verification against the dated beta package.
Maintenance4/5nitrojs/nitro was pushed on 2026-08-26, is not archived, and has 11,146 stars. GitHub listed 621 open issues and pull requests together, a sizable queue for maintainers to sort. Release 3.0.260610-beta landed on 2026-06-10 with isolated prerendering, Vite graph work, generated-type fixes, and provider changes. Work is active, though the combination of a busy tracker and an unfinished major warrants upgrade testing.
Ecosystem5/5npm counted 16,578,780 Nitro downloads from 2026-08-19 through 2026-08-25, and the repository has 11,146 stars. The project connects Vite with H3, unstorage, ocache, crossws, database helpers, and deployment presets for major Node, serverless, and edge hosts. Its position beneath Nuxt expands real use, while adopters must filter version 2 answers and nitropack examples out of searches for the version 3 package.

Use it if

  • A Vite application needs API routes and server rendering in the same project, with one build pipeline owning both sides
  • The same handler code must produce artifacts for Node and one or more documented serverless or edge providers
  • Route rules, server plugins, prerendering, storage mounts, and response caching would otherwise require separate infrastructure packages
  • The team is ready to test a version 3 beta and can run smoke tests against the actual deployment preset
Skip it if

Setup reality

We installed Nitro 3.0.260610-beta in a fresh Node 22 Bookworm container in 46.4 seconds. The result occupied 31 MB across 22 packages, while npm audit found zero known vulnerabilities at all 4 severity levels. Nitro declares 14 direct dependencies and 8 peers, is 4328 KB unpacked, and includes TypeScript declarations. The package requires Node ^20.19.0 or >=22.12.0.

The package declares ESM and an exports map. Both require() and ESM import worked on our Node 22.23.2 box. An import-all esbuild browser probe produced 7.4 KB minified and 3 KB gzipped. That number describes the exported surface the probe retained; Nitro's builders, routes, storage, and provider adapters are still server-side concerns. Install only the peers needed by the chosen Vite, Rollup, queue, XML, or deployment integration.

A Nitro 3 project needs nitro.config.ts plus a Vite plugin or a direct server entry. Values in .env and .env.local load during development, while production expects the hosting platform's environment configuration. Runtime overrides use NITRO_ names and only replace keys already declared in runtimeConfig. The Node preset writes .output/server/index.mjs, reads PORT or NITRO_PORT, and defaults to port 3000.

Preset choice affects more than packaging. Production cache and root storage default to memory, so data disappears at restart and remains private to each instance unless you mount a shared driver. Cached handlers drop request headers unless they are listed in varies, which can mix tenant or locale responses under one key. Cloudflare bindings, Vercel features, WebSocket upgrades, filesystem access, and cold starts need tests on the target platform. The current beta's Vercel websocket support is still described as internal testing.

Patterns

Attach Nitro to a Vite project enable-vite-server

// vite.config.ts
import { defineConfig } from 'vite'
import { nitro } from 'nitro/vite'

export default defineConfig({
  plugins: [nitro()],
})

// nitro.config.ts
export default defineConfig({
  serverDir: './server',
})

Nitro 3 scans the configured serverDir for routes and plugins. Keep the config beside Vite so local and production builds resolve the same root.

Return JSON from a file route serve-json-route

// server/routes/api/status.get.ts
import { defineHandler } from 'nitro'

export default defineHandler(() => ({
  ok: true,
  version: 3,
}))

The .get suffix limits this file to GET requests. A file under routes/api maps to the /api URL prefix.

Read a JSON request body parse-request-body

// server/routes/api/orders.post.ts
import { defineHandler, HTTPError } from 'nitro'

export default defineHandler(async (event) => {
  const body = await event.req.json()
  if (typeof body?.sku !== 'string') {
    throw new HTTPError({ status: 400, message: 'sku is required' })
  }
  return saveOrder(body)
})

Nitro 3 exposes the web Request through event.req. Validate body shape before passing it to storage or business code.

Use dynamic path parameters read-route-params

// server/routes/api/repos/[owner]/[name].get.ts
import { defineHandler } from 'nitro'

export default defineHandler((event) => {
  const { owner, name } = event.context.params
  return { owner, name }
})

Bracketed folders create named path segments. Validate or encode the values before including them in a filesystem path or upstream URL.

Protect an API subtree with middleware guard-api-prefix

// server/middleware/auth.ts
import { defineHandler, HTTPError } from 'nitro'

export default defineHandler((event) => {
  if (!event.url.pathname.startsWith('/api/private/')) return
  const token = event.req.headers.get('authorization')
  if (!token) throw new HTTPError({ status: 401, message: 'missing token' })
  event.context.user = verifyToken(token)
})

Middleware can run for every request, so narrow the path before doing authentication work. Returning a response stops the chain.

Cache a localized GET response cache-get-handler

import { defineCachedHandler } from 'nitro/cache'

export default defineCachedHandler(
  async () => loadCatalog(),
  { maxAge: 300, swr: true, varies: ['accept-language'] },
)

Cached handlers preserve only headers named in varies. Add authorization, host, or tenant headers when any of them changes the response.

Cache and invalidate one function result invalidate-cached-function

import { defineCachedFunction } from 'nitro/cache'

const loadPrice = defineCachedFunction(
  async (sku: string) => database.price(sku),
  { name: 'price', maxAge: 60, getKey: (sku) => sku },
)

const first = await loadPrice('A-17')
await loadPrice.invalidate('A-17')

invalidate() resolves the same key through getKey. Every argument that affects the result must appear in that key.

Declare cache, redirect, and prerender rules configure-route-rules

// nitro.config.ts
export default defineConfig({
  routeRules: {
    '/news/**': { swr: 600 },
    '/api/live/**': { cache: false },
    '/company': { prerender: true },
    '/old/**': { redirect: { to: '/docs', status: 308 } },
  },
})

A preset may translate route rules into provider files or runtime behavior. Test the built target because provider capabilities differ.

Map production environment values into runtime config override-runtime-config

// nitro.config.ts
export default defineConfig({
  runtimeConfig: {
    billing: { endpoint: 'http://localhost:9000', token: '' },
  },
})

// server route
import { useRuntimeConfig } from 'nitro/runtime-config'
const config = useRuntimeConfig()

// production environment
// NITRO_BILLING_ENDPOINT=https://billing.example.com
// NITRO_BILLING_TOKEN=secret

Production overrides only apply to keys declared in runtimeConfig. Development .env loading does not replace the host's production secret configuration.

Mount Redis instead of process memory mount-persistent-storage

// nitro.config.ts
export default defineConfig({
  storage: {
    shared: {
      driver: 'redis',
      url: process.env.REDIS_URL,
    },
  },
})

// route or plugin
import { useStorage } from 'nitro/storage'
await useStorage('shared').setItem('jobs:42', { state: 'queued' })

Root and cache storage use memory in production unless configured otherwise. Memory disappears at restart and is separate in each instance.

Define a named server task register-background-task

// server/tasks/reports/daily.ts
import { defineTask } from 'nitro/task'

export default defineTask({
  meta: { name: 'reports:daily', description: 'Build the daily report' },
  async run({ payload }) {
    const count = await buildReport(payload.date)
    return { result: { count } }
  },
})

A task definition does not create a durable queue by itself. Choose a scheduler and retry policy that match the deployment provider.

Start the generated Node server build-node-output

NITRO_PRESET=node npx nitro build
PORT=8080 HOST=0.0.0.0 node .output/server/index.mjs

The Node preset defaults to port 3000 and handles SIGINT and SIGTERM. Ship the full .output directory, then exercise its health route before traffic moves.

Alternatives

PackageRegistryPick it when
h3npmChoose it for Nitro's web-standard handler model without the builder, presets, storage, and prerender layers
hononpmChoose it for a compact router that runs across several JavaScript runtimes with less build machinery
fastifynpmChoose it for a Node-only service that values plugins, hooks, and schema-driven request validation
expressnpmChoose it when an established Node middleware stack matters more than portable deployment output

More web backend guides

urllib3 · requests · ws · anyio · httpx · undici · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.