"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
const profileSchema = z.object({
email: z.string().email("Email invalid"),
name: z.string().min(2, "Numele e prea scurt"),
});
export async function updateProfile(prevState: any, formData: FormData) {
const validated = profileSchema.safeParse({
email: formData.get("email"),
name: formData.get("name"),
});
if (!validated.success) {
return { errors: validated.error.flatten().fieldErrors };
}
await db.user.update({
where: { id: "user_123" },
data: validated.data,
});
revalidatePath("/settings/profile");
return { success: true, errors: null };
}Am trecut recent un proiect SaaS cu 15k utilizatori activi de pe clasicele API Routes pe Server Actions în Next.js. Am câștigat la capitolul DX și am scăpat de vreo 30% din codul boilerplate de fetch, dar am dat și cu capul de pragul de sus. Hai să-ți spun exact unde merită mutate și unde e mai bine să rămâi pe API Routes tradiționale.
Form Submit: Victoria clară pentru Server Actions
Dacă ai formulare simple sau medii — login, editare profil, comentarii — Server Actions sunt genial de comode. Scapi de useState pentru fiecare input, scapi de fetch('/api/user', { method: 'POST' }) și de gestionarea manuală a erorilor de rețea.
Proiectul de care ziceam avea vreo 20 de formulare de CRUD. Trecerea pe Server Actions cu useActionState și zod ne-a curățat codebase-ul masiv. Câștigi revalidare automată de cache prin revalidatePath sau revalidateTag, plus că ai type safety direct între server și client fără eforturi cu setup-uri complexe de tRPC.
Trade-off-ul? Dacă ai nevoie de optimistic UI complex cu rollback-uri pe stări colaterale, useOptimistic poate deveni rapid un vizuină de iepure (rabbit hole) dacă nu ești atent la stările locale din React.
File Upload: Atenție la limitele Serverless
Aici am dat prima oară de zid. Am încercat să urcăm fișiere de 20-30MB direct printr-un Server Action. În medii serverless precum Vercel sau AWS Lambda, payload-ul maxim pe un request de Server Action e limitat (4MB implicit pe Vercel).
Dacă încerci să trimiți un FormData cu un fișier mare prin Server Action, procesul trece prin Lambda-ul de Next.js, consumă memorie scumpă și riști un timeout urât.
Regula mea după experiența asta:
- Fișiere mici (avatar sub 1MB): merge un Server Action cu transformare în buffer/base64 sau streaming direct.
- Fișiere mari (PDF-uri, video, imagini hi-res): API Route tradițional care generează un Presigned URL pentru S3/Cloudflare R2, iar clientul urcă fișierul direct în storage bucket.
Webhook-uri terțe: Rămâi 100% pe API Routes
Aici nu există dezbatere. Când integrezi Stripe, Shopify sau GitHub webhooks, Server Actions ies complet din discuție.
Webhook-urile de la Stripe au nevoie de acces la request-ul brut (rawBody) pentru a verifica semnătura criptografică cu stripe.webhooks.constructEvent. Server Actions sunt abstracții făcute pentru comunicarea internă dintre clientul tău React și serverul tău. Nu ai un URL public curat, predictibil și fără headere interne pe care să-l dai Stripe-ului ca endpoint.
Pentru tot ce înseamnă apeluri externe care vin SPRE serverul tău (API public, webhook-uri, cron jobs), API Route-urile din app/api/.../route.ts sunt singura opțiune corectă.
Concluzie
Server Actions nu înlocuiesc API Route-urile, ci le completează. Folosește Server Actions pentru mutațiile interne de stare alimentate de utilizator și UI, iar API Route-urile pentru integrări externe, uploads mari și API-uri publice.
Tu ce abordare folosești la proiectele noi? Mai scrii API Routes pentru formulare sau ai trecut 100% pe Server Actions?