Next.js Server Actions: RPC over the wire
- Last updated
- Prerequisites:
- Next.js App Router basics
- Understanding of React Server Components
- nextjs
- server-actions
- rpc
- forms
- react
- app-router
Read at your depth
The practical view
With 'use server' at the top of a file (or inline in a server component), you can define functions that run on the server but are called from the client: async function createPost(formData) { 'use server'; await db.insert(...); revalidatePath('/'); }. Use them in <form action={createPost}> or call them directly from event handlers in Client Components (passing the imported server function as a prop). The framework serializes arguments and return values over the wire. Forms get a progressive enhancement bonus: if JavaScript fails to load, the form still submits via a plain POST.
The same idea in other frameworks
// React 19: <form action={serverFunction}> is the same RSC
// mechanism — Server Actions are part of React, not Next.js// Angular: traditional HTTP client calls to an API route;
// no compiler-level RPC layer// Vue/Nuxt: useFetch/useAsyncData + server API routes;
// mutations via $fetch to endpoints// SvelteKit: form actions in +page.server.ts — same
// progressive-enhancement form + server mutation modelLegacy vs modern
Client-side fetch to an API route vs a Server Action
The fetch-to-API approach splits mutation and cache invalidation across two endpoints; a Server Action mutates and revalidates in one compiled RPC call.
await fetch('/api/posts', { method: 'POST', body: JSON.stringify({ title }) });
router.refresh();async function createPost(formData) {
'use server';
await db.insert(...);
revalidatePath('/');
}Interview gotchas
Context
This is the most important follow-up in production interviews — actions execute server-side with your privileges, so the security model matters more than the syntax.
The mechanical answer
Server Actions are effectively POST endpoints, so they inherit endpoint security requirements: authenticate and authorize every action, and validate all inputs server-side (they arrive as serialized data, not as 'trusted' function arguments). Next.js adds CSRF protection by checking the origin (the Action-Submission-Origin / Next-Action headers) and the action ID, and it enforces that actions only run on the same-origin request. Because actions have access to server-only modules (database clients, secrets), an action that blindly accepts user data is the same class of vulnerability as an unvalidated API handler — the RPC sugar must not lull you into trusting client-supplied arguments.
Trap
The naive answer is 'actions are safe because they are server-side'. Server-side does not mean safe — it means privileged. The sharp answer walks through: CSRF/origin validation is built into the protocol, but authorization, rate limiting, and schema validation are still the developer's job. Another trap: claiming actions are not exposed as public endpoints — they are publicly reachable POST routes, which is why you must never rely on 'nobody will guess the action ID' as a security boundary.
Context
This probes the RSC/flight payload mechanics behind the 'one round trip' claim — the interviewer wants the protocol, not just the DX.
The mechanical answer
When the form submits, the client sends a POST with the action ID and serialized FormData. The server runs the action, then builds a React Flight payload: it re-renders the affected server component tree (including any revalidatePath-triggered data refetches) and streams the serialized JSX back. The client parses the Flight stream and reconciles it into the existing React tree — patching only the parts that changed. Because the whole exchange uses React's serialization format, the UI updates and the server data stay consistent in a single request, and the navigation history is not polluted (unlike a redirect-and-refetch cycle).
Trap
A common wrong claim is 'the page fully reloads'. Form actions do not reload the page; they perform a background RPC and merge the returned Flight payload. Another trap: saying 'revalidatePath re-fetches on the client'. revalidatePath invalidates the Next.js data cache on the server; the refetch happens during the server-side re-render of the returned payload. Candidates who name the Flight payload and cache invalidation layers show they understand the architecture.