feat: add decision guide for Server Actions vs API Routes
- Introduced a new guide in `server-actions.mdc` to clarify when to use Server Actions versus API Routes in Next.js. - Included examples, implementation patterns, migration strategies, and anti-patterns to avoid for better developer understanding.
This commit is contained in:
113
.cursor/rules/server-actions.mdc
Normal file
113
.cursor/rules/server-actions.mdc
Normal file
@@ -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 (
|
||||
<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**.
|
||||
Reference in New Issue
Block a user