Especialização em Front-End com Vue • Demonstrações Práticas Compiláveis


💻 Código de Exemplo 18 — Rotas de API Nitro Server e Configuração Híbrida de Renderização (SSR / SSG)

Este exemplo demonstra a criação de uma API Backend interna no Nuxt 3 através do Nitro Engine (server/api/), manipulação segura de headers e query params com defineEventHandler, e o consumo direto na camada de frontend com $fetch.

1. Endpoint Server-Side Nitro: server/api/cluster/metrics.get.ts

// server/api/cluster/metrics.get.ts
import { defineEventHandler, getQuery, createError } from 'h3';

interface ClusterMetricResponse {
  timestamp: string;
  clusterId: string;
  loadAverage: number;
  memoryUsedPercentage: number;
  activePods: number;
}

export default defineEventHandler(async (event): Promise<ClusterMetricResponse> => {
  // 1. Extração segura de parâmetros de query string
  const query = getQuery(event);
  const clusterId = (query.cluster as string) || 'default-us-east';

  // 2. Simulação de autenticação de segredos server-only
  const apiKey = event.node.req.headers['x-internal-secret'];
  const runtimeConfig = useRuntimeConfig();

  // Em ambiente server, runtimeConfig.apiSecret é protegido contra vazamento no cliente
  console.log(`[Nitro Server] Atendendo requisição para o cluster: ${clusterId}`);

  // Simulação de latência de banco de dados
  await new Promise((resolve) => setTimeout(resolve, 300));

  return {
    timestamp: new Date().toISOString(),
    clusterId,
    loadAverage: Number((Math.random() * 2 + 1.2).toFixed(2)),
    memoryUsedPercentage: Number((Math.random() * 30 + 55).toFixed(1)),
    activePods: Math.floor(Math.random() * 40) + 120
  };
});

2. Configuração de Cache no Edge: nuxt.config.ts

// nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  routeRules: {
    // Rotas estáticas pré-renderizadas no build (SSG puro)
    '/about': { prerender: true },
    '/docs/**': { prerender: true },

    // Rota SSR sob demanda com cache SWR de 60 segundos no Edge
    '/dashboard/**': { swr: 60 },

    // Endpoint de API com cache do servidor por 10 segundos
    '/api/cluster/**': { cache: { maxAge: 10 } },

    // SPA puro no cliente para área restrita com autenticação
    '/admin/**': { ssr: false }
  }
});

3. Componente Vue Consumidor: pages/cluster-status.vue

<!-- pages/cluster-status.vue -->
<script setup lang="ts">
import { ref } from 'vue';

const selectedCluster = ref<string>('us-east-prod');

// $fetch é o cliente HTTP nativo universal do Nuxt (baseado em ofetch)
const { data: metrics, pending, refresh } = await useAsyncData(
  'cluster-metrics',
  () => $fetch('/api/cluster/metrics', {
    query: { cluster: selectedCluster.value }
  }),
  { watch: [selectedCluster] } // Dispara novo fetch automaticamente se o cluster mudar
);
</script>

<template>
  <div class="max-w-xl mx-auto p-6 bg-white border rounded-2xl shadow-sm space-y-6">
    <div class="flex items-center justify-between border-b pb-4">
      <div>
        <h2 class="text-xl font-bold text-slate-800">Nitro Server API & SSR</h2>
        <p class="text-xs text-slate-500">Endpoints server-only embutidos no Nuxt 3</p>
      </div>
      <button
        @click="() => refresh()"
        class="text-xs px-3 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-semibold"
      >
        {{ pending ? 'Consultando...' : 'Atualizar Dados' }}
      </button>
    </div>

    <div>
      <label class="block text-xs font-semibold text-slate-600 mb-1">Selecionar Cluster:</label>
      <select
        v-model="selectedCluster"
        class="w-full border px-3 py-2 rounded-lg text-sm bg-white"
      >
        <option value="us-east-prod">US-East-Prod (Principal)</option>
        <option value="sa-east-backup">SA-East-Backup (São Paulo)</option>
        <option value="eu-west-edge">EU-West-Edge (Frankfurt)</option>
      </select>
    </div>

    <div v-if="metrics" class="grid grid-cols-3 gap-3">
      <div class="p-3 bg-slate-50 border rounded-xl text-center">
        <span class="text-[11px] text-slate-400 font-semibold uppercase">Load Avg</span>
        <div class="text-xl font-black text-indigo-600 mt-1">{{ metrics.loadAverage }}</div>
      </div>
      <div class="p-3 bg-slate-50 border rounded-xl text-center">
        <span class="text-[11px] text-slate-400 font-semibold uppercase">Memória</span>
        <div class="text-xl font-black text-emerald-600 mt-1">{{ metrics.memoryUsedPercentage }}%</div>
      </div>
      <div class="p-3 bg-slate-50 border rounded-xl text-center">
        <span class="text-[11px] text-slate-400 font-semibold uppercase">Pods Ativos</span>
        <div class="text-xl font-black text-amber-600 mt-1">{{ metrics.activePods }}</div>
      </div>
    </div>

    <div v-if="metrics" class="text-[11px] font-mono text-slate-400 text-right">
      Timestamp do Servidor Nitro: {{ metrics.timestamp }}
    </div>
  </div>
</template>

🖥️ Saída / Comportamento Esperado

Chamada HTTP de Teste via Terminal:
$ curl http://localhost:3000/api/cluster/metrics?cluster=sa-east-backup

Resposta JSON:
{
  "timestamp": "2026-09-28T04:10:00.123Z",
  "clusterId": "sa-east-backup",
  "loadAverage": 1.84,
  "memoryUsedPercentage": 68.2,
  "activePods": 142
}

Comportamento no Frontend:
Ao trocar a opção no select para "EU-West-Edge":
A opção { watch: [selectedCluster] } em useAsyncData detecta a alteração reativa.
Dispara automaticamente a nova consulta à API Nitro com query param atualizado.
Os 3 cards são recalculados sem recarregar a página.

🔍 Destaques da Implementação

  1. Zero Configuração de Servidor: Arquivos dentro de server/api/ tornam-se automaticamente endpoints HTTP tipados sem necessidade de Express ou Fastify externo.
  2. routeRules Híbridas: Permite misturar no mesmo projeto rotas SSG (estáticas), SSR puro, SPA desabilitado e SWR com cache na CDN.
  3. Segurança de Variáveis: O runtimeConfig do Nitro nunca expõe senhas de banco ou chaves privadas ao bundle JavaScript do cliente.