// app/actions.ts
'use server';
import { z } from 'zod';
const schema = z.object({
email: z.string().email('Email invalid!')
});
export async function subscribeNewsletter(prevState: any, formData: FormData) {
const validated = schema.safeParse({
email: formData.get('email')
});
if (!validated.success) {
return { error: validated.error.errors[0].message };
}
// Aici faci insert-ul direct în DB
// await db.subscribe(validated.data.email);
return { success: true };
}Hai să facem lumină între Server Actions și API Routes în Next.js, fără hype-ul de pe Twitter. Ambele rulează pe server, dar le folosești complet diferit în producție. Vreau să îți arăt exact unde strălucește fiecare și unde îți prinzi urechile dacă faci alegerea greșită.
Am trecut un SaaS mediu (pe la 12k useri activi) de pe clasicele API Routes (pages/api sau Route Handlers) pe Server Actions în Next 14, iar pe parcurs am dat cu capul de pragul de sus de destule ori.
Form Submit: Unde Server Actions domină
Dacă ai de făcut un formular simplu de contact, un update de profil sau un checkout rapid, Server Actions sunt geniale. Scapi de tot boilerplate-ul de API: nu mai scrii fetch('/api/update-user', { method: 'POST' ... }), nu mai definești rute separate și tipizezi totul mult mai ușor.
Am redus codul de pe client cu vreo 30% doar trecând formularele pe Server Actions. Folosești useActionState, legi acțiunea direct de proprietatea action a form-ului și Next.js se ocupă de restul. Merge inclusiv cu progressive enhancement dacă userul are JavaScript dezactivat în browser (deși, să fim sinceri, câți useri reali mai au JS oprit în 2024?).
File Upload: Zona gri unde Server Actions încep să scârțâie
Aici am pățit-o urât. Trebuia să implementăm un upload de imagini de profil și documente PDF. Am zis: "Super, facem o acțiune de server, trimitem FormData direct și de acolo urcăm în S3".
Teoretic merge. Practic, la fișiere mai mari de 4-5MB, Server Actions încep să gâfâie pe infrastructurile serverless (cum e Vercel sau AWS Lambda). De ce? Pentru că tot fișierul este serializat în request-ul de POST către acțiunea respectivă, trece prin funcția lambda și abia apoi ajunge în S3. Plătești timp de execuție aiurea și riști timeout-uri costisitoare.
Pentru upload-uri, API Routes rămân sfinte. Ba chiar mai bine: folosești un API Route doar ca să generezi un presigned URL de S3 sau Cloudflare R2, iar clientul face upload direct în storage-ul extern. Server Actions sunt pur și simplu ineficiente pentru payload-uri binare mari.
Webhooks: Teritoriul exclusiv al API Routes
Dacă integrezi Stripe, Shopify sau orice alt serviciu terț care trimite webhook-uri, Server Actions ies complet din discuție.
De ce? Pentru că Server Actions au nevoie de contextul Next.js, de headere specifice și sunt gândite exclusiv pentru interacțiunea client-server din interiorul aplicației tale. Stripe are nevoie de un endpoint HTTP clasic (un URL curat de tipul /api/webhooks/stripe), unde să poată trimită un POST brut și unde tu să poți valida semnătura payload-ului folosind request-ul brut (raw body). Aici Route Handlers sunt singura opțiune corectă.
Trade-off-uri și ce am învățat
Server Actions sunt excelente pentru UX rapid, mutarea stării pe server și validare strânsă cu biblioteci ca Zod. Dar vin cu un cost mare de debugging: erorile din Server Actions sunt uneori mascate din motive de securitate în producție și te trezești cu un sec "An error occurred on the server" în consolă, fiind obligat să cauți prin logurile din cloud.
Dacă ai nevoie de un API public, de integrare cu aplicații mobile sau de procesare de fișiere mari, API Routes (Route Handlers) sunt în continuare calea de urmat.
Voi ce folosiți mai des în ultima vreme? Ați mutat complet formularele pe acțiuni sau ați rămas la clasicul fetch către /api?