Capítulo 37 de 456
Server Components mudam onde e como dados são acessados; este guia define os três padrões de data fetching recomendados e as práticas de segurança para não vazar dados privados do server para o client.
fetch em Server Components, igual faria em Client Components; bom para apps grandes com equipe de backend separada.server-only que centraliza fetch, faz checagem de autorização e retorna DTOs mínimos; recomendado para projetos novos.experimental_taintObjectReference e experimental_taintUniqueValue (React) bloqueiam um objeto/valor específico de ser passado ao client; ativado via experimental.taint no next.config.js. É camada extra, não substitui sanitização no DAL.server-only package: import 'server-only' no topo de um módulo causa build error se ele for importado em código client, prevenindo execução client-side de lógica de servidor.next build, não gerando endpoint público.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: env var para fixar a chave de encriptação entre múltiplas instâncias self-hosted (base64, 16/24/32 bytes decodificados).serverActions.allowedOrigins: opção em next.config.js para listar origins confiáveis quando o app roda atrás de proxy reverso, evitando bloqueio por mismatch de Origin/Host.// data/auth.ts — DAL: helper cacheado por request
import { cache } from 'react'
import { cookies } from 'next/headers'
export const getCurrentUser = cache(async () => {
const cookieStore = await cookies()
const token = cookieStore.get('AUTH_TOKEN')
const decodedToken = await decryptAndValidate(token)
return new User(decodedToken.id)
})
cache() do React compartilha o mesmo valor entre Server Components na mesma request sem passar prop manualmente, evitando repassar dado sensível adiante.// app/actions.ts — Server Action reautorizando e checando ownership (IDOR)
'use server'
import { auth } from '@/lib/auth'
import { db } from '@/lib/db'
export async function deletePost(postId: string) {
const session = await auth()
if (!session?.user) throw new Error('Unauthorized')
const post = await db.post.findUnique({ where: { id: postId } })
if (post.authorId !== session.user.id) throw new Error('Forbidden')
await db.post.delete({ where: { id: postId } })
}
session?.user) e autorização (post.authorId !== session.user.id) dentro da própria action, nunca confiando no check feito na página.searchParams/inputs do client para decisões de acesso: isAdmin === (await searchParams).isAdmin é falsificável; sempre reverifique via cookies()/sessão no server.<Profile user={userData} /> com userData cru expõe todos os campos ao bundle client; retorne apenas os campos necessários.server-only + DTOs mínimos é a defesa mais robusta contra vazamento acidental de dado para o client.allowedOrigins atrás de proxy."use client" não vazando dado privado, validação/ownership dentro de "use server", e route.ts/proxy.ts (alto poder, exigem revisão extra).auth()/sessão usado em todo exemplo deste capítulo.process.env para segredos, conforme recomendado aqui.