Server Actions: mutação sem API route
Antes, toda mutação exigia criar uma API route e chamar fetch nela. Agora um form pode chamar uma função de servidor direto.
Historicamente, qualquer mutação — criar, editar, apagar algo — significava criar um endpoint (route.ts) e chamar ele via fetch do client, como no projeto guiado da lição 10. Server Actions permitem pular essa etapa: uma função marcada com "use server" pode ser chamada direto por um componente, inclusive como action de um <form>.
"use server" — uma função que roda no servidor
"use server";
import { revalidatePath } from "next/cache";
export async function criarTarefa(formData: FormData) {
const titulo = formData.get("titulo") as string;
if (!titulo || titulo.length < 3) throw new Error("Título muito curto");
await db.tarefa.create({ data: { titulo } });
revalidatePath("/tarefas"); // invalida o cache da rota, próxima visita busca dado novo
}Essa função nunca vira JavaScript enviado ao navegador — o Next.js gera, por baixo dos panos, uma requisição que executa ela no servidor. revalidatePath avisa o Next que os dados daquela rota mudaram, sem você reimplementar cache manualmente.
Form chamando a Server Action direto
import { criarTarefa } from "./actions";
export function NovaTarefaForm() {
return (
<form action={criarTarefa} className="flex gap-2">
<input name="titulo" placeholder="Nova tarefa" className="flex-1 border rounded-lg px-3 py-2" />
<button type="submit" className="bg-blue-600 text-white px-4 py-2 rounded-lg">
Adicionar
</button>
</form>
);
}Repare que NovaTarefaForm nem precisa de "use client" aqui — não há onSubmit, useState nem fetch manual. O action={criarTarefa} é a submissão real do formulário; o Next.js conecta isso à Server Action.
Quando ainda faz sentido ter uma API route de verdade
Server Actions são pensadas pra mutações chamadas pela sua própria UI React. Uma API route continua sendo a peça certa quando quem consome a mutação não é o seu próprio app Next — um webhook de um serviço de pagamento, um aplicativo mobile separado, uma integração pública de terceiros. Nesses casos você precisa de uma URL HTTP estável e documentável, não de uma função interna do seu bundle.
Exercício
Pegue o NovaTarefaForm da lição 10, que faz fetch("https://api.exemplo.com/tarefas", { method: "POST" }) dentro de um onSubmit num Client Component, e reescreva-o usando Server Action.