--- alwaysApply: true description: Guide for when to use Server Actions vs API Routes in Next.js App Router --- # Server Actions vs API Routes - Decision Guide ## ✅ USE SERVER ACTIONS for: ### Quick Actions & Mutations - **TaskCard actions**: `updateTaskStatus()`, `updateTaskTitle()`, `deleteTask()` - **Daily checkboxes**: `toggleCheckbox()`, `addCheckbox()`, `updateCheckbox()` - **User preferences**: `updateTheme()`, `updateViewPreferences()`, `updateFilters()` - **Simple CRUD**: `createTag()`, `updateTag()`, `deleteTag()` ### Characteristics of Server Action candidates: - Simple, frequent mutations - No complex business logic - Used in interactive components (forms, buttons, toggles) - Need immediate UI feedback with `useTransition` - Benefit from automatic cache revalidation ## ❌ KEEP API ROUTES for: ### Complex Endpoints - **Initial data fetching**: `GET /api/tasks` with complex filters - **External integrations**: `POST /api/jira/sync` with complex logic - **Analytics & reports**: Complex data aggregation - **Public API**: Endpoints that might be called from mobile/external ### Characteristics that require API Routes: - Complex business logic or data processing - Multiple service orchestration - Need for HTTP monitoring/logging - External consumption (mobile apps, webhooks) - Real-time features (WebSockets, SSE) - File uploads or special content types ## 🔄 Implementation Pattern ### Server Actions Structure ```typescript // actions/tasks.ts 'use server' import { tasksService } from '@/services/tasks'; import { revalidatePath } from 'next/cache'; export async function updateTaskStatus(taskId: string, status: TaskStatus) { try { const task = await tasksService.updateTask(taskId, { status }); revalidatePath('/'); // Auto cache invalidation return { success: true, data: task }; } catch (error) { return { success: false, error: error.message }; } } ``` ### Component Usage with useTransition ```typescript // components/TaskCard.tsx 'use client'; import { updateTaskStatus } from '@/actions/tasks'; import { useTransition } from 'react'; export function TaskCard({ task }) { const [isPending, startTransition] = useTransition(); const handleStatusChange = (status) => { startTransition(async () => { const result = await updateTaskStatus(task.id, status); if (!result.success) { // Handle error } }); }; return (
{/* UI with loading state */}
); } ``` ## 🏗️ Migration Strategy When migrating from API Routes to Server Actions: 1. **Create server action** in `actions/` directory 2. **Update component** to use server action directly 3. **Remove API route** (PATCH, POST, DELETE for mutations) 4. **Simplify client** (remove mutation methods, keep GET only) 5. **Update hooks** to use server actions instead of HTTP calls ## 🎯 Benefits of Server Actions - **🚀 Performance**: No HTTP serialization overhead - **🔄 Cache intelligence**: Automatic revalidation with `revalidatePath()` - **📦 Bundle reduction**: Less client-side HTTP code - **⚡ UX**: Native loading states with `useTransition` - **🎯 Simplicity**: Direct service calls, less boilerplate ## 🚨 Anti-patterns to Avoid - Don't use server actions for complex data fetching - Don't use server actions for endpoints that need HTTP monitoring - Don't use server actions for public API endpoints - Don't mix server actions with client-side state management for the same data Remember: Server Actions are for **direct mutations**, API Routes are for **complex operations** and **public interfaces**.