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
- Arquitetura Nitro: Rollup Build + Roteamento H3 + Desacoplamento de Runtime
- Camada de armazenamento: useStorage() + suporte multi-driver para KV, Redis, FileSystem e OSS
- API de Cache: cachedEventHandler / defineCachedFunction
- Event Hooks: Ciclo de vida do nitro.hooks
- Cache de Produtos do MegaShop + Atualizações de Estoque + Implantação com Múltiplos Presets
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:
// 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
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
// 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:
// 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
// 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:
// Execução bem-sucedida
(2) ▶ Exemplo: Configuração de Armazenamento Redis
// 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:
// 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
// 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:
// Execução bem-sucedida
(2) ▶ Exemplo: defineCachedFunction
// 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:
// 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
// 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:
// Execução bem-sucedida
7. Exemplo Completo: Sistema de Cache de Produtos do MegaShop
// 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/']
}
}
})
// 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']
}
)
// 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
useStorage?nuxt.config.ts—nenhuma alteração de código é necessária.cachedEventHandler e routeRules no SWR?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.plugins/) rodam dentro da aplicação Vue e lidam com componentes, Composables e SDKs de terceiros.useStorage para fornecer uma abstração unificada em múltiplos ambientes.📖 Resumo
- Nitro é o motor server-side do Nuxt 3: build + roteamento H3 + armazenamento + cache + múltiplos presets de implantação
- useStorage() fornece uma interface de armazenamento unificada; use memória para desenvolvimento e Redis/KV para produção, sem alterações de código
cachedEventHandlerarmazena em cache respostas de API;defineCachedFunctionarmazena em cache resultados de funções- Hooks do Nitro monitoram o ciclo de vida de request/response para implementar autenticação, logging e invalidação de cache
- MegaShop usa Nitro Storage para cache de produtos + event hooks para invalidar automaticamente o cache quando o estoque é atualizado
📝 Exercícios
- 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
- Exercício Avançado (Dificuldade: ⭐⭐): Use
cachedEventHandlerpara implementar cache para a API de lista de produtos e verifique que a resposta é mais rápida quando o cache é atingido - 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
---|



