Skip to main content

nextjs-data-fetching

Use when next.js data fetching patterns including SSG, SSR, and ISR. Use when building data-driven Next.js applications.

Informations de source

Dépôt
TheBushidoCollective/han
Dernière activité de la source
11 février 2026 à 17:47
Langue détectée de SKILL.md
anglais
Étoiles
198
Forks
22

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
nextjs-data-fetching
user-invocable
false
description
Use when next.js data fetching patterns including SSG, SSR, and ISR. Use when building data-driven Next.js applications.
allowed-tools
["Bash","Read"]
# Next.js Data Fetching Master data fetching in Next.js with static generation, server-side rendering, and incremental static regeneration. ## Fetch with Caching Strategies ```typescript // Default: 'force-cache' (similar to SSG) export default async function Page() { const data = await fetch('https://api.example.com/data'); const json = await data.json(); return <div>{json.title}</div>; } // No caching: 'no-store' (similar to SSR) export default async function DynamicPage() { const data = await fetch('https://api.example.com/data', { cache: 'no-store' }); const json = await data.json(); return <div>{json.title}</div>; } // Revalidate every 60 seconds (ISR) export default async function RevalidatedPage() { const data = await fetch('https://api.example.com/data', { next: { revalidate: 60 } }); const json = await data.json(); return <div>{json.title}</div>; } // Per-route caching config export const revalidate = 3600; // Revalidate every hour export default async function Page() { const data = await fetch('https://api.example.com/data'); return <div>{data.title}</div>; } // Dynamic rendering export const dynamic = 'force-dynamic'; // Equivalent to cache: 'no-store' export const dynamic = 'force-static'; // Equivalent to cache: 'force-cache' export const dynamic = 'error'; // Error if dynamic functions used export const dynamic = 'auto'; // Default behavior ``` ## Static Generation with generateStaticParams ```typescript // app/posts/[id]/page.tsx interface Post { id: string; title: string; content: string; } // Generate static paths at build time export async function generateStaticParams() { const posts = await fetch('https://api.example.com/posts').then(r => r.json()); return posts.map((post: Post) => ({ id: post.id.toString() })); } export default async function Post({ params }: { params: { id: string } }) { const post = await fetch(`https://api.example.com/posts/${params.id}`, { next: { revalidate: 3600 } // Revalidate hourly }).then(r => r.json()); return ( <article> <h1>{post.title}</h1> <p>{post.content}</p> </article> ); } // Multiple dynamic segments // app/shop/[category]/[product]/page.tsx export async function generateStaticParams() { const categories = await getCategories(); const paths = await Promise.all( categories.map(async (category) => { const products = await getProducts(category.slug); return products.map((product) => ({ category: category.slug, product: product.slug })); }) ); return paths.flat(); } export default async function Product({ params }: { params: { category: string; product: string } }) { const product = await getProduct(params.category, params.product); return ( <div> <h1>{product.name}</h1> <p>{product.description}</p> </div> ); } // Limit static generation for large datasets export const dynamicParams = true; // Default: generate on-demand export const dynamicParams = false; // Return 404 for ungenerated paths export async function generateStaticParams() { // Only generate top 100 posts at build time const posts = await getPosts({ limit: 100 }); return posts.map((post) => ({ id: post.id })); } ``` ## Time-Based Revalidation (ISR) ```typescript // Page-level revalidation export const revalidate = 60; // Revalidate every 60 seconds export default async function Page() { const posts = await fetch('https://api.example.com/posts').then(r => r.json()); return ( <div> {posts.map(post => ( <article key={post.id}> <h2>{post.title}</h2> </article> ))} </div> ); } // Per-request revalidation export default async function Page() { const staticData = await fetch('https://api.example.com/static', { next: { revalidate: 3600 } // Cache for 1 hour }).then(r => r.json()); const dynamicData = await fetch('https://api.example.com/dynamic', { next: { revalidate: 10 } // Cache for 10 seconds }).then(r => r.json()); return ( <div> <div>Static: {staticData.value}</div> <div>Dynamic: {dynamicData.value}</div> </div> ); } // Mixed caching strategies export default async function Dashboard() { // Never cache (always fresh) const liveData = await fetch('https://api.example.com/live', { cache: 'no-store' }).then(r => r.json()); // Cache indefinitely (build-time only) const staticData = await fetch('https://api.example.com/static', { cache: 'force-cache' }).then(r => r.json()); // Revalidate periodically const periodicData = await fetch('https://api.example.com/periodic', { next: { revalidate: 300 } }).then(r => r.json()); return ( <div> <LiveStats data={liveData} /> <StaticContent data={staticData} /> <PeriodicUpdates data={periodicData} /> </div> ); } ``` ## On-Demand Revalidation ```typescript // app/api/revalidate/route.ts import { revalidatePath, revalidateTag } from 'next/cache'; import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { const secret = request.nextUrl.searchParams.get('secret'); // Verify secret token if (secret !== process.env.REVALIDATE_SECRET) { return NextResponse.json({ error: 'Invalid secret' }, { status: 401 }); } const path = request.nextUrl.searchParams.get('path'); if (path) { // Revalidate specific path revalidatePath(path); return NextResponse.json({ revalidated: true, path }); } return NextResponse.json({ error: 'Missing path' }, { status: 400 }); } // app/posts/[slug]/page.tsx - Using cache tags export default async function Post({ params }: { params: { slug: string } }) { const post = await fetch(`https://api.example.com/posts/${params.slug}`, { next: { tags: ['posts', `post-${params.slug}`] } }).then(r => r.json()); return <article>{post.content}</article>; } // app/api/revalidate-tag/route.ts export async function POST(request: NextRequest) { const tag = request.nextUrl.searchParams.get('tag'); if (tag) { revalidateTag(tag); return NextResponse.json({ revalidated: true, tag }); } return NextResponse.json({ error: 'Missing tag' }, { status: 400 }); } // Server Action for revalidation 'use server'; import { revalidatePath } from 'next/cache'; export async function updatePost(id: string, data: FormData) { await db.post.update({ where: { id }, data }); // Revalidate the post page revalidatePath(`/posts/${id}`); // Revalidate the posts list revalidatePath('/posts'); } ``` ## Parallel Data Fetching ```typescript // Fetch multiple resources in parallel export default async function Page() { const [posts, categories, tags] = await Promise.all([ fetch('https://api.example.com/posts').then(r => r.json()), fetch('https://api.example.com/categories').then(r => r.json()), fetch('https://api.example.com/tags').then(r => r.json()) ]); return ( <div> <PostList posts={posts} /> <CategoryList categories={categories} /> <TagCloud tags={tags} /> </div> ); } // Parallel fetching with different cache strategies export default async function Dashboard() { const [stats, recentActivity, settings] = await Promise.all([ fetch('https://api.example.com/stats', { next: { revalidate: 60 } }).then(r => r.json()), fetch('https://api.example.com/activity', { cache: 'no-store' }).then(r => r.json()), fetch('https://api.example.com/settings', { cache: 'force-cache' }).then(r => r.json()) ]); return ( <div> <Stats data={stats} /> <Activity data={recentActivity} /> <Settings data={settings} /> </div> ); } // Fetching with fallbacks export default async function Page() { const results = await Promise.allSettled([ fetch('https://api.example.com/required').then(r => r.json()), fetch('https://api.example.com/optional1').then(r => r.json()), fetch('https://api.example.com/optional2').then(r => r.json()) ]); const [required, optional1, optional2] = results; return ( <div> {required.status === 'fulfilled' && <Required data={required.value} />} {optional1.status === 'fulfilled' && <Optional1 data={optional1.value} />} {optional2.status === 'fulfilled' && <Optional2 data={optional2.value} />} </div> ); } ``` ## Streaming and Suspense ```typescript // app/posts/page.tsx import { Suspense } from 'react'; export default function PostsPage() { return ( <div> <h1>Blog Posts</h1> {/* Stream featured posts */} <Suspense fallback={<FeaturedSkeleton />}> <FeaturedPosts /> </Suspense> {/* Stream all posts */} <Suspense fallback={<PostsSkeleton />}> <AllPosts /> </Suspense> {/* Stream comments */} <Suspense fallback={<CommentsSkeleton />}> <RecentComments /> </Suspense> </div> ); } async function FeaturedPosts() { const posts = await fetch('https://api.example.com/posts/featured', { next: { revalidate: 300 } }).then(r => r.json()); return ( <div className="featured"> {posts.map(post => ( <PostCard key={post.id} post={post} /> ))} </div> ); } async function AllPosts() { const posts = await fetch('https://api.example.com/posts', { next: { revalidate: 60 } }).then(r => r.json()); return ( <div className="posts"> {posts.map(post => (
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub