Server Actions și formulare
Mutații sigure, useActionState, validare și UI optimist.
Actualizat
Ce este o Server Action
O Server Action e o funcție async marcată cu 'use server', pe care o apelezi din UI (un formular, un buton), dar care rulează pe server. Next creează automat un endpoint și face request-ul pentru tine.
Le folosești pentru mutații: creare, editare, ștergere, salvare. Înlocuiesc combinația „API route + fetch + loading state” pentru propriile formulare.
// features/create-post/api/create-post.ts
'use server'
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(prev: State, formData: FormData) {
const session = await auth()
if (!session) return { error: 'Neautorizat' }
const title = String(formData.get('title') ?? '').trim()
if (title.length < 3) return { error: 'Titlul e prea scurt' }
const post = await db.post.create({ data: { title, authorId: session.userId } })
updateTag('posts')
redirect(`/posts/${post.id}`)
}Aplicația asta salvează progresul tău exact așa (entities/progress/api/save-progress.ts).
Securitate — cel mai important lucru
O Server Action e un endpoint POST public. Oricine o poate apela direct, fără UI-ul tău, cu ce date vrea.
- Verifică autentificarea și autorizarea în fiecare acțiune („e logat?” și „are voie pe resursa asta?”).
- Validează tot inputul (zod / valibot) —
formDatapoate conține orice. - Nu returna date sensibile.
Moduri de a apela o Server Action
| Mod | Cod | Când |
|---|---|---|
<form action={fn}> |
primește FormData |
implicit pentru formulare; merge și fără JS |
useActionState(fn, initial) |
primești [state, action, pending] |
afișezi erori / mesaje / pending |
<button formAction={fn}> |
mai multe acțiuni într-un form | „Salvează” vs „Șterge” |
| apel direct într-un handler | await fn(args) |
interacțiuni care nu sunt formulare (like, salvare automată) |
Formular cu useActionState
'use client'
import { useActionState } from 'react'
export function PostForm() {
const [state, action, pending] = useActionState(createPost, { error: null })
return (
<form action={action}>
<input name="title" required />
<p aria-live="polite">{state.error}</p>
<button disabled={pending}>{pending ? 'Se salvează…' : 'Salvează'}</button>
</form>
)
}Cu useActionState, acțiunea primește state-ul anterior ca prim argument și FormData ca al doilea.
Progressive enhancement: un formular randat pe server cu o Server Action funcționează chiar dacă JS nu s-a încărcat încă.
Fluxul complet, cu validare Zod și valori păstrate după eroare, e în Formulare cu Server Actions.
UI optimist
const [optimisticLikes, addLike] = useOptimistic(likes, (state, n: number) => state + n)
<form action={async () => { addLike(1); await like(postId) }}>UI-ul se schimbă instant; dacă acțiunea eșuează, React revine la valoarea reală.
Server Action vs Route Handler
| Server Action | Route Handler |
|---|---|
| formulare și butoane din aplicația ta | API consumat de alții (mobile, parteneri) |
| mutații | webhooks (Stripe, CMS) |
| apelate secvențial, una câte una | răspunsuri non-HTML (RSS, fișiere, JSON public) |
Citirea datelor nu se face prin Server Actions — pentru asta există Server Components.
Pe scurt
- Server Action = funcție
'use server'apelată din UI, rulată pe server; pentru mutații. - Fiecare acțiune: auth + autorizare + validare.
<form action>+useActionStatepentru erori și pending;updateTagdupă mutație.