Patterns

Patterns — Next.js 16

Fetch paralelo com Promise.all

Quando usar: duas+ chamadas de dado independentes no mesmo componente. Como: dispare os fetches sem await, guarde as Promises, depois await Promise.all([a, b]). Trade-offs: uma falha rejeita tudo; use Promise.allSettled se falha parcial for tolerável.

Maximizar o static shell (push dynamic access down)

Quando usar: layout/página lê cookies()/headers()/params mas parte da árvore pode ficar estática. Como: não dê await no dado no componente top-level; passe a Promise a um filho envolto em <Suspense> que faz o await. Trade-offs: mais um nível de componente e fallback visual; só o filho sai do shell estático.

Streaming servidor→cliente com use()

Quando usar: Client Component precisa de dado do servidor sem bloquear render inicial. Como: no Server Component, chame o fetch sem await, passe a Promise como prop dentro de <Suspense>; no filho, const data = use(promiseProp). Trade-offs: a Promise não pode ser recriada a cada render do Client Component.

Runtime value → cached function

Quando usar: dado depende de cookie/sessão mas você quer manter prefetch (em vez de use cache: private). Como: leia a runtime API fora do cache, extraia só o valor necessário e passe como argumento pra uma função use cache (entra na cache key). Trade-offs: cache in-memory por instância a menos que use use cache: remote.

Cache em camadas (use cache + remote + private)

Quando usar: app precisa combinar dado público compartilhável com dado por-usuário. Como: use cache pro shell público, use cache: remote pra compartilhar entre instâncias, use cache: private pro que depende de cookies/headers. Trade-offs: mais complexidade de composição; exige cache handler remoto configurado pra segunda camada.

Invalidação: updateTag vs revalidateTag

Quando usar: mutação precisa refletir imediatamente (read-your-own-writes) vs. pode tolerar stale-while-revalidate. Como: updateTag() em Server Action expira na hora e força recompute; revalidateTag()/revalidatePath() serve stale enquanto revalida em background. Trade-offs: updateTag só funciona em Server Actions; o próximo leitor paga o custo do recompute.

Revalidação sob demanda em vez de TTL fixo

Quando usar: conteúdo de CMS/fonte externa muda raramente e imprevisivelmente. Como: cachear com cacheTag + cacheLife('max'); configurar webhook da fonte pra chamar revalidateTag quando o conteúdo muda de fato. Trade-offs: exige infra de webhook; TTL fixo é mais simples mas desperdiça revalidação.

Deferred random/timestamp value

Quando usar: rota precisa de valor único por request (ID, timestamp) sob Cache Components. Como: chame connection() antes do valor não-determinístico, dentro de componente em <Suspense>. Trade-offs: o componente sai do shell estático; alternativa é use cache se o valor puder ser compartilhado.

Extrair componente interativo em vez de marcar tudo Client

Quando usar: só um pedaço de UI (ex. search bar) precisa de estado/eventos. Como: mantenha o pai como Server Component; "use client" só no filho interativo, importado normalmente. Trade-offs: exige achar a fronteira certa; marcar o pai inteiro é mais simples mas infla o bundle.

DAL (Data Access Layer) centralizada

Quando usar: autorização precisa ser consistente entre Server Components, Actions e Route Handlers. Como: verifySession() memoizado com React.cache(), chamado por todo data fetcher; redireciona/lança erro se sessão inválida. Trade-offs: exige disciplina pra nunca pular a DAL ao criar fetcher novo.

DTO para exposição de dados

Quando usar: dado do banco tem campo sensível (senha, PII). Como: função retorna só os campos necessários, com lógica de visibilidade por role. Trade-offs: mais boilerplate por endpoint, evita vazamento acidental.

Pending state com useActionState/useOptimistic

Quando usar: form via Server Action precisa de feedback de loading/erro sem JS extra. Como: useActionState retorna [state, action, pending]; useOptimistic mostra valor otimista até a Action resolver. Trade-offs: rollback em erro precisa ser tratado manualmente no reducer/state.

Optimistic checks no Proxy

Quando usar: redirecionar por permissão/sessão em toda rota (incl. prefetched) sem custo de DB a cada request. Como: decriptar cookie de sessão em proxy.ts, comparar contra protectedRoutes, NextResponse.redirect. Trade-offs: nunca substitui checagem segura (DB) próxima da mutação; é só a primeira linha de defesa.

Modal com deep link via Intercepting + Parallel Routes

Quando usar: modal precisa ter URL própria (compartilhável) mas abrir sem navegação completa quando clicado in-app. Como: slot @modal (Parallel) com pasta (.)photo (Intercepting) que sobrescreve a rota real só na navegação client-side. Trade-offs: hard navigation/refresh renderiza a rota completa, não o modal.

Migração incremental Pages → App Router

Quando usar: app grande em Pages Router que não pode migrar tudo de uma vez. Como: app/ e pages/ coexistem (App tem precedência); migre rota por rota, mantendo _app/_document até o fim. Trade-offs: dois modelos de data fetching coexistindo até a migração terminar.

Multi-Zones via rewrites()

Quando usar: micro-frontend com múltiplos apps Next.js independentes sob um domínio. Como: app principal usa rewrites() (ou basePath) pra rotear paths pra outros apps/deploys; assetPrefix evita colisão de assets. Trade-offs: navegação entre zonas é hard navigation, não client-side.

Deploy standalone (Output File Tracing)

Quando usar: deploy self-hosted (Docker) sem node_modules completo. Como: output: 'standalone' gera .next/standalone com server.js mínimo e só os arquivos tracados via @vercel/nft. Trade-offs: assets de public//.next/static precisam ser copiados manualmente pro standalone.

Version-safe rolling deploy com deploymentId

Quando usar: deploy multi-container/rolling onde cliente antigo pode bater em servidor novo. Como: deploymentId (ou NEXT_DEPLOYMENT_ID) força hard navigation ao detectar deploy diferente entre cliente/servidor; generateBuildId mantém build ID consistente entre containers. Trade-offs: hard navigation perde estado client-side na primeira troca pós-deploy.

Cache handler distribuído (self-hosting multi-instância)

Quando usar: self-hosted com múltiplas instâncias que precisam compartilhar cache de use cache/ISR. Como: cacheHandler/cacheHandlers em next.config.js aponta pra storage compartilhado (Redis, etc.) em vez do cache em memória padrão. Trade-offs: exige infra extra; sem isso, cada instância tem cache independente.

CSP com nonce via Proxy

Quando usar: política de segurança estrita exige bloquear scripts/estilos inline não autorizados. Como: gerar nonce aleatório em proxy.ts, propagar via header customizado, ler com headers() e aplicar em <script nonce>. Trade-offs: nonce exige request dinâmica (não cacheável); SRI é alternativa que preserva static generation.

Allowlist estrita de imagens remotas

Quando usar: <Image> carrega de hosts externos (CMS, CDN de terceiros). Como: images.remotePatterns no next.config.js, listando protocolo/hostname/pathname explicitamente. Trade-offs: qualquer host novo exige redeploy do config; domains (legado) é menos preciso.

Shallow routing para atualizar query sem refetch

Quando usar: mudar searchParams (filtro, paginação) sem re-rodar data fetching da página. Como: useSearchParams + router.push/replace (scroll: false), preservando o resto da query. Trade-offs: Pages Router usa { shallow: true }; no App Router é implícito ao não mudar segmento.