From the server, storage and caching parts of your Getting started III note, and the response fragmentation part of Getting started I.
useStorage() with drivers per environment), unenv (Node polyfills and stubs per preset).server/api/ gets /api, server/routes/ doesn't; .get/.post suffixes; [id] and [...slug]; returned objects become JSON; useFetch is typed from them. shared/utils and shared/types are auto-imported on both sides.getQuery, getRouterParam, readBody, getHeader, getCookie; validated versions getValidatedQuery, getValidatedRouterParams, readValidatedBody (400 on failure); errors with throw createError({ status, statusText }).event.$fetch: an internal call that carries the incoming request's headers and context; plain $fetch on the server starts a fresh, anonymous request.event.context); plugins run once at start-up and register hooks: request, beforeResponse, afterResponse, error, render:html, render:response, close.render:html and turns the response into JSON { head, body } in render:response, so an old system can embed parts rendered by Nuxt.defineCachedEventHandler (one handler, with getKey, varies, shouldBypassCache), defineCachedFunction (one expensive call, keyed by arguments). Request headers are dropped unless listed in varies. Memory per instance by default; mount Redis or KV to share.server/utils: auto-imported across the server, the place for helpers like a defineAuthenticatedHandler wrapper."await getBody(event)", "getValidatedRourerParam" and "use throwError() to throw": the functions are readBody(event), getValidatedRouterParams(event, validate) (plural) and throw createError({ status, statusText }). There's no getBody or throwError in h3.
"If I want to use any header, have to define that in the vary option": the option is varies. And "useStorage('provider')": the argument is the name of a mount point you configured under nitro.storage (or devStorage), like useStorage('redis'), not the driver or provider itself.
"I can't return a value": you can, but whatever server middleware returns ends the request and is sent as the response, skipping the route handler. That's why middleware should only add to event.context or throw.
server/api/products/[id].get.ts with getValidatedRouterParams and Zod, call it with a bad id and read the 400, then check that useFetch('/api/products/1') is typed.request and afterResponse; load a page and an API route and compare.defineCachedEventHandler, read a custom header inside it, and see it missing until you add it to varies.?fragment=1."Middleware runs on every request before the handler and prepares the request, typically verifying the session and putting the user on event.context; it shouldn't return anything. A Nitro plugin runs once when the server starts and registers hooks for every request: afterResponse for timing, error for error tracking, render:html and render:response to change rendered pages."
"With response fragmentation: the old system requests the fragment from Nuxt and inserts it. A Nitro plugin removes what the host page already has in render:html and turns the response into JSON with a head part and a body part in render:response. Asset URLs have to be absolute, CSS scoped, shared fragments cached, and the old system needs a timeout and fallback so Nuxt can't take it down."
Nothing yet. Notes filed here show up on this page and the section overview.
Routing, data and state
What happens on a NuxtLink click, route middleware versus server middleware, $fetch versus useFetch versus useAsyncData, keys, await versus lazy versus server false, cookies during SSR, what useState is and isn't, errors on the server and in the browser, and SEO tags.
Performance, assets and testing
Where to start on a slow page, lazy components versus lazy hydration, NuxtLink prefetching, Nuxt Image, Fonts and Scripts, app/assets versus public, serving .output/public in Kubernetes and on Workers, fonts and layout shift, LCP, and Nuxt's test environments.