Next.js Caching ve Revalidation Stratejileri

Next.js Caching ve Revalidation Stratejileri
Next.js, performansı artırmak için çok katmanlı bir caching sistemi sunar. Bu sistem, gereksiz hesaplamaları ve veri çekme işlemlerini minimize ederek uygulamanızı hızlandırır. Ancak caching mekanizmasını doğru anlamak ve yönetmek, tutarlı ve güncel içerik sunmak için kritiktir. Bu rehberde Next.js'in tüm caching katmanlarını ve revalidation stratejilerini detaylı inceleyeceğiz.
Caching Katmanları
Next.js dört temel caching mekanizması kullanır:
| Mekanizma | Ne Cache'ler? | Nerede? | Amacı |
|---|---|---|---|
| Request Memoization | Fetch istekleri | Sunucu (render süreci) | Aynı istekleri deduplicate eder |
| Data Cache | Fetch yanıtları | Sunucu (kalıcı) | Veriyi istekler arası cache'ler |
| Full Route Cache | HTML ve RSC Payload | Sunucu (kalıcı) | Render maliyetini azaltır |
| Router Cache | RSC Payload | İstemci (session) | Navigasyon isteklerini azaltır |
Request Memoization
Aynı render ağacındaki aynı URL ve seçeneklere sahip fetch istekleri otomatik olarak deduplicate edilir:
// Bu iki bileşen aynı render'da çalışırsa, fetch sadece 1 kez yapılır
async function getUser(id: string) {
const res = await fetch(`https://api.example.com/users/${id}`);
return res.json();
}
// layout.tsx
export default async function Layout({ children }) {
const user = await getUser('123'); // İstek #1
return <div><Header user={user} />{children}</div>;
}
// page.tsx
export default async function Page() {
const user = await getUser('123'); // Deduplicate — cache'ten gelir
return <Profile user={user} />;
}
Data Cache Kontrolü
// Varsayılan: Cache'le (SSG davranışı)
const data = await fetch('https://api.example.com/posts', {
cache: 'force-cache', // Varsayılan
});
// Cache'leme (SSR davranışı)
const data = await fetch('https://api.example.com/dashboard', {
cache: 'no-store',
});
// Time-based revalidation (ISR)
const data = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 }, // 1 saat
});
// Tag-based cache
const data = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] },
});
no-store olarak değişmiştir. Önceki versiyonlarda varsayılan force-cache idi. Bu değişikliği göz önünde bulundurun.
Route Segment Config ile Cache
// Sayfa bazlı cache kontrolü
export const dynamic = 'auto'; // Varsayılan
export const dynamic = 'force-dynamic'; // Her istekte SSR
export const dynamic = 'force-static'; // Build'de SSG
export const dynamic = 'error'; // Dinamik fonksiyon kullanılırsa hata
export const revalidate = 0; // Her istekte yenile
export const revalidate = 3600; // 1 saat cache
export const revalidate = false; // Sonsuz cache (varsayılan)
export const fetchCache = 'auto';
export const fetchCache = 'default-cache';
export const fetchCache = 'default-no-store';
export const fetchCache = 'force-cache';
export const fetchCache = 'force-no-store';
On-Demand Revalidation
Belirli olaylar tetiklendiğinde cache'i manuel olarak temizleyebilirsiniz:
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
export async function POST(request: Request) {
const { secret, type, value } = await request.json();
if (secret !== process.env.REVALIDATION_SECRET) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
if (type === 'path') {
// Belirli bir path'i revalidate et
revalidatePath(value);
// Layout dahil tüm cache temizlenir
revalidatePath(value, 'layout');
// Sadece page cache'i temizlenir
revalidatePath(value, 'page');
}
if (type === 'tag') {
// Tag ile ilişkili tüm cache'leri temizle
revalidateTag(value);
}
return Response.json({ revalidated: true, now: Date.now() });
}
// Kullanım — CMS webhook'unda:
// POST /api/revalidate
// { "secret": "xxx", "type": "tag", "value": "blog-posts" }
Server Action ile Revalidation
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
await prisma.post.create({
data: { title, content },
});
// Cache'i temizle
revalidateTag('posts');
revalidatePath('/blog');
}
export async function updateProduct(id: string, data: ProductData) {
await prisma.product.update({
where: { id },
data,
});
revalidateTag(`product-${id}`);
revalidatePath(`/products/${id}`);
revalidatePath('/products'); // Liste sayfasını da yenile
}
revalidatePath('/') çağrısı tüm route'ları revalidate eder. Gereksiz yere geniş revalidation yapmaktan kaçının; spesifik path'ler ve tag'ler kullanın.
unstable_cache ile Fonksiyon Cache
import { unstable_cache } from 'next/cache';
const getCachedPosts = unstable_cache(
async (category: string) => {
return prisma.post.findMany({
where: { category },
orderBy: { createdAt: 'desc' },
});
},
['posts-by-category'], // Cache key
{
revalidate: 3600, // 1 saat
tags: ['posts'], // Tag ile revalidate edilebilir
}
);
export default async function PostsPage({
searchParams,
}: {
searchParams: { category: string };
}) {
const posts = await getCachedPosts(searchParams.category);
return <PostList posts={posts} />;
}
Router Cache (Client-Side)
'use client';
import { useRouter } from 'next/navigation';
export function RefreshButton() {
const router = useRouter();
return (
<button
onClick={() => {
// Router cache'i temizle ve sayfayı yenile
router.refresh();
}}
>
Yenile
</button>
);
}
// Link prefetch kontrolü
import Link from 'next/link';
// Prefetch'i devre dışı bırak
<Link href="/dashboard" prefetch={false}>Dashboard</Link>
router.refresh() bu cache'i temizler.
Caching Stratejisi Seçimi
- Statik içerik (blog, docs): Varsayılan cache + on-demand revalidation
- Sık güncellenen (haberler): ISR (revalidate: 300-3600)
- Kullanıcıya özel (dashboard): no-store veya force-dynamic
- E-ticaret ürünleri: Tag-based revalidation
- Arama sonuçları: no-store (her zaman dinamik)
Sonuç
Next.js'in caching sistemi, doğru yapılandırıldığında uygulamanızın performansını dramatik şekilde artırır. Request memoization, data cache, full route cache ve router cache katmanlarını anlayarak her sayfa için en uygun stratejiyi belirleyebilirsiniz. On-demand revalidation ile veri güncelliğini korurken, gereksiz sunucu yükünü minimize edebilirsiniz.