Server Actions
Priority: P1 (HIGH)
[!WARNING] If project uses
pages/directory instead of App Router, ignore this skill entirely.
Build action files as secure server entrypoints, not as thin wrappers around unsafe form data.
Recipe
- Define action in
actions.tswith'use server'. - Parse input with a schema before touching storage or services, for example
z.object({ ... }).safeParse(...)orformData.get('title')-> validated shape. - Authorize inside the action; middleware alone is not enough.
- Delegate to DAL/service layer; keep storage details out of route UI files.
- Revalidate exact tags/paths owned by the mutation.
- Expose pending and optimistic state with
useFormStatus,useActionState,useTransition, oruseOptimisticas needed. Typical form wiring is<form action={createPost}>or<form action={action}>, withuseFormStatus()anddisabled={pending}inside the submit child.
Verify
- [ ] Action validates
FormDataor arguments before processing. - [ ] Action performs authn/authz inside the server function.
- [ ] Mutations call DAL/service code, not raw storage from components.
- [ ] Success path revalidates tags or paths.
- [ ] Redirects or thrown errors are handled in the expected control flow.
Anti-Patterns
- No unvalidated Server Action inputs: Always validate with Zod before processing.
- No skipped auth checks: Verify session/user inside every action, not middleware.
- No actions defined inside components: Define in
actions.tsto avoid closure bugs. - No
redirect()in try/catch:redirect()throws; catching it suppresses redirect.