---
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 (
    <div className={isPending ? 'opacity-50' : ''}>
      {/* UI with loading state */}
    </div>
  );
}
```

## 🏗️ 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**.