diff --git a/.cursor/rules/server-actions.mdc b/.cursor/rules/server-actions.mdc new file mode 100644 index 0000000..ec2fdb1 --- /dev/null +++ b/.cursor/rules/server-actions.mdc @@ -0,0 +1,113 @@ +--- +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 ( +