404 Not Found

404 Not Found


nginx

Nitro Server Engine

Charlie needs to add a caching layer to MegaShop—frequent database queries on the page listing millions of products are too slow. Bob discovered that storage methods differ across deployment environments (Node, Docker, Vercel). The Nitro engine provides a unified storage abstraction layer and caching API, allowing a single codebase to work across all environments.

1. What You'll Learn


2. A True Story of an Architect

(1) Pain Point: Queries on millions of products are overwhelming the database

The MegaShop product detail page performs 2,000 database queries per second with a response time of 500 ms. Bob added a Redis cache, but the code is tightly coupled with the deployment environment—it uses file-based caching locally and Redis in production, requiring two separate sets of code changes.

(2) Solutions for the Nitro Storage Abstraction Layer

Nitro's useStorage() unified storage interface lets you switch drivers without changing your code:

TYPESCRIPT
// Same code, different driver based on preset
const storage = useStorage('products')
await storage.setItem('product:123', productData)

(3) Benefits: Unified interface + 5 ms response time

The storage layer code is standardized: memory/fs is used for local development, and Redis/KV is used in production. When a product cache hit occurs, the response time is 5 ms, and database queries are reduced by 95%.


3. The Nitro Architecture

(1) An Overview of the Nitro Architecture

100%
graph TB
    A[Nitro Engine] --> B[Rollup Build]
    A --> C[H3 HTTP Framework]
    A --> D[Storage Abstraction]
    A --> E[Cache System]
    A --> F[Hook System]
    A --> G[Multi-Preset Deploy]

    B --> B1[Server bundle]
    B --> B2[Tree-shaking unused code]

    C --> C1[defineEventHandler]
    C --> C2[Router / Middleware]

    D --> D1[Memory Driver]
    D --> D2[FileSystem Driver]
    D --> D3[Redis Driver]
    D --> D4[Cloud KV Driver]

    E --> E1[cachedEventHandler]
    E --> E2[defineCachedFunction]

    G --> G1[Node Server]
    G --> G2[Vercel / Cloudflare]
    G --> G3[Docker / Lambda]

(2) Comparison of Nitro Presets

Presets Runtime Environment Storage Driver Deployment Method
node-server Node.js fs/memory/redis PM2/Docker
Vercel Vercel Serverless Vercel KV git push
vercel-edge Vercel Edge Edge KV git push
cloudflare-pages Cloudflare Workers KV/R2 wrangler deploy
netlify Netlify Functions Netlify Blobs git push
bun Bun runtime fs/memory bun run

(1) ▶ Example: Configuring a Nitro Preset

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: process.env.DEPLOY_TARGET || 'node-server',
    compressPublicAssets: true,
    storage: {
      // Development: memory driver
      cache: { driver: 'memory' },
      // Production: configured via environment
      products: { driver: process.env.STORAGE_DRIVER || 'memory' }
    }
  }
})

Output:

TEXT
// Execution Successful

4. Storage Abstraction Layer

(1) useStorage API

Method Description Example
setItem(key, value) Write await storage.setItem('product:1', data)
getItem(key) Read await storage.getItem('product:1')
removeItem(key) Delete await storage.removeItem('product:1')
getKeys(base) List keys await storage.getKeys('product:')
hasItem(key) Exists await storage.hasItem('product:1')
clear(base) Clear await storage.clear('product:')

(1) ▶ Example: Product Cache Storage

TYPESCRIPT
// server/api/products/[id].get.ts - With storage cache
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')
  const storage = useStorage('products')

  // Try cache first
  const cached = await storage.getItem(`product:${id}`)
  if (cached) return cached

  // Cache miss: fetch from database
  const product = await fetchProductFromDB(Number(id))
  if (!product) {
    throw createError({ statusCode: 404, message: 'Product not found' })
  }

  // Store in cache with TTL (1 hour)
  await storage.setItem(`product:${id}`, product, { ttl: 3600 })

  return product
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Redis Storage Configuration

TYPESCRIPT
// nuxt.config.ts - Production Redis storage
export default defineNuxtConfig({
  nitro: {
    storage: {
      products: {
        driver: 'redis',
        url: process.env.REDIS_URL || 'redis://localhost:6379',
        prefix: 'megashop:products:'
      },
      cache: {
        driver: 'redis',
        url: process.env.REDIS_URL || 'redis://localhost:6379',
        prefix: 'megashop:cache:'
      }
    }
  }
})

Output:

TEXT
// Execution Successful

(2) Comparison of Storage Drivers

Drive Speed Durability Distributed Use Cases
memory ⚡⚡⚡ ❌ Lost on reboot Development/Testing
fs ⚡⚡ ✅ Local Standalone deployment
redis ⚡⚡ Production Cluster
cloudflare-kv Cloudflare
vercel-kv Vercel

5. Caching API

(1) ▶ Example: cachedEventHandler

TYPESCRIPT
// server/api/products/featured.get.ts
export default cachedEventHandler(
  async () => {
    // This handler result is cached
    const products = await $fetch('/api/internal/products/featured')
    return products
  },
  {
    maxAge: 60 * 60,           // Cache for 1 hour
    swr: true,                  // Serve stale while revalidating
    staleMaxAge: 60 * 60 * 4,  // Stale valid for 4 hours
    getKey: () => 'featured-products',
    varies: ['Accept-Language'] // Per-language cache
  }
)

Output:

TEXT
// Execution Successful

(2) ▶ Example: defineCachedFunction

TYPESCRIPT
// server/utils/cachedProduct.ts
export const getCachedProduct = defineCachedFunction(
  async (id: number) => {
    return await fetchProductFromDB(id)
  },
  {
    maxAge: 60 * 60,
    swr: true,
    getKey: (id) => `product:${id}`,
    name: 'cachedProduct'
  }
)

// Usage in API handler
// server/api/products/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = Number(getRouterParam(event, 'id'))
  return await getCachedProduct(id)
})

Output:

TEXT
// Execution Successful

6. Event Hooks

(1) Nitro Lifecycle Hooks

Hook Triggering Condition Purpose
close Stop Service Release Resources
| error | Unhandled error | Error reporting |

| request | Request Start | Log/Authentication | | response | Response Sent | Performance Statistics | | beforeResponse | Before Response | Modify Response |

(1) ▶ Example: Inventory Update Event Hook

TYPESCRIPT
// server/plugins/stock.ts
export default defineNitroPlugin((nitroApp) => {
  // Listen for stock update events
  nitroApp.hooks.hook('request', async (event) => {
    const url = getRequestURL(event)
    if (url.pathname === '/api/orders' && getMethod(event) === 'POST') {
      // Before order: check stock availability
      const body = await readBody(event)
      const outOfStock = await checkStockAvailability(body.items)
      if (outOfStock.length > 0) {
        throw createError({
          statusCode: 400,
          message: `Items out of stock: ${outOfStock.join(', ')}`
        })
      }
    }
  })

  // After order: invalidate product cache
  nitroApp.hooks.hook('afterResponse', async (event) => {
    const url = getRequestURL(event)
    if (url.pathname === '/api/orders' && getMethod(event) === 'POST') {
      const body = await readBody(event)
      const storage = useStorage('products')
      // Invalidate cached products
      for (const item of body.items) {
        await storage.removeItem(`product:${item.productId}`)
      }
    }
  })
})

Output:

TEXT
// Execution Successful

7. Comprehensive Example: MegaShop Product Caching System

TYPESCRIPT
// nuxt.config.ts - Production Nitro config
export default defineNuxtConfig({
  nitro: {
    preset: process.env.DEPLOY_TARGET || 'node-server',
    compressPublicAssets: true,
    storage: {
      products: {
        driver: process.env.NODE_ENV === 'production' ? 'redis' : 'memory',
        url: process.env.REDIS_URL
      }
    },
    cache: {
      pages: ['/products/', '/categories/']
    }
  }
})
TYPESCRIPT
// server/api/products/[id].get.ts - Full caching strategy
export default cachedEventHandler(
  async (event) => {
    const id = Number(getRouterParam(event, 'id'))
    const product = await fetchProductFromDB(id)

    if (!product) {
      throw createError({ statusCode: 404, message: 'Product not found' })
    }

    return product
  },
  {
    maxAge: 60 * 60,         // 1 hour cache
    swr: true,                // Stale while revalidate
    staleMaxAge: 60 * 60 * 24, // Stale valid 24 hours
    getKey: (event) => `product:${getRouterParam(event, 'id')}`,
    varies: ['Accept-Language']
  }
)
TYPESCRIPT
// server/api/admin/invalidate-cache.post.ts - Manual cache invalidation
export default defineEventHandler(async (event) => {
  const { productId, scope } = await readBody(event)
  const storage = useStorage('products')

  if (scope === 'all') {
    // Invalidate all product caches
    const keys = await storage.getKeys()
    for (const key of keys) {
      await storage.removeItem(key)
    }
    return { message: 'All caches invalidated' }
  }

  if (productId) {
    await storage.removeItem(`product:${productId}`)
    return { message: `Cache invalidated for product ${productId}` }
  }

  throw createError({ statusCode: 400, message: 'Specify productId or scope=all' }
})

❓ FAQ

Q What is the relationship between Nitro and Nuxt?
A Nitro is the server-side engine for Nuxt 3, responsible for building, routing, storage, caching, and deployment. Nuxt 3 = Vue 3 + Vite (frontend) + Nitro (backend).
Q Does data get lost after a restart when using the useStorage memory driver?
A Yes. While the memory driver is convenient in a development environment, you must use a persistence driver such as Redis or FS in a production environment. To configure the driver, simply modify nuxt.config.ts—no code changes are required.
Q What is the difference between cachedEventHandler and routeRules in SWR?
A routeRules in SWR caches the entire page's HTML, while cachedEventHandler caches API response data. Use routeRules at the page level and cachedEventHandler at the API level.
Q What is the difference between Nitro plugins and Nuxt plugins?
A Nitro plugins (server/plugins/) run on the server and interact with storage, hooks, and the database. Nuxt plugins (located in plugins/) run within the Vue application and handle components, Composables, and third-party SDKs.
Q How do I monitor the Nitro cache hit rate?
A Log cache hits and misses in the request/response hooks. In production, use an APM tool (Sentry/Datadog) for monitoring, or set up your own metrics endpoint.
Q How do I choose between Cloudflare KV and Redis?
A Use KV for Cloudflare deployments (native integration, global distribution), and Redis for Node.js/Vercel deployments (low latency, rich data structures). MegaShop uses useStorage to provide a unified abstraction across multiple environments.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Configure the useStorage memory driver to implement simple read and write operations for product data caching
  2. Advanced Exercise (Difficulty: ⭐⭐): Use cachedEventHandler to implement caching for the product list API, and verify that the response is faster when the cache is hit.
  3. Challenge (Difficulty: ⭐⭐⭐): Implement a complete cache invalidation mechanism—automatically invalidate the cache for related products when an order is created; a manual API should allow clearing the cache by product ID or clearing the entire cache.

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏