O Navistron é deployado na Vercel — a plataforma criada pela mesma equipe do Next.js. Isso significa suporte nativo a Server Components, ISR, API Routes como Serverless Functions e CDN global. Mas o deploy não é mágico: requer configuração cuidadosa de variáveis de ambiente, connection patterns para MongoDB e decisões sobre o que pré-renderizar. Neste artigo, vamos explorar cada aspecto do pipeline de deploy.

Build Automático via Git Push

O fluxo de deploy é simples: cada git push para a branch main aciona um build automático na Vercel. O processo executa next build, que:

  1. Compila Server Components — home, ranking, blog, stats, sponsors, perfis de jogador são renderizados para HTML estático ou ISR
  2. Gera páginas SSG — 23 artigos de blog via generateStaticParams() e até 500 perfis de jogador
  3. Empacota Client Components — apenas 3 páginas (play, dashboard, admin) geram bundles JavaScript para o navegador
  4. Cria Serverless Functions — cada Route Handler (route.js) se torna uma função Lambda independente
  5. Gera sitemap dinâmico — consulta o MongoDB para listar até 1.000 jogadores
  6. Gera robots.txt — via função que referencia o sitemap e bloqueia /api/ e /admin/

Não há pré-build script, dockerfile ou CI/CD customizado. O build usa o adaptador nativo da Vercel para Next.js — sem output: 'standalone' ou output: 'export'.

3 Variáveis de Ambiente: Tudo Que o Deploy Precisa

O Navistron inteiro funciona com apenas 3 variáveis de ambiente customizadas, configuradas no painel da Vercel:

  • MONGODB_URI (server-only) — Connection string do MongoDB Atlas. Usada por getClientPromise() em mongodb.js e pelo seed.mjs. Sem prefixo NEXT_PUBLIC_, nunca exposta ao client
  • NAVISTRON_PASSWORD (server-only) — Senha do painel admin. Usada por validateAdmin() em 3 rotas admin. Também sem prefixo público
  • NEXT_PUBLIC_SITE_URL (build-time, pública) — URL base do site. Com prefixo NEXT_PUBLIC_, o valor é embutido no bundle no build time. Fallback: https://navistron.io. Usada em 13+ arquivos para canonical URLs, OpenGraph, sitemap e metadataBase

Uma consequência do prefixo NEXT_PUBLIC_: em preview deployments (ex: navistron-abc123.vercel.app), os canonicals e OG URLs ainda apontam para a URL de produção, a menos que a variável seja sobrescrita por branch.

8 API Routes como Serverless Functions

Cada arquivo route.js dentro de src/app/api/ é deployado como uma Serverless Function independente na Vercel — 8 funções no total, com 13 handlers HTTP:

  • /api/scores — GET (top 100) + POST (registrar score). Collection: scores
  • /api/anonymous-sessions — GET (7 aggregations) + POST (registrar sessão). Collection: anonymous_sessions
  • /api/sponsors — GET (listar por valor). Collection: sponsors
  • /api/sponsors/click — POST (registrar clique, $inc + insert). Collections: sponsors, sponsor_clicks
  • /api/stats — GET (10 aggregations paralelas via Promise.all). Collection: scores
  • /api/admin/scores — GET + DELETE + PUT (protegido). Collection: scores
  • /api/admin/sponsors — GET + POST + PUT + DELETE (protegido). Collection: sponsors
  • /api/admin/sponsors/stats — GET (7 aggregations com $lookup). Collections: sponsors, sponsor_clicks

Cada função é stateless: inicia, executa a query MongoDB, retorna a resposta JSON e pode ser destruída. O auto-scaling da Vercel cria réplicas conforme a demanda — zero configuração de infraestrutura.

MongoDB Singleton em Ambiente Serverless

A conexão com o MongoDB usa um padrão singleton em mongodb.js. A variável clientPromise armazena a Promise de conexão no escopo do módulo. Em ambiente serverless, isso funciona assim:

  • Cold start — Primeira invocação de uma função: cria new MongoClient(uri), chama .connect(), armazena a Promise em clientPromise. Latência de ~200-500ms adicional
  • Warm container — Invocações subsequentes no mesmo container reutilizam o mesmo clientPromise — a conexão já está estabelecida. Latência de conexão: ~0ms
  • Container reciclado — Após inatividade, a Vercel destrói o container. Próxima invocação = novo cold start

Em desenvolvimento (NODE_ENV === 'development'), o singleton é armazenado em global._mongoClientPromise para sobreviver ao Hot Module Replacement (HMR) do Next.js. Em produção, usa o escopo do módulo — suficiente para containers serverless.

Todas as 8 API routes + as funções SSR usam o mesmo padrão: const client = await getClientPromise(); const db = client.db('navistron').

ISR na Vercel: Edge Cache com Stale-While-Revalidate

O Navistron tem 10 páginas ISR com intervalos diferentes. Na Vercel, o ISR funciona como stale-while-revalidate:

  1. A página é renderizada e cacheada no edge da Vercel (ponto de presença mais próximo do usuário)
  2. Dentro do intervalo de revalidate, todas as requisições recebem a versão cacheada (instantâneo)
  3. Após o intervalo expirar, a próxima requisição recebe o cache stale enquanto a Vercel re-renderiza a página em background
  4. A versão nova substitui o cache — próximos visitantes veem dados atualizados

Os intervalos são calibrados por volatilidade dos dados:

  • 120s — Stats diário (dados mudam a cada partida)
  • 300s — Ranking (geral, semanal, mensal) e Stats (geral, mensal)
  • 600s — Stats anual e Sponsors (dados estáveis)
  • 3.600s — Home e perfis de jogador

SSG: 23 Artigos + 500 Perfis Pré-Renderizados

Duas rotas dinâmicas usam generateStaticParams() para pré-renderizar páginas no build:

  • Blog [slug] — Chama getAllSlugs() que retorna os 23 slugs do array JavaScript em blogArticles.js. Sem consulta ao banco — são dados estáticos. Cada slug gera uma página HTML completa no build. Sem revalidate — só atualiza com novo deploy
  • Player [nickname] — Chama getAllPlayerNames() que consulta o MongoDB via aggregation $group. Os primeiros 500 nomes (.slice(0, 500)) são pré-renderizados. Com dynamicParams = true, jogadores além dos 500 são renderizados on-demand e cacheados com ISR de 1 hora

Na Vercel, páginas SSG são servidas como arquivos estáticos da CDN — latência mínima, sem execução de código no servidor. Os artigos de blog, em particular, são entregues como HTML puro sem JavaScript de página (Server Components).

Sitemap Dinâmico com Dados do MongoDB

O sitemap.js é executado no build e gera o sitemap.xml com 3 fontes de URLs:

  • 12 páginas estáticas — Com prioridades explícitas: home (1.0), play (0.9), ranking (0.8), blog (0.8), stats (0.7), sponsors (0.6), dashboard (0.5), e sub-rotas
  • 23 slugs de blog — Via getAllSlugs(), prioridade 0.7, changeFrequency: 'monthly'
  • Até 1.000 jogadores — Consulta db.collection('scores').distinct('playerName') diretamente no MongoDB. Cada nome gera /player/{nome} com prioridade 0.5 e changeFrequency: 'daily'

Isso garante que o Google descubra perfis de jogadores automaticamente — sem necessidade de links internos para cada um.

Security Headers no Edge

O next.config.mjs define 3 headers de segurança aplicados a todas as rotas via source: '/(.*)':

  • X-Content-Type-Options: nosniff — Impede MIME sniffing de conteúdo
  • X-Frame-Options: SAMEORIGIN — Bloqueia embedding em iframes de terceiros (anti-clickjacking)
  • Referrer-Policy: strict-origin-when-cross-origin — Controla dados no header Referer

Na Vercel, esses headers são injetados na camada edge — aplicados antes de qualquer resposta chegar ao navegador. Adicionalmente, poweredByHeader: false remove o header X-Powered-By: Next.js, reduzindo surface area de ataque. O robots.js complementa bloqueando crawlers de /api/ e /admin/.

Image Optimization na Vercel

O componente next/image serve imagens otimizadas via /_next/image — na Vercel, isso é processado por uma função serverless de otimização. As 11 imagens WebP do projeto passam por:

  • Redimensionamento automático — Baseado no atributo sizes (ex: (max-width: 768px) 100vw, 800px), gera múltiplas versões para o srcset
  • Negociação de formato — Se o navegador suporta AVIF (via header Accept), a Vercel pode converter de WebP para AVIF automaticamente
  • Cache na CDN — Imagens otimizadas são cacheadas no edge após a primeira requisição
  • Preload de hero images — Imagens com priority geram <link rel="preload"> no <head>, iniciando download antes do parser chegar ao <img>

As imagens OG (1200×630) são servidas como arquivos estáticos de /public — não passam pelo Image Optimization, pois são referenciadas diretamente nos metadados OpenGraph.

FAQ — Perguntas Frequentes sobre o Deploy

O deploy é automático?

Sim. Cada push para a branch main aciona build + deploy automático na Vercel. Preview deployments são criados para branches e pull requests, cada um com URL única. O rollback é instantâneo — basta selecionar um deploy anterior no dashboard da Vercel.

O seed.mjs roda durante o deploy?

Não. O script npm run seed (node scripts/seed.mjs) é executado manualmente, uma única vez, para criar os 2 patrocinadores iniciais e os índices do MongoDB ({ score: -1 }, { playedAt: -1 }, { name: 1 } em scores e { value: -1 } em sponsors). O script é idempotente — não insere dados se a collection já tem documentos.

O que acontece no cold start de uma Serverless Function?

O container é inicializado, o módulo é carregado, e getClientPromise() cria uma nova conexão MongoDB (~200-500ms). Requisições subsequentes no mesmo container warm reutilizam a conexão. A Vercel mantém containers warm por alguns minutos após a última invocação, reduzindo cold starts em APIs com tráfego regular.

O banco de dados fica exposto publicamente?

Não. O MONGODB_URI é server-only (sem prefixo NEXT_PUBLIC_), nunca incluído em bundles do client. Rotas admin são protegidas por validateAdmin() com comparação direta contra NAVISTRON_PASSWORD. O robots.js bloqueia /api/ de crawlers, e os endpoints públicos (scores GET, sponsors GET) retornam apenas dados já públicos no ranking.

Como configurar o deploy na Vercel do zero?

3 passos: (1) conectar o repositório Git no dashboard da Vercel; (2) configurar as 3 variáveis de ambiente — MONGODB_URI, NAVISTRON_PASSWORD e NEXT_PUBLIC_SITE_URL; (3) executar npm run seed manualmente para criar índices e dados iniciais. A Vercel detecta o Next.js automaticamente, sem configuração adicional.