Build type-safe server actions for Next.js forms. Validation, error handling, optimistic updates, and revalidation — the modern mutation pattern.
## Task Type-safe server actions for form mutations in Next.js App Router. ## Requirements - Next.js 15+ with Server Actions - Zod for validation - TypeScript strict mode ## Pattern ```typescript // 1. Define action with validation "use server"; const CreatePostSchema = z.object({ title: z.string().min(1).max(200), body: z.string().min(10), tags: z.array(z.string()).max(5), }); type ActionState = { success: boolean; errors?: Record<string, string[]>; data?: { id: string; slug: string }; }; export async function createPost( prevState: ActionState, formData: FormData ): Promise<ActionState> { // Validate const parsed = CreatePostSchema.safeParse({ title: formData.get("title"), body: formData.get("body"), tags: formData.getAll("tags"), }); if (!parsed.success) { return { success: false, errors: parsed.error.flatten().fieldErrors }; } // Auth check const session = await auth(); if (!session) return { success: false, errors: { _form: ["Not authenticated"] } }; // Mutate const post = await db.post.create({ data: { ...parsed.data, authorId: session.user.id } }); // Revalidate revalidatePath("/posts"); return { success: true, data: { id: post.id, slug: post.slug } }; } ``` ## Client Component ```typescript "use client"; // 2. Use with useActionState + optimistic updates const [state, action, isPending] = useActionState(createPost, { success: false }); // 3. Show field errors inline // 4. Redirect on success // 5. Toast on error ``` ## Implementation Notes 1. Always validate on server — client validation is UX, not security 2. Return typed ActionState — never throw from server actions 3. Use revalidatePath/revalidateTag after mutations 4. Add rate limiting for public-facing actions 5. Include CSRF protection (Next.js handles this automatically) 6. Test: happy path, validation failure, auth failure, DB error
No gallery images yet.