Server Routes & Resilience — Series 3

Preview — 3 of 10 questions

What does this routeRules block accomplish, and how is it different from configuring caching inside each individual server route handler?

javascript
// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/products/**': { swr: 3600 },
    '/admin/**': { ssr: false },
    '/api/legacy/**': { redirect: '/api/v2' },
  },
})
ArouteRules is a centralized, declarative place to configure rendering/caching/redirect behavior per URL pattern, applied by Nitro at the routing layer itself — rather than scattering caching logic, SSR-disabling logic, or redirect logic across many individual route handlers or page components, one place declares "the homepage is always prerendered," "product pages use stale-while-revalidate caching for an hour," "the admin section skips SSR entirely," and "these legacy API paths redirect." This gives a single, scannable overview of an app's whole rendering/caching strategy across every route, rather than that strategy being implicit and scattered across the codebase
BrouteRules only affects development-mode behavior — production builds ignore this configuration entirely and require the same rules to be duplicated inside each route's own handler
CrouteRules can only be applied to page routes under pages/; server API routes under server/api/ are entirely unaffected by any routeRules configuration
DEach entry in routeRules requires a corresponding middleware file to actually be enforced — the object alone has no effect without additional wiring

What does wrapping this handler accomplish, compared to a plain defineEventHandler?

javascript
// server/api/trending-products.get.ts
export default defineCachedEventHandler(
  async (event) => {
    return await computeExpensiveTrendingList() // takes ~800ms
  },
  {
    maxAge: 60 * 5, // cache for 5 minutes
    swr: true,
  }
)
AdefineCachedEventHandler only caches the response in the browser's cache via response headers — the server-side computation still runs fresh on every single request regardless
BThis is purely a development-mode debugging tool — maxAge and swr have no effect in a production deployment
CdefineCachedEventHandler caches the handler's actual return value server-side (using Nitro's storage layer), keyed by the request — for maxAge seconds, subsequent requests skip re-running the expensive computeExpensiveTrendingList() entirely and are served the cached result directly, and swr: true (stale-while-revalidate) means that once the cache technically expires, a request still gets the (now-stale) cached value immediately while the expensive computation re-runs in the background to refresh the cache for next time — avoiding ever making a real user wait for the full 800ms computation, even right at the cache expiration boundary
Dswr: true makes the handler run the computation in parallel across multiple server instances simultaneously, using the fastest result

Why use this instead of a plain $fetch call inside a server-rendered composable that needs to call another of the app's own authenticated API routes?

javascript
// A composable called during SSR
export function useUserOrders() {
  const requestFetch = useRequestFetch()
  return useAsyncData('user-orders', () => requestFetch('/api/orders'))
}
AuseRequestFetch is purely a TypeScript convenience — it has identical runtime behavior to calling the global $fetch directly
BrequestFetch, obtained via useRequestFetch(), automatically forwards the original incoming request's relevant context (cookies, headers) to the fetch it makes — meaning a call to /api/orders correctly carries along whatever authentication cookie the original page request itself came in with, without that having to be manually extracted (via useRequestHeaders, as covered elsewhere) and re-attached by hand. A plain $fetch call during SSR has no such automatic context forwarding — it would make a genuinely unauthenticated request to /api/orders, missing the cookie the original page request had
CuseRequestFetch can only be used inside server/ directory files — calling it from an app-side composable throws
DuseRequestFetch bypasses Nitro entirely and connects directly to the underlying database, skipping the API route's own handler logic

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.