İçeriğe geç
nextjs

Next.js Caching ve Revalidation Stratejileri

Tarık Tunç
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'] },
});
İpucu: Next.js 15'te fetch cache varsayılan davranışı 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
}
Uyarı: 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>
Bilgi: Router cache, client-side navigasyonlarda önceki sayfa verilerini 30 saniye (dinamik) veya 5 dakika (statik) boyunca tutar. 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.

Kaynaklar

İlgili Yazılar