| name | data |
| description | Use when implementing data fetching, API calls, server/client components, or SWR hooks |
Source Cursor rule: .cursor/rules/data.mdc.
Original Cursor alwaysApply: false.
Data Fetching
Core Pattern: Server → Client → SWR
1. Server Page Fetches Data
export default async function TasksPage({ params }: { params: Promise<{ orgId: string }> }) {
const { orgId } = await params;
const tasks = await getTasks(orgId);
return <TaskListClient organizationId={orgId} initialTasks={tasks} />;
}
2. Client Component Receives Initial Data
'use client';
export function TaskListClient({ organizationId, initialTasks }: Props) {
const { tasks, createTask, updateTask } = useTasks({
organizationId,
initialData: initialTasks,
});
}
3. SWR Hook with fallbackData
export function useTasks({ organizationId, initialData }: UseTasksOptions) {
const { data, mutate } = useSWR(
['/v1/tasks', organizationId],
async ([endpoint, orgId]) => {
const response = await apiClient.get(endpoint, orgId);
return response.data?.tasks ?? [];
},
{ fallbackData: initialData }
);
const createTask = async (input: CreateTaskInput) => {
await apiClient.post('/v1/tasks', input, organizationId);
mutate();
};
const updateTask = async ({ taskId, input }: { taskId: string; input: UpdateTaskInput }) => {
await apiClient.put(`/v1/tasks/${taskId}`, input, organizationId);
mutate();
};
return { tasks: data ?? [], createTask, updateTask, mutate };
}
API Client
Use apiClient from @/lib/api-client:
import { apiClient } from '@/lib/api-client';
await apiClient.get<ResponseType>('/v1/endpoint', organizationId);
await apiClient.post<ResponseType>('/v1/endpoint', body, organizationId);
await apiClient.put<ResponseType>('/v1/endpoint', body, organizationId);
await apiClient.delete('/v1/endpoint', organizationId);
Server vs Client Components
Layouts = server. Interactive logic in separate client components.
export default function Layout({ children }) {
return (
<PageLayout>
<PageHeader title="Title" />
<ClientTabs /> {/* Client component */}
{children}
</PageLayout>
);
}
'use client';
export function ClientTabs() {
const router = useRouter();
}
State Management
No nuqs - use React state or Next.js patterns:
const [isOpen, setIsOpen] = useState(false);
const router = useRouter();
const searchParams = useSearchParams();
import { useQueryState } from 'nuqs';
Rules
const { orgId } = await params;
const { data } = useSWR(key, f, { fallbackData });
await apiClient.get('/v1/endpoint', orgId);
useSWR(['/v1/tasks', orgId], fetcher);
const orgId = session?.activeOrganizationId;
const { data } = useSWR('/api/data');
await fetch('/api/endpoint');
File Structure
app/(app)/[orgId]/tasks/
├── page.tsx # Server - fetches data
├── components/
│ └── TaskListClient.tsx # Client - receives initialData
├── hooks/
│ └── useTasks.ts # SWR hook with mutations
└── data/
└── queries.ts # Server-side queries