A clean architectural pattern for handling optimistic mutations, server state invalidation, and type-safe Zod validation.
# How to Structure Server Actions & TanStack Query in Next.js
Modern full-stack React architecture in Next.js App Router presents a powerful question: when should you use native Server Actions directly, and when should you orchestrate them with TanStack Query?
Server Actions execute strictly on the server, benefiting from direct access to databases, secrets, and session cookies without exposing internal API endpoints. However, client-side coordination — such as optimistic state rollbacks, retry backoff, and caching — is often best managed by a dedicated query manager.
Every mutation must be verified against Murphy's Law:
- **Validate inputs with Zod** before executing any query. - **Authorize the active session** on the server — never trust client headers. - **Ensure idempotency** by accepting an idempotency token to prevent double-click errors.
export async function enrollStudentAction(workshopId: string, idempotencyKey: string) {
const session = await getVerifiedSession();
if (!session) throw new Error("Unauthorized");
// Guard against duplicate mutations
const existing = await findIdempotentKey(idempotencyKey);
if (existing) return existing.result;
return await db.enrollments.create({ data: { workshopId, userId: session.user.id } });
}When a user taps "Enroll Now" or "Submit Project", the UI should respond immediately with an optimistic status pill, while safely reverting to previous state if the mutation encounters a network interruption.
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: enrollStudentAction,
onMutate: async (newEnrollment) => {
await queryClient.cancelQueries({ queryKey: ['enrollments'] });
const previous = queryClient.getQueryData(['enrollments']);
queryClient.setQueryData(['enrollments'], (old) => [...old, newEnrollment]);
return { previous };
},
onError: (err, newEnrollment, context) => {
queryClient.setQueryData(['enrollments'], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['enrollments'] });
},
});We explore this pattern end-to-end in the **Next.js 16 Production Architecture & Turbopack Workshop**, where students build a production-grade education dashboard with zero layout shift.
Architect high-performance web applications using Server Components, caching strategies, and streaming SSR.
This breakdown of Server Actions vs TanStack Query really clarified optimistic rollback mechanics. Looking forward to applying it in the workshop project!

Smart Skill Workshop