1. サーバー側のデータ取得
// Async Server Component — fetch directly
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 }, // ISR: revalidate every hour
}).then(res => res.json());
return (
<div>
{posts.map((post: Post) => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}
2. キャッシュ戦略
// Static (cached forever until redeployed)
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache', // Default behavior
});
// Dynamic (no cache, fresh every request)
const data = await fetch('https://api.example.com/data', {
cache: 'no-store',
});
// ISR (revalidate after N seconds)
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }, // Revalidate every 60 seconds
});
// Tag-based revalidation
const data = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
});
// Revalidate by tag (from Server Action or Route Handler)
import { revalidateTag, revalidatePath } from 'next/cache';
revalidateTag('posts'); // Invalidate all fetches with 'posts' tag
revalidatePath('/blog'); // Invalidate specific path
3. 並列データフェッチ
// ❌ Waterfall — sequential fetching
export default async function Dashboard() {
const user = await fetchUser(); // 500ms
const orders = await fetchOrders(); // 500ms
const analytics = await fetchAnalytics(); // 500ms
// Total: 1500ms 😱
}
// ✅ Parallel — concurrent fetching
export default async function Dashboard() {
const [user, orders, analytics] = await Promise.all([
fetchUser(), // 500ms
fetchOrders(), // 500ms ← concurrent
fetchAnalytics(), // 500ms ← concurrent
]);
// Total: 500ms 🚀
}
// ✅ Streaming — each section loads independently
export default function Dashboard() {
return (
<div>
<Suspense fallback={<Skeleton />}>
<UserInfo />
</Suspense>
<Suspense fallback={<Skeleton />}>
<OrderList />
</Suspense>
<Suspense fallback={<Skeleton />}>
<Analytics />
</Suspense>
</div>
);
}
4. データベースへの直接アクセス
import { db } from '@/lib/db';
// Truy vấn database trực tiếp trong Server Component
async function UserList() {
const users = await db.user.findMany({
where: { isActive: true },
orderBy: { createdAt: 'desc' },
take: 10,
});
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
5.不安定なキャッシュ
import { unstable_cache } from 'next/cache';
const getCachedUser = unstable_cache(
async (userId: string) => {
return db.user.findUnique({ where: { id: userId } });
},
['user'], // Cache key prefix
{
revalidate: 3600, // 1 hour
tags: ['user'], // Tag for manual revalidation
}
);
// Usage in Server Component
export default async function Profile({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const user = await getCachedUser(id);
return <div>{user?.name}</div>;
}
6. 静的レンダリングと動的レンダリング
| レンダリング | いつ | キャッシュ |
|---|---|---|
| 静的 | 動的データや Cookie/ヘッダーはありません | ビルド時間、CDN |
| ダイナミック | クッキー()、ヘッダー()、searchParams | リクエスト時間 |
| ISR | 静的 + 再検証 | ビルド時間 + 定期的な更新 |
// Force dynamic rendering
export const dynamic = 'force-dynamic';
// Or: export const revalidate = 0;
// Force static
export const dynamic = 'force-static';
次の記事: サーバーアクションとミューテーション — 「サーバーを使用」、フォームアクション、楽観的な更新。