SuperAPI AI Directory

data-fetching

Data & analytics

View repository

Updated 10-15 23:58

About this skill

Data Fetching (Next.js)

Canonical patterns for fetching data in Next.js App Router.

Fetch Strategy Decision

Need data for a page / route?
├─ Data is public / user-agnostic?
│   ├─ Static (rarely changes) → Server Component + fetch() with { cache: 'force-cache' }
│   ├─ Dynamic (per-request)   → Server Component + fetch() (default dynamic)
│   └─ Revalidating            → Server Component + fetch() with { next: { revalidate: N } }
├─ Data is user-specific?
│   └─ Server Component + cookies()/headers() (auto-deduped, secure)
├─ Need real-time / polling?
│   └─ Client Component + React Query / SWR
└─ Need user-initiated mutation?
    └─ Server Action (form action / event handler)

Patterns

1. Server Component Data Fetching (Preferred)

// app/posts/page.tsx — Server Component, fetch runs on server, zero client JS
export default async function PostsPage() {
  const posts = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 }  // ISR: revalidate every 60s
  }).then(r => r.json())

  return <PostList posts={posts} />
}

2. Parallel Data Fetching

export default async function Dashboard() {
  // Parallel — both fetches start at the same time
  const [user, analytics] = await Promise.all([
    getUser(),
    getAnalytics()
  ])

  return <DashboardView user={user} analytics={analytics} />
}

3. Streaming with Suspense

import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <Header />              {/* Shown instantly */}
      <Suspense fallback={<Skeleton />}>
        <SlowDataComponent />  {/* Streamed in when ready */}
      </Suspense>
    </div>
  )
}

4. Server Actions (Mutations)

'use server'

export async function createPost(formData: FormData) {
  const title = formData.get('title')
  await db.post.create({ title })
  revalidatePath('/posts')  // Revalidate the cache
}

5. Client-Side React Query (Real-time / Polling)

When Server Components aren't enough (polling, real-time, window-focus refetch):

  • Use React Query / TanStack Query
  • Keep the fetch logic in a custom hook
  • Use initialData from Server Components to hydrate

Anti-Patterns

Anti-Pattern Why Fix
Fetching in useEffect for initial page data Wastes server capability, hurts UX Use Server Component fetching
Sequential fetches in Server Components Doubles load time Use Promise.all()
Mixing server/client fetch for the same data Redundant network calls Fetch once in Server, pass down
Over-fetching in RSC Large bundle from serialized data Only pass what the client needs
useEffect + fetch for mutations No built-in loading/error states Use Server Actions + useActionState

Checklist

  • Could this fetch happen in a Server Component? (prefer it)
  • Are parallel fetches batched with Promise.all?
  • Is the revalidation strategy correct? (time-based / on-demand / no cache)
  • Are loading states handled via loading.tsx or Suspense?
  • Are error states handled via error.tsx?
  • Are Server Actions used for mutations (not API routes)?
  • Is sensitive data only fetched server-side, never exposed to client JS?