Renderização Híbrida e Route Rules
Aula 2 de 4
Hybrid Rendering
Nitro permite diferentes modos de renderização para diferentes rotas no mesmo projeto.
Modos de Renderização
| Modo | Descrição |
|---|---|
server | Renderizado sob demanda (SSR tradicional) |
client | SPA renderizado no cliente |
static | Pré-renderizado no build (HTML estático) |
prerender | Igual static, mas força pré-renderização |
// nitro.config.ts
import { defineNitroConfig } from 'nitro/config';
export default defineNitroConfig({
routeRules: {
// Home: estática (pré-renderizada)
'/': { prerender: true },
// Blog: ISR (revalida a cada hora)
'/blog/**': { isr: 3600 },
// Dashboard: sempre server-side
'/dashboard/**': { ssr: true },
// Admin SPA: client-side rendering
'/admin/**': { ssr: false },
// API: sempre server-side
'/api/**': { ssr: true },
// Redirect
'/old-page': { redirect: '/new-page' },
// CORS para API
'/api/**': { cors: true }
}
});
Prerender
// nitro.config.ts
export default defineNitroConfig({
prerender: {
crawlLinks: true, // Segue links para pré-renderizar
routes: ['/', '/about', '/contact'],
ignore: ['/api/**', '/admin/**']
}
});
// server/routes/about.ts — será pré-renderizado
export default defineEventHandler(() => {
return {
title: 'Sobre Nós',
content: 'Página estática gerada no build'
};
});
Route Rules
Route rules permitem configurar comportamento específico por padrão de URL.
// nitro.config.ts
export default defineNitroConfig({
routeRules: {
// ISR - Incremental Static Regeneration
'/products/**': {
isr: 300 // revalida a cada 5 minutos
},
// SWR - Stale While Revalidate
'/blog/**': {
swr: 3600, // serve cache por 1 hora
isr: 3600 // revalida em background
},
// Static
'/': { prerender: true },
// Redirect (301)
'/old-blog/**': {
redirect: {
to: '/blog/**',
statusCode: 301
}
},
// Proxy
'/legacy-api/**': {
proxy: {
to: 'https://old-server.com/api/**',
opts: {
headers: { 'X-Proxy': 'nitro' }
}
}
},
// Headers customizados
'/secure/**': {
headers: {
'X-Frame-Options': 'DENY',
'Content-Security-Policy': "default-src 'self'"
}
},
// CORS
'/api/**': {
cors: true,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE'
}
}
}
});
Route Rules com Cache
// nitro.config.ts
export default defineNitroConfig({
routeRules: {
// Cache no servidor
'/api/public/**': {
cache: {
maxAge: 300, // 5 minutos no CDN
swr: true, // serve stale enquanto revalida
headersOnly: true // cache apenas se headers permitirem
}
},
// Sem cache
'/api/private/**': {
cache: false
}
}
});
Cache
Nitro possui sistema de cache interno e integração com CDNs.
// server/api/products.ts
export default defineEventHandler(async (event) => {
// Cache manual com event.caches (se disponível)
const cache = event.caches?.('products');
const cached = await cache?.match(event.path);
if (cached) return cached;
const products = await fetchProducts();
// Cache para 5 minutos, serve stale enquanto revalida
setResponseHeader(event, 'Cache-Control', 'public, max-age=300, stale-while-revalidate=3600');
return products;
});
Cache com Storage
// server/middleware/cache.ts
export default defineEventHandler(async (event) => {
const url = event.path;
const cacheKey = `cache:${url}`;
// Tentar ler do cache (Redis, filesystem, etc)
const cached = await useStorage('cache').getItem(cacheKey);
if (cached) {
setResponseHeader(event, 'X-Cache', 'HIT');
return cached;
}
// Se cache miss, processar normalmente
setResponseHeader(event, 'X-Cache', 'MISS');
});
Render Modes Combinados
// nitro.config.ts
export default defineNitroConfig({
routeRules: {
// Blog posts: ISR com cache
'/blog/**': {
isr: 60,
swr: true,
prerender: true // pré-renderiza os principais
},
// Admin: SPA client-side
'/admin/**': {
ssr: false
},
// Landing pages: estáticas
'/': { prerender: true },
'/features': { prerender: true },
'/pricing': { prerender: true },
// API: server + CORS
'/api/**': {
ssr: true,
cors: true
}
},
// Dev options
devServer: {
watch: ['server/**']
}
});
useNitroApp
// server/plugins/hooks.ts
export default defineNitroPlugin((nitroApp) => {
// Hook executado antes de cada requisição
nitroApp.hooks.hook('request', (event) => {
console.log(`[${event.method}] ${event.path}`);
});
// Hook após resposta
nitroApp.hooks.hook('afterResponse', (event, response) => {
console.log(`Response: ${response.status}`);
});
});
// server/api/use-nitro.ts
export default defineEventHandler(async (event) => {
// Acessar o app Nitro
const app = useNitroApp();
// Executar outro handler internamente
const result = await app.callHandler(event, '/api/products');
// Acessar configuração
const config = useRuntimeConfig();
return {
appVersion: config.appVersion,
result
};
});
Lab: Exercício - Site Híbrido
// nitro.config.ts
export default defineNitroConfig({
routeRules: {
'/': { prerender: true },
'/about': { prerender: true },
'/blog': { prerender: true },
'/blog/**': { isr: 300 },
'/api/**': { ssr: true, cors: true },
'/dashboard/**': { ssr: true }
},
prerender: {
crawlLinks: true,
routes: ['/', '/about', '/blog']
}
});
// server/api/posts.ts
export default defineEventHandler(async () => {
// Simula busca de dados
const posts = [
{ slug: 'intro-nitro', title: 'Introdução ao Nitro' },
{ slug: 'route-rules', title: 'Route Rules Explicadas' }
];
setResponseHeader(event, 'Cache-Control', 'public, max-age=300');
return posts;
});
# Build (pré-renderiza as rotas configuradas)
npm run build
# Em produção, / e /about são HTML estático
# /blog é estático, mas posts individuais são ISR
# /api é SSR normal com CORS
Route rules no Nitro permitem configurar diferentes estratégias de renderização por URL. Combine prerender (rotas estáticas), isr (cache com revalidação) e ssr (dinâmico) no mesmo projeto. Cache headers e CORS são configurados centralmente.