Generate Next.js data fetching patterns with Server Components, fetch API, caching strategies, and streaming. Supports ORM integration, client-side fetching, and performance optimization.
Generate data fetching implementations for Next.js applications using modern patterns from the App Router. Supports Server Components, Client Components, streaming, caching strategies, and ORM integration.
This skill helps you implement data fetching in Next.js 14+ applications following official best practices. It covers:
When the user requests data fetching implementation:
1. **Identify the component type and requirements:**
- Ask whether this is a Server Component (default) or Client Component
- Determine the data source (REST API, GraphQL, database/ORM)
- Identify if streaming or loading states are needed
- Check if caching or revalidation is required
2. **For Server Components with fetch API:**
- Make the component an async function
- Use `await fetch()` directly in the component
- Apply appropriate cache options: `{ cache: 'force-cache' }` (default), `{ cache: 'no-store' }` for dynamic data, or `{ next: { revalidate: 60 } }` for ISR
- Handle errors with try-catch or error boundaries
- Return JSX that maps over the fetched data
3. **For Server Components with ORM/database:**
- Import the database client or ORM (e.g., Prisma, Drizzle, Postgres.js)
- Make the component async
- Use `await` to query the database directly
- Optionally wrap queries with React's `cache()` function for deduplication
- Return JSX with the query results
4. **For Client Components with streaming:**
- Create a Server Component that fetches data and returns a promise (don't await)
- Pass the promise as a prop to a Client Component
- Wrap the Client Component in `<Suspense fallback={<Loading />}>`
- In the Client Component, use `'use client'` directive and React's `use()` hook to unwrap the promise
- Render the data once resolved
5. **For Client Components with community libraries:**
- Use `'use client'` directive
- Import and configure SWR or React Query
- Implement the fetcher function
- Handle `isLoading` and `error` states
- Render data when available
6. **Implement streaming when needed:**
- Create `loading.js` in the route folder for page-level streaming
- Or wrap specific components with `<Suspense>` boundaries for granular streaming
- Design meaningful loading states (skeletons, spinners, placeholders)
7. **Optimize with caching and deduplication:**
- Use `cache: 'force-cache'` for static data
- Use `{ next: { revalidate: seconds } }` for time-based revalidation
- Wrap non-fetch data access with React's `cache()` for request memoization
- Leverage automatic fetch deduplication for identical requests
8. **Handle data fetching patterns:**
- **Sequential:** Await one fetch before starting the next (use when data depends on previous result)
- **Parallel:** Call multiple fetches without await, then `Promise.all()` or render them in separate Suspense boundaries
- **Preloading:** Call data fetching functions at the route level, pass promises to components
9. **Follow Next.js conventions:**
- Prefer Server Components for data fetching when possible
- Use Client Components only when needed (interactivity, browser APIs, `use` hook)
- Fetch data as close to where it's used as possible
- Don't fetch data in layouts unless it's needed by all pages
**Example 1: Server Component with fetch API**
```typescript
// app/blog/page.tsx
export default async function BlogPage() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 } // Revalidate every hour
})
const posts = await res.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```
**Example 2: Server Component with ORM**
```typescript
// app/blog/page.tsx
import { db, posts } from '@/lib/db'
export default async function BlogPage() {
const allPosts = await db.select().from(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```
**Example 3: Streaming with Suspense**
```typescript
// app/blog/page.tsx (Server Component)
import { Suspense } from 'react'
import Posts from '@/components/Posts'
async function getPosts() {
const res = await fetch('https://api.example.com/posts')
return res.json()
}
export default function BlogPage() {
const postsPromise = getPosts() // Don't await
return (
<Suspense fallback={<div>Loading posts...</div>}>
<Posts posts={postsPromise} />
</Suspense>
)
}
// components/Posts.tsx (Client Component)
'use client'
import { use } from 'react'
export default function Posts({ posts }: { posts: Promise<Post[]> }) {
const allPosts = use(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```
**Example 4: Client Component with SWR**
```typescript
'use client'
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then(r => r.json())
export default function BlogPage() {
const { data, error, isLoading } = useSWR('https://api.example.com/posts', fetcher)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data.map((post: Post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```
**Example 5: Parallel data fetching**
```typescript
export default async function Page() {
// Initiate both requests in parallel
const artistPromise = fetch('https://api.example.com/artist')
const albumsPromise = fetch('https://api.example.com/albums')
// Wait for both to resolve
const [artist, albums] = await Promise.all([
artistPromise.then(r => r.json()),
albumsPromise.then(r => r.json())
])
return (
<div>
<h1>{artist.name}</h1>
<ul>
{albums.map((album) => (
<li key={album.id}>{album.title}</li>
))}
</ul>
</div>
)
}
```
Official Next.js documentation: https://nextjs.org/docs/app/building-your-application/data-fetching
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/nextjs-data-fetching/raw