404 Not Found

404 Not Found


nginx

Nitro Server Engine

Charlie precisa adicionar uma camada de cache ao MegaShop—consultas frequentes ao banco de dados na página que lista milhões de produtos são lentas demais. Bob descobriu que os métodos de armazenamento diferem entre ambientes de implantação (Node, Docker, Vercel). O motor Nitro fornece uma camada de abstração de armazenamento unificada e API de cache, permitindo que uma única base de código funcione em todos os ambientes.

1. O Que Você Vai Aprender


2. Uma História Real de um Arquiteto

(1) Dor: Consultas em milhões de produtos estão sobrecarregando o banco de dados

A página de detalhes do produto do MegaShop realiza 2.000 consultas ao banco de dados por segundo com tempo de resposta de 500 ms. Bob adicionou um cache Redis, mas o código está acoplado ao ambiente de implantação—usa cache baseado em arquivo localmente e Redis em produção, exigindo dois conjuntos separados de alterações de código.

(2) Solução com a Camada de Abstração de Armazenamento do Nitro

A interface de armazenamento unificada useStorage() do Nitro permite trocar drivers sem alterar o código:

TYPESCRIPT
// Mesmo código, driver diferente baseado no preset
const storage = useStorage('products')
await storage.setItem('product:123', productData)

(3) Benefícios: Interface unificada + tempo de resposta de 5 ms

O código da camada de armazenamento é padronizado: memória/fs é usado para desenvolvimento local, e Redis/KV é usado em produção. Quando ocorre um cache hit de produto, o tempo de resposta é de 5 ms, e as consultas ao banco de dados são reduzidas em 95%.


3. A Arquitetura Nitro

(1) Visão Geral da Arquitetura Nitro

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) Comparação dos Presets do Nitro

Presets Ambiente de Runtime Driver de Armazenamento Método de Implantação
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) ▶ Exemplo: Configurando um Nitro Preset

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: process.env.DEPLOY_TARGET || 'node-server',
    compressPublicAssets: true,
    storage: {
      // Desenvolvimento: driver de memória
      cache: { driver: 'memory' },
      // Produção: configurado via ambiente
      products: { driver: process.env.STORAGE_DRIVER || 'memory' }
    }
  }
})

Saída:

TEXT
// Execução bem-sucedida

4. Camada de Abstração de Armazenamento

(1) API useStorage

Método Descrição Exemplo
setItem(key, value) Escrita await storage.setItem('product:1', data)
getItem(key) Leitura await storage.getItem('product:1')
removeItem(key) Exclusão await storage.removeItem('product:1')
getKeys(base) Listar chaves await storage.getKeys('product:')
hasItem(key) Existe await storage.hasItem('product:1')
clear(base) Limpar await storage.clear('product:')

(1) ▶ Exemplo: Armazenamento de Cache de Produto

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

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

  // Cache miss: buscar do banco de dados
  const product = await fetchProductFromDB(Number(id))
  if (!product) {
    throw createError({ statusCode: 404, message: 'Product not found' })
  }

  // Armazenar no cache com TTL (1 hora)
  await storage.setItem(`product:${id}`, product, { ttl: 3600 })

  return product
})

Saída:

TEXT
// Execução bem-sucedida

(2) ▶ Exemplo: Configuração de Armazenamento Redis

TYPESCRIPT
// nuxt.config.ts - Armazenamento Redis para produção
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:'
      }
    }
  }
})

Saída:

TEXT
// Execução bem-sucedida

(2) Comparação de Drivers de Armazenamento

Driver Velocidade Durabilidade Distribuído Casos de Uso
memory ⚡⚡⚡ ❌ Perdido ao reiniciar Desenvolvimento/Testes
fs ⚡⚡ ✅ Local Implantação standalone
redis ⚡⚡ Cluster de Produção
cloudflare-kv Cloudflare
vercel-kv Vercel

5. API de Cache

(1) ▶ Exemplo: cachedEventHandler

TYPESCRIPT
// server/api/products/featured.get.ts
export default cachedEventHandler(
  async () => {
    // O resultado deste handler é cacheado
    const products = await $fetch('/api/internal/products/featured')
    return products
  },
  {
    maxAge: 60 * 60,           // Cache por 1 hora
    swr: true,                  // Serve stale enquanto revalida
    staleMaxAge: 60 * 60 * 4,  // Stale válido por 4 horas
    getKey: () => 'featured-products',
    varies: ['Accept-Language'] // Cache por idioma
  }
)

Saída:

TEXT
// Execução bem-sucedida

(2) ▶ Exemplo: 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'
  }
)

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

Saída:

TEXT
// Execução bem-sucedida

6. Event Hooks

(1) Hooks do Ciclo de Vida do Nitro

Hook Condição de Disparo Finalidade
close Parar Serviço Liberar Recursos
error Erro não tratado Relatório de erros
request Início da Requisição Log/Autenticação
response Resposta Enviada Estatísticas de Performance
beforeResponse Antes da Resposta Modificar Resposta

(1) ▶ Exemplo: Hook de Evento de Atualização de Estoque

TYPESCRIPT
// server/plugins/stock.ts
export default defineNitroPlugin((nitroApp) => {
  // Escutar eventos de atualização de estoque
  nitroApp.hooks.hook('request', async (event) => {
    const url = getRequestURL(event)
    if (url.pathname === '/api/orders' && getMethod(event) === 'POST') {
      // Antes do pedido: verificar disponibilidade de estoque
      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(', ')}`
        })
      }
    }
  })

  // Após o pedido: invalidar cache de produto
  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')
      // Invalidar cache de produtos
      for (const item of body.items) {
        await storage.removeItem(`product:${item.productId}`)
      }
    }
  })
})

Saída:

TEXT
// Execução bem-sucedida

7. Exemplo Completo: Sistema de Cache de Produtos do MegaShop

TYPESCRIPT
// nuxt.config.ts - Configuração Nitro para produção
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 - Estratégia de cache completa
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,         // Cache de 1 hora
    swr: true,                // Stale while revalidate
    staleMaxAge: 60 * 60 * 24, // Stale válido por 24 horas
    getKey: (event) => `product:${getRouterParam(event, 'id')}`,
    varies: ['Accept-Language']
  }
)
TYPESCRIPT
// server/api/admin/invalidate-cache.post.ts - Invalidação manual de cache
export default defineEventHandler(async (event) => {
  const { productId, scope } = await readBody(event)
  const storage = useStorage('products')

  if (scope === 'all') {
    // Invalidar todos os caches de produto
    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' })
})

❓ Perguntas Frequentes

P Qual é a relação entre Nitro e Nuxt?
R Nitro é o motor server-side do Nuxt 3, responsável por build, roteamento, armazenamento, cache e implantação. Nuxt 3 = Vue 3 + Vite (frontend) + Nitro (backend).
P Os dados são perdidos após uma reinicialização ao usar o driver de memória useStorage?
R Sim. Embora o driver de memória seja conveniente em ambiente de desenvolvimento, você deve usar um driver de persistência como Redis ou FS em ambiente de produção. Para configurar o driver, basta modificar nuxt.config.ts—nenhuma alteração de código é necessária.
P Qual é a diferença entre cachedEventHandler e routeRules no SWR?
R routeRules no SWR armazena em cache o HTML da página inteira, enquanto cachedEventHandler armazena em cache os dados de resposta da API. Use routeRules no nível da página e cachedEventHandler no nível da API.
P Qual é a diferença entre plugins do Nitro e plugins do Nuxt?
R Plugins do Nitro (server/plugins/) rodam no servidor e interagem com armazenamento, hooks e banco de dados. Plugins do Nuxt (localizados em plugins/) rodam dentro da aplicação Vue e lidam com componentes, Composables e SDKs de terceiros.
P Como monitorar a taxa de cache hit do Nitro?
R Registre hits e misses de cache nos hooks de request/response. Em produção, use uma ferramenta APM (Sentry/Datadog) para monitoramento, ou configure seu próprio endpoint de métricas.
P Como escolher entre Cloudflare KV e Redis?
R Use KV para implantações Cloudflare (integração nativa, distribuição global), e Redis para implantações Node.js/Vercel (baixa latência, estruturas de dados ricas). MegaShop usa useStorage para fornecer uma abstração unificada em múltiplos ambientes.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Configure o driver de memória useStorage para implementar operações simples de leitura e escrita para cache de dados de produto
  2. Exercício Avançado (Dificuldade: ⭐⭐): Use cachedEventHandler para implementar cache para a API de lista de produtos e verifique que a resposta é mais rápida quando o cache é atingido
  3. Desafio (Dificuldade: ⭐⭐⭐): Implemente um mecanismo completo de invalidação de cache—invalidar automaticamente o cache dos produtos relacionados quando um pedido é criado; uma API manual deve permitir limpar o cache por ID de produto ou limpar todo o cache

---|

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%