--- 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 (