Episode 2 — React Frontend Architecture NextJS / 2.21 — Working with Server Actions
2.21 — Quick Revision: Server Actions
One Page Cheat Sheet
| Topic | Remember |
|---|---|
| Directive | 'use server' at top of module (or inline on a function) |
| vs Route Handler | Action = app-internal mutation ergonomics; Handler = HTTP API for any client |
| Form wiring | <form action={fn}> — Server Components need no 'use client' |
| Extra args | fn.bind(null, id) — still verify ownership on server |
| Stateful forms | useActionState(action, initialState) — action gets (prev, formData) |
| Pending UI | useFormStatus() in child of <form> |
| Non-form call | startTransition(() => action(arg)) |
| Fresh RSC data | revalidatePath / revalidateTag |
| Navigation | redirect() / notFound() from server modules |
| Security | Session + authz + Zod every time — no “trusted because React” |
2.21.a — vs API routes (boundary)
- Server Action: remote procedure for same app; great for forms and colocated mutations.
- Route Handler: HTTP contract for arbitrary clients + webhooks.
2.21.b — use server (boundary)
- Marks server graph; keeps secrets/server deps off client bundle.
- Arguments/results must be serializable; think RPC hygiene.
2.21.c — Mutations & forms (UX)
- Pair
useActionStatewith Zod for inline errors. - Use
useFormStatusfor disabled submit buttons. - After writes,
revalidatePath/revalidateTagto avoid stale RSC caches.
2.21.d — Security & production
- Authn + authz + validation + rate limits on every mutation.
- Safe client errors; rich logs only server-side.
- CSRF posture: follow pinned Next guidance; defend high-risk flows explicitly.
Mental Diagram (30 seconds)
UI event → (form | transition) → Next runtime POST → 'use server' fn → DB → revalidate → UI
Phrases That Sound Senior
- “I colocate mutations but share domain services with Route Handlers.”
- “I treat actions like POST handlers: schema validate, authorize, rate limit.”
- “I pick Route Handlers when I need a documented HTTP contract.”
Self-check: spoken answers
| # | Prompt | Answer you should produce |
|---|---|---|
| 1 | What is a Server Action? | A server-only async function marked 'use server' invoked via Next’s runtime—great for same-app mutations without hand-written REST for every form. |
| 2 | vs Route Handler? | Handlers are public HTTP; actions are internal RPC-style calls tied to the Next app boundary. |
| 3 | Why .bind for ids? | Forms post FormData; bind injects trusted parameters while you still re-check authz server-side. |
| 4 | Stale UI after mutation? | Cached RSC tree—fix with revalidatePath/revalidateTag (and understand fetch tags). |
| 5 | Client validation enough? | Never—attackers can bypass; server Zod is the source of truth. |
| 6 | Stripe webhooks? | Route Handler verifying signatures—not a Server Action. |
| 7 | Pending button state? | useFormStatus in a child component under the <form>. |
| 8 | Production errors? | Structured failures for expected cases; generic message + server logs for unexpected. |