Capítulo 79 de 456

Server Actions

Core Idea

Detalha o comportamento específico do Next.js para Server Actions (React Server Functions invocadas via <form action>, formAction, ou transição client-side): modelo de resposta em roundtrip único, dispatch sequencial, fronteira de segurança e integração com cache.

Key Concepts

  • Dispatch sequencial: o Next.js despacha Server Actions uma de cada vez por cliente — a segunda espera a primeira terminar. Promise.all no cliente não paraleliza Server Actions; paralelize dentro de uma única action, ou use Server Component/Route Handler.
  • Resposta única (data + UI): quando a action dispara revalidação imediata, a mesma requisição HTTP retorna o valor de retorno da action E o RSC Payload re-renderizado da rota atual, no mesmo Flight stream — sem fetch de follow-up.
  • Gatilhos de re-render incluído: updateTag, revalidatePath, mutação de cookies via cookies(), e redirect incluem re-render na mesma resposta; revalidateTag com perfil stale-while-revalidate NÃO inclui (marca para refresh em background).
  • CSRF check: Origin comparado a Host/X-Forwarded-Host; mismatches rejeitados. Configurável via serverActions.allowedOrigins.
  • Body size limit: 1MB por padrão, configurável via serverActions.bodySizeLimit.
  • Encrypted action IDs + dead code elimination: referências de action são criptografadas no build; Server Functions não usadas são removidas do bundle cliente.
  • Closure variable encryption: variáveis capturadas por uma action inline são criptografadas antes de ir ao cliente; usar NEXT_SERVER_ACTIONS_ENCRYPTION_KEY estável em deployments multi-instância.
  • updateTag: expira tag imediatamente, a próxima leitura (inclusive o re-render da própria resposta) espera dado fresco — use para read-your-own-writes. Server Actions apenas.
  • revalidateTag: refresh stale-while-revalidate com cache-life profile — leituras seguintes recebem valor stale enquanto busca fresca ocorre em background; o próprio re-render NÃO espera.
  • revalidatePath: invalida por path de URL, quando tagging é overkill para uma única rota afetada.
  • refresh: reobtém o RSC Payload da rota atual sem invalidar cache — usar quando a view depende de estado fora do cache.
  • Action ID rotation: cada deploy gera novos action IDs (rotacionados no máximo a cada 14 dias mesmo sem mudança de código); cliente com build antigo pode invocar ID inexistente, gerando erro "Failed to find Server Action".

Code Examples

'use server'
import { revalidatePath } from 'next/cache'
import { auth } from '@/lib/auth'
import { db } from '@/lib/db'

export async function createPost(formData: FormData) {
  const session = await auth()
  if (!session?.user) throw new Error('Unauthorized')

  await db.post.create({
    data: { title: String(formData.get('title')), authorId: session.user.id },
  })

  revalidatePath('/posts')
}
  • O que demonstra: mutação + invalidação de cache completam em um único roundtrip; código após redirect não executaria (ele lança exceção), então revalidação deve vir antes.
'use server'
import { auth } from '@/lib/auth'
import { db } from '@/lib/db'

// Safe: take only the change, derive identity from the session, look up by ownership.
export async function completeItem(itemId: string) {
  const session = await auth()
  if (!session?.user) return

  const item = await db.item.findFirst({
    where: { id: itemId, ownerId: session.user.id },
  })
  if (!item) return

  await db.item.update({ where: { id: item.id }, data: { completed: true } })
}
  • O que demonstra: enviar apenas o ID + mudança e derivar identidade/ownership da sessão, em vez de confiar no objeto inteiro vindo do cliente.

Anti-patterns

  • Usar Promise.all no cliente pra paralelizar Server Actions: o dispatcher do Next.js as serializa por cliente de qualquer forma; a paralelização precisa acontecer dentro de uma única action.
  • Confiar em gating de renderização como segurança ("só renderizo o form se autenticado"): não é fronteira de segurança, pois requisições podem ser enviadas sem passar pela UI.
  • Aceitar o objeto inteiro do cliente e confiar no id/ownership nele: validação de schema (zod) só checa forma, não posse — sempre re-derive ownership a partir da sessão.
  • Retornar registros de banco crus da action: os retornos são serializados ao cliente; molde ao que a UI precisa, não ao schema do banco.

Key Takeaways

  1. Uma Server Action que chama updateTag/revalidatePath/cookies() (set/delete)/redirect ganha re-render incluído na mesma resposta; revalidateTag (SWR) não.
  2. Toda Server Action é um endpoint POST alcançável por qualquer um que replique a requisição — trate como não confiável e autentique/autorize dentro da action, não apenas na UI.
  3. Escolha a API de revalidação pelo requisito: updateTag para read-your-own-writes imediato, revalidateTag para refresh em background, revalidatePath para invalidação simples por rota, refresh para reler estado fora do cache.
  4. Em deployments com rolling updates, mantenha NEXT_SERVER_ACTIONS_ENCRYPTION_KEY estável entre instâncias e trate "Failed to find Server Action" como caminho de retry na UI, não falha dura.

Connects To

  • Self-Hosting (ch078): mesma chave NEXT_SERVER_ACTIONS_ENCRYPTION_KEY e considerações de multi-instância.
  • Data Security guide: padrões completos de Data Access Layer, tainting de retorno e rate limiting.
  • How Revalidation Works: modelo subjacente de updateTag/revalidateTag/revalidatePath/refresh.