// App Router: Exemplu de Server Action vs API Route
// 1. SERVER ACTION (În interiorul componentei sau în actions.ts)
export async function updateProfile(formData: FormData) {
'use server';
const userId = await getSessionUserId();
const name = formData.get('name') as string;
if (!name || name.length < 3) throw new Error('Nume invalid');
await db.user.update({ where: { id: userId }, data: { name } });
}
// 2. API ROUTE HANDLER (În app/api/stripe/route.ts)
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const payload = await req.text();
const sig = req.headers.get('stripe-signature');
try {
const event = stripe.webhooks.constructEvent(payload, sig!, process.env.STRIPE_WEBHOOK_SECRET!);
await handleStripeEvent(event);
return NextResponse.json({ received: true });
} catch (err: any) {
return NextResponse.json({ error: err.message }, { status: 400 });
}
}De când Next.js a trecut Server Actions în zona de „stable”, am văzut o tendință periculoasă: mulți devs aruncă la gunoi toate API route-urile clasice. Am făcut și eu greșeala asta la un proiect mărișor, cu vreo 12k utilizatori activi. Mi s-a părut că am dat de Sfântul Graal al dezvoltării rapide, dar m-am lovit rapid de realitate.
Nu totul trebuie să fie o acțiune de server. Există scenarii clare unde vechile API-uri rămân sfinte, iar astăzi vreau să vedem exact unde tragem linia între ele, pe baza unor cazuri reale.
Formulare simple și mutații de date: Server Actions sunt rege
Dacă ai de făcut un formular clasic de update de profil sau o adăugare de comentariu, Server Actions sunt geniale. Am eliminat cam 30% din codul de boilerplate (fără fetch, fără stări de loading gestionate manual prin useState dacă folosești useTransition, fără controllere separate).
În plus, ai tipizare directă din cutie. Clientul apelează funcția de server direct ca pe o funcție locală, iar TypeScript știe exact ce parametri primește.
Totuși, mare atenție la securitate. Server Actions sunt, în spate, tot niște endpoint-uri POST generate de Next.js. Trebuie să faci validarea input-ului (cu Zod, de exemplu) și verificarea sesiunii direct în interiorul acțiunii, exact cum ai face-o într-un controller clasic.
File Upload: De ce Server Actions s-ar putea să-ți crape aplicația
Aici am pățit-o urât. Aveam un formular unde utilizatorii încărcau imagini de profil și PDF-uri pentru facturi. Am zis să folosesc Server Actions pentru că trimit direct FormData.
Merge brici pe local cu fișiere mici. În producție însă, pe Vercel sau alte platforme serverless, ai o limită de payload de 4.5MB. Când un user a încercat să urce un PDF scanat de 15MB, Server Action-ul a dat crash instant cu timeout și eroare de dimensiune depășită.
Cum am rezolvat-o: Pentru fișiere mari, soluția optimă este tot un API Route (sau chiar un apel direct din client către S3/Cloudinary folosind un pre-signed URL). Generezi URL-ul securizat pe server printr-o metodă rapidă, iar upload-ul propriu-zis se face prin streaming direct din browser, ocolind serverul tău Next.js.
Webhook-uri de la terți: API Routes sunt obligatorii
Dacă integrezi Stripe, Lemon Squeezy sau orice serviciu care îți trimite un webhook când se confirmă o plată, Server Actions ies complet din discuție.
Serviciile externe nu știu să interacționeze cu mecanismul intern de Server Actions din React. Ele au nevoie de un endpoint HTTP standard (de exemplu, /api/webhooks/stripe), care să răspundă cu un status 200 OK și unde poți accesa corpul request-ului în format brut (raw body) pentru a verifica semnătura criptografică a webhook-ului.
La Stripe, dacă nu validezi semnătura folosind request-ul brut, ești vulnerabil la atacuri de tip replay. În Server Actions nu ai acces curat și direct la request-ul HTTP brut în același mod în care o faci într-un Route Handler clasic.
Concluzie: Cum împarți arhitectura?
Regula mea de aur după un an de experimente este simplă:
- Folosește Server Actions pentru interacțiuni strânse client-server în interiorul aplicației tale (formulare, butoane de tip like, ștergeri de rânduri dintr-un tabel, mutări de stare).
- Folosește API Routes (Route Handlers) pentru tot ce înseamnă integrări externe, webhook-uri, rute publice consumate de o aplicație mobilă sau scenarii de streaming de fișiere mari.
Voi cum ați împărțit lucrurile în ultimele proiecte? Mai scrieți API Routes pentru formulare simple sau ați trecut complet pe Server Actions?