Capítulo 18: Server Routes, Server-Side Rendering (SSR) e SSG no Nuxt

Especialização em Front-End com Vue • Vue 3 & Nuxt 3 • Composition API, Pinia, Vue Router, SSR e TypeScript


🗺️ Mapa Conceitual do Nitro Engine e Renderização Híbrida

flowchart TD
    A["Requisição HTTP Entrante"] --> B["Nitro Engine (H3 Event Handler)"]

    B --> C{"Tipo de Destino"}
    C -->|"Rota de API Backend: server/api/*"| D["defineEventHandler() -> Resposta JSON"]
    C -->|"Página Frontend: pages/*"| E{"Regra em routeRules (nuxt.config)"}

    E -->|"prerender: true"| F["SSG (HTML Estático no Build)"]
    E -->|"ssr: true (Padrão)"| G["SSR Dinâmico sob Demanda"]
    E -->|"swr: 60"| H["SWR Cache no Edge / CDN"]
    E -->|"ssr: false"| I["SPA Puro no Navegador"]

    D & F & G & H & I --> J["Entrega Otimizada ao Cliente"]

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style D fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style E fill:#fff9c4,stroke:#fbc02d,stroke-width:2px
    style J fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. O Nitro Engine e a Renderização Híbrida

No Nuxt 3, o backend não é um servidor Node.js pesado e separado; ele é provido nativamente pelo Nitro Engine, um servidor web leve e ultra-otimizado baseado no framework minimalista H3. Com o Nitro, você pode construir rotas de API tipadas, manipular webhooks, conectar diretamente a bancos de dados com Prisma ou Drizzle e definir regras de renderização cirúrgicas por rota através de routeRules.

1.1 Rotas de Servidor: server/api/ e defineEventHandler

Qualquer arquivo dentro do diretório server/api/ torna-se automaticamente um endpoint HTTP REST:

  • server/api/relatorio.get.ts responde exclusivamente a requisições HTTP GET /api/relatorio.
  • server/api/usuario.post.ts responde a requisições HTTP POST /api/usuario.
  • Os manipuladores são definidos através de defineEventHandler(async (event) => { ... }), com acesso a funções utilitárias do H3:
    • getQuery(event): Faz o parse seguro de parâmetros de query string.
    • readBody(event): Lê e valida o payload JSON enviado no corpo do POST/PUT.
    • createError({ statusCode, statusMessage }): Dispara erros HTTP formatados.

1.2 Segurança e Proteção de Segredos com runtimeConfig

O maior risco de segurança em aplicações front-end é vazar tokens privados de banco de dados ou chaves de IA (como OpenAI/Anthropic API keys) no código do cliente. No Nuxt 3:

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // Chaves privadas (Acessíveis APENAS em server/):
    databasePassword: process.env.DB_PASSWORD,
    // Chaves públicas (Acessíveis tanto no cliente quanto no servidor):
    public: {
      apiBaseUrl: 'https://api.empresa.com.br'
    }
  }
});

1.3 As 4 Modalidades de Renderização Híbrida (routeRules)

Você não precisa escolher entre SPA, SSR ou SSG para todo o projeto. O Nuxt permite misturar as 4 estratégias no mesmo arquivo de configuração:

  1. SSG (Static Site Generation): { prerender: true }. A página é compilada em HTML estático durante a esteira de CI/CD. Ideal para blogs, documentação e páginas institucionais.
  2. SSR (Server-Side Rendering): { ssr: true }. A página é gerada no servidor a cada requisição. Ideal para e-commerce e portais de notícias.
  3. SWR (Stale-While-Revalidate): { swr: 60 }. O servidor atende a requisição a partir do cache da CDN e, em segundo plano, revalida o conteúdo a cada 60 segundos.
  4. SPA (Single Page Application): { ssr: false }. Desabilita o SSR, entregando apenas a casca do app. Ideal para painéis administrativos fechados sob login.

⚖️ 2. Tabela Comparativa de Estratégias de Renderização

Modo Geração do HTML Custo de Servidor Latência (TTFB) Ideal Para
SSG No build time (CI/CD). Praticamente zero (CDN estática). Ultra-Baixa (< 50ms). Páginas de Marketing, Documentações.
SSR A cada requisição no servidor. Moderado (requer computação Node/Edge). Média (depende do banco de dados). E-commerces, Portais de Notícias dinâmicos.
SWR Cache na CDN com revalidação. Baixo (computa apenas a cada intervalo). Ultra-Baixa (servido do cache). Catálogos de produtos de alto tráfego.
SPA No navegador do cliente. Zero no servidor (apenas arquivos estáticos). Lenta na primeira carga; rápida após. Dashboards corporativos fechados.

⚠️ 3. Armadilhas Comuns em Produção (Gotchas & Anti-Patterns)

Anti-Pattern 1: Acessar Segredos de Servidor dentro de Componentes Vue

Erro Comum: Escrever const config = useRuntimeConfig(); console.log(config.databasePassword) dentro de um componente .vue. Consequência: O valor retornará undefined no cliente, e se renderizado durante o SSR, o segredo poderá ser vazado no HTML enviado ao navegador. Regra de Ouro: Chaves de API privadas e senhas devem residir exclusivamente dentro de server/api/.

Anti-Pattern 2: Esquecer de Definir o Método HTTP no Nome do Arquivo

Erro Comum: Criar server/api/orders.ts para salvar pedidos. Sem o sufixo .post.ts, a rota responderá a qualquer método HTTP (GET, DELETE, etc.), violando boas práticas REST.


💻 4. Código Conceitual Arquitetural Comentado

// server/api/system/audit.post.ts
import { defineEventHandler, readBody, createError } from 'h3';

interface AuditLogPayload {
  userId: string;
  action: string;
  entityId: string;
}

export default defineEventHandler(async (event) => {
  // 1. Acesso seguro a segredos do servidor
  const config = useRuntimeConfig();

  // 2. Leitura e validação de payload tipado
  const body = await readBody<AuditLogPayload>(event);

  if (!body.userId || !body.action) {
    throw createError({
      statusCode: 400,
      statusMessage: 'Requisição inválida: userId e action são campos obrigatórios.'
    });
  }

  // 3. Simulação de inserção segura no banco de dados da infraestrutura
  const logRecord = {
    id: `log-${Date.now()}`,
    ...body,
    createdAt: new Date().toISOString()
  };

  console.log('[Nitro Database] Auditoria registrada com sucesso:', logRecord);

  return { success: true, record: logRecord };
});

🛠️ 5. Cheat-Sheet de Configuração do routeRules

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // 1. Pré-renderizar páginas estáticas (SSG)
    '/institucional/**': { prerender: true },

    // 2. Cache na CDN com revalidação de 10 minutos (SWR)
    '/produtos/**': { swr: 600 },

    // 3. SPA puro para o painel administrativo (Zero SSR)
    '/admin/**': { ssr: false },

    // 4. Redirecionamento 301 definitivo
    '/legado-portal': { redirect: { to: '/novo-portal', statusCode: 301 } }
  }
});