Subchapter 3.3
references/core-cache.mdMarkdown5 KBView on GitHub
nitro/cachedefineCachedHandler works like defineHandler plus a cache-options argument.
import { defineCachedHandler } from "nitro/cache";
export default defineCachedHandler((event) => {
return "I am cached for an hour";
}, { maxAge: 60 * 60 });Behavior:
GET/HEAD are cached; other methods bypass and call the handler.etag, last-modified, and cache-control headers, plus 304 Not Modified for conditional requests.>= 400 or undefined body are not cached.varies to keep specific ones.Cache any async function (e.g. an upstream API call) and reuse it across handlers.
import { defineHandler, type H3Event } from "nitro";
import { defineCachedFunction } from "nitro/cache";
const cachedGHStars = defineCachedFunction(async (repo: string) => {
const data = await fetch(`https://api.github.com/repos/${repo}`).then((r) => r.json());
return data.stargazers_count;
}, {
maxAge: 60 * 60,
name: "ghStars",
getKey: (repo: string) => repo,
});
export default defineHandler(async (event) => {
const { repo } = event.context.params!;
const stars = await cachedGHStars(repo).catch(() => 0);
return { repo, stars };
});On edge workers the instance is destroyed after each request. Pass
eventas the first argument to the cached function (and togetKey) so Nitro can useevent.waitUntilto finish background revalidation.
Cached values are JSON-serialized — don’t return Symbols, Maps, or Sets.
Wrap handlers in caching by glob pattern without touching handler code:
import { defineConfig } from "nitro";
export default defineConfig({
storage: { redis: { driver: "redis", url: "redis://localhost:6379" } },
routeRules: {
"/blog/**": { swr: true }, // SWR, default maxAge
"/api/**": { swr: 3600 }, // SWR, 1h
"/heavy/**": { cache: { maxAge: 3600, base: "redis" } }, // custom mountpoint
"/api/realtime/**": { cache: false }, // disable
},
});Route-rule handlers use the group nitro/route-rules.
Shared (defineCachedHandler + defineCachedFunction):
| Option | Default | Description |
|---|---|---|
maxAge | 1 | Seconds the cache is valid. |
swr | true | Serve stale while revalidating in background. |
staleMaxAge | 0 | Extra seconds a stale value is served. -1 keeps serving stale during refresh. |
base | cache | Storage mountpoint. |
name | inferred | Cache namespace. |
group | nitro/handlers / nitro/functions | Key group. |
getKey(...args) | hash | Compute cache key. |
integrity | code hash | Invalidate when changed. |
shouldInvalidateCache / shouldBypassCache | — | Per-call predicates. |
onError(err) | log | Custom error handling. |
Handler-only: varies (header names to include in the key / keep on request), headersOnly (only do conditional-request handling). Function-only: transform(entry, ...args), validate(entry, ...args).
Key pattern: `${base}:${group}:${name}:${getKey(...args)}.json`.
// Every cached function exposes .invalidate()
await cachedGHStars("unjs/nitro"); // populate
await cachedGHStars.invalidate("unjs/nitro"); // remove
// Or invalidate from anywhere with matching options
import { invalidateCache } from "ocache";
await invalidateCache({
options: { name: "ghStars", group: "nitro/functions", getKey: (repo: string) => repo },
args: ["unjs/nitro"],
});The name, group, base, and getKey passed to invalidateCache must match the definition exactly, or a different key is computed.
nitro/cache; swr is enabled by default (maxAge defaults to 1s).defineCachedFunction is for caching reusable logic; defineCachedHandler caches whole responses.event through cached functions for waitUntil-backed refresh.cache/swr) for declarative, app-wide caching strategies.