- Veri çekmenin yeri: sunucu bileşeni
- Varsayılan davranış: istekler önbelleğe alınmaz
- use cache ile veriyi ve arayüzü önbelleğe almak
- Yavaş ya da taze veriyi akışla göstermek
- İstekleri paralel kurmak, bağımlı veriyi sıralamak
- Veriyi tazelemek: zamana ve olaya göre yeniden doğrulama
- Sık hatalar ve kontrol listesi
- Bu beceriyi Mindset programlarında nerede çalışırsınız
- Sık sorulan sorular
Next.js'te veri nasıl çekilir, nasıl önbelleğe alınır ve ne zaman tazelenir?
Next.js'te sunucu bileşeninde veri çekme, paralel istek, Suspense ile akış, use cache ile önbellek ve etiketle yeniden doğrulama adım adım anlatılıyor.
Veri çekmenin yeri: sunucu bileşeni
App Router'da veriyi çekmenin varsayılan yeri sunucu bileşenidir. Bileşeni async yapar, içinde await ile fetch ya da bir veritabanı sorgusu çalıştırırsınız; ayrı bir veri yükleme fonksiyonu tanımlamanız gerekmez:
export default async function Page() { const yanit = await fetch('https://api.ornek.com/yazilar') const yazilar = await yanit.json() return ( <ul> {yazilar.map((y) => ( <li key={y.id}>{y.baslik}</li> ))} </ul> ) }
Bileşen sunucuda çalıştığı için veritabanı sorgusu ve gizli anahtarlar istemci paketine girmez; ORM ya da veritabanı istemcisini doğrudan çağırabilirsiniz. Yine de bu çağrıların kimlik doğrulama ve yetkilendirme kontrolünden geçmesi gerekir, çünkü sunucuda çalışmak kendiliğinden güvenlik sağlamaz.
Bir kolaylık daha: aynı bileşen ağacında birbirinin aynı olan fetch istekleri varsayılan olarak tek isteğe indirgenir. Bu sayede veriyi, ihtiyaç duyan bileşenin kendi içinde çekebilir, özellikleri aşağıya taşıma yükünden kurtulursunuz. Fetch kullanmayan veri erişiminde (ORM sorgusu gibi) aynı sonucu React'in cache fonksiyonuyla sağlarsınız; bu önbellek yalnızca o isteğin ömrü boyunca geçerlidir.
Varsayılan davranış: istekler önbelleğe alınmaz
Önbellekleme konusunda en sık yanlış anlaşılan nokta, varsayılan davranıştır. Güncel dokümantasyona göre fetch istekleri varsayılan olarak önbelleğe alınmaz ve sayfa, istek tamamlanana kadar çizilmeyi bekler. Yani hiçbir şey eklemezseniz veri her istekte taze çekilir. Önbellek bilinçli olarak, use cache yönergesiyle açılır.
Bu yaklaşım Cache Components adı verilen modele dayanır. Yeni oluşturulan uygulamalarda bu model varsayılan olarak açık gelir; mevcut bir uygulamada next.config dosyasında cacheComponents seçeneğini açarsınız:
// next.config.ts const nextConfig = { cacheComponents: true, } export default nextConfig
Bu seçenek kapalıysa eski önbellek modeli geçerlidir ve dokümantasyonda ayrı bir kılavuzda anlatılır. Eski modele ait seçenekleri (örneğin fetch çağrısına eklenen yeniden doğrulama ayarı) yeni modelle karıştırmak, önbellek davranışını tahmin etmeyi zorlaştırır. Bir projede hangi modelin açık olduğunu önce yapılandırma dosyasından kontrol edin.
use cache ile veriyi ve arayüzü önbelleğe almak
Önbelleğe alınacak bir fonksiyonun ya da bileşenin başına use cache yönergesini yazarsınız. Farklı düzeylerde kullanılır. Veri düzeyinde, veri çeken bir fonksiyon önbelleğe alınır:
import { cacheLife } from 'next/cache'
export async function getProducts() { 'use cache' cacheLife('hours') return db.query('SELECT id, name FROM products') }
Arayüz düzeyinde ise bir bileşen ya da sayfanın tamamı önbelleğe alınır. Dosyanın en üstüne yönergeyi yazarsanız o dosyadan dışa aktarılan bütün fonksiyonlar önbelleğe alınır.
Her önbellek yönergesini bir cacheLife çağrısıyla eşleştirmeniz önerilir. cacheLife, bir profil adı (seconds, minutes, hours, days, weeks, max gibi) ya da özel bir yapılandırma nesnesi alır. Profil seçerken sorunuz şu olsun: bu verinin biraz bayat kalması kabul edilebilir mi? Ürün kataloğu ya da blog listesi için evet; stok adedi ya da kullanıcıya özel bakiye için hayır, onlar önbellek dışında kalmalı.
Önbellek anahtarı, fonksiyona geçilen argümanlardan ve yakalanan değişkenlerden oluşur; farklı argüman farklı önbellek girdisi üretir. Çerez ya da başlık gibi istek anında bilinen değerleri önbellekli fonksiyonun içinde okuyamazsınız. Onları dışarıda okuyup argüman olarak geçirirsiniz.
Yavaş ya da taze veriyi akışla göstermek
Önbelleğe alınmayan, her istekte taze gelmesi gereken veri varsa use cache kullanmazsınız. Bu bileşeni Suspense ile sarar ve yedek arayüz verirsiniz; sayfanın hazır kısmı hemen gelir, yavaş kısım hazır olunca akışla yerine geçer:
import { Suspense } from 'react'
export default function Page() { return ( <> <header>Blog</header> <Suspense fallback={<p>Yazılar yükleniyor...</p>}> <SonYazilar /> </Suspense> </> ) }
Bütün sayfayı kapsayacak yükleme durumu için aynı klasöre loading dosyası da koyabilirsiniz. Ancak dokümantasyon, yavaş ya da istek anına bağlı veriye mümkün olduğunca yakın bir Suspense kullanmayı önerir. Çünkü ağaçta ne kadar aşağıda beklerseniz, sayfanın o kadar büyük kısmı önceden hazırlanıp hemen sunulabilir.
Çerez, başlık, searchParams ve dinamik params gibi yalnızca istek geldiğinde bilinen değerlere erişen bileşenleri de Suspense içine almak gerekir; aksi hâlde geliştirme ortamı bunu engelleyici rota uyarısıyla bildirir.
İstekleri paralel kurmak, bağımlı veriyi sıralamak
Bir bileşenin içinde art arda yazılan await ifadeleri birbirini bekler. Birbirinden bağımsız verileri şöyle yazarsanız sonraki istek, öncekinin bitmesini bekler:
const sanatci = await sanatciGetir(kullanici) const albumler = await albumleriGetir(kullanici)
Bağımsız istekleri önce başlatıp sonra birlikte beklemek için Promise.all kullanırsınız:
const sanatciSozu = sanatciGetir(kullanici) const albumSozu = albumleriGetir(kullanici) const [sanatci, albumler] = await Promise.all([sanatciSozu, albumSozu])
Promise.all'da bir istek hata verirse bütün işlem başarısız olur; bunu istemiyorsanız Promise.allSettled kullanın. Bir verinin diğerine bağımlı olduğu durumda (önce sanatçı, sonra o sanatçının çalma listeleri gibi) sıralı yazmak zorundasınız; bu durumda bağımlı parçayı ayrı bir bileşene alıp Suspense ile sarmak, sayfanın geri kalanının beklememesini sağlar.
Sayfa ve layout'lar zaten paralel çizilir, dolayısıyla her segment verisini kendi içinde çekerse isteklerin çoğu kendiliğinden eşzamanlı başlar. Sorun, tek bir bileşende gereksiz yere art arda dizilmiş await ifadelerinde çıkar.
Veriyi tazelemek: zamana ve olaya göre yeniden doğrulama
Önbelleğe aldığınız verinin ne zaman yenileneceğini zamana ya da olaya bağlı olarak yönetirsiniz.
Zamana göre: cacheLife profili verinin ne kadar süre geçerli sayılacağını belirler. Süre dolunca veri yeniden üretilir.
Olaya göre: önbellekteki veriye cacheTag ile bir etiket verirsiniz, bir değişiklik olduğunda o etiketi geçersiz kılarsınız.
export async function getProducts() { 'use cache' cacheTag('products') return db.query('SELECT id, name FROM products') }
Geçersiz kılmak için birden çok işlev vardır. revalidateTag, bayat içeriği sunmaya devam ederken arka planda yenisini üretir; bir yazı kataloğu gibi küçük gecikmenin kabul edildiği durumlarda uygundur. Dokümantasyonda önerilen kullanım profil argümanı olarak max vermektir. updateTag ise önbelleği hemen geçersiz kılar; kullanıcı kendi yaptığı değişikliği anında görmelidir (örneğin yeni yazı ekleyip yazı sayfasına yönlendirme). updateTag yalnızca Server Action içinde kullanılabilir.
Bir yolu belirli bir etiketi bilmeden yenilemek isterseniz revalidatePath vardır; ancak doküman, gereksiz geniş geçersiz kılmadan kaçınmak için etiket tabanlı yöntemi tercih etmenizi söyler. Bir CMS kullanıyorsanız içerik değiştiğinde etiketi geçersiz kılan bir web kancası kurmak, zaman bazlı yenilemeyi azaltır.
Sık hatalar ve kontrol listesi
- Önbelleğin açık olduğunu varsaymak: Varsayılan olarak fetch önbelleğe alınmaz; use cache yazmadığınız veri her istekte çekilir.
- Kullanıcıya özel veriyi paylaşımlı önbelleğe almak: Çerezle belirlenen kullanıcı verisini genel bir önbellekli fonksiyona koymak, veriyi başkasına gösterme riski doğurur.
- Her önbelleğe cacheLife vermemek: Süreyi bilinçli seçmeden varsayılan profile güvenmek, tazelik beklentisini belirsiz bırakır.
- Mutasyon sonrası geçersiz kılmayı unutmak: Veriyi değiştiren Server Action'ın sonunda ilgili etiketi ya da yolu geçersiz kılmazsanız kullanıcı eski içeriği görür.
- Suspense olmadan istek anı verisi okumak: Çerez ya da searchParams okuyan bileşeni sınırsız bırakmak, rotanın engelleyici olmasına yol açar.
- Gereksiz sıralı await: Bağımsız istekleri art arda beklemek sayfayı yavaşlatır.
Uygulamada önce hangi verinin herkese aynı, hangisinin kullanıcıya ya da isteğe özel olduğunu bir tabloya yazın. Herkese aynı olanı etiket ve cacheLife ile önbelleğe alın; isteğe özel olanı Suspense altına koyun. Sonra her mutasyonun hangi etiketi geçersiz kılacağını eşleştirin.
Bu beceriyi Mindset programlarında nerede çalışırsınız
Next.js ile Full-Stack Web Uygulamaları programında Veri Getirme ve Önbellekleme ayrı bir modüldür; sonrasında veri değiştirme ve form akışları, kimlik doğrulama ile test, dağıtım ve gözlemlenebilirlik modülleri gelir. Önbellek kararlarını bir ürün ekibinin ortak diline çevirmek isteyen ön yüz ve full-stack geliştiriciler, bu sıralamayla veri akışını baştan sona izleyebilir.
Kurumsal bir ekipte tipik kazanım, hangi verinin ortak, hangisinin kullanıcıya özel olduğunun ve değişiklik sonrası neyin yenilenmesi gerektiğinin ekip konvansiyonu hâline gelmesidir.
İlgili Mindset programı
Next.js ile Full-Stack Web Uygulamaları
Sık sorulan sorular
Next.js'te fetch varsayılan olarak önbelleğe alınır mı?
Güncel dokümantasyona göre hayır. Fetch istekleri varsayılan olarak önbelleğe alınmaz ve sayfa isteğin tamamlanmasını bekler. Önbelleği use cache yönergesiyle bilinçli olarak açarsınız. Eski sürümlerde farklı davranış olduğu için projenizin hangi önbellek modelini kullandığını yapılandırma dosyasından kontrol edin.
revalidateTag ile updateTag arasındaki fark nedir?
revalidateTag bayat içeriği sunmaya devam ederek yenisini arka planda üretir ve hem Server Action hem Route Handler içinde kullanılır. updateTag önbelleği anında geçersiz kılar, kullanıcının kendi değişikliğini hemen görmesi gereken durumlara uygundur ve yalnızca Server Action içinde çalışır.
Kullanıcıya özel veriyi önbelleğe alabilir miyim?
Paylaşımlı bir önbelleğe kullanıcıya özel veri koymaktan kaçının. İstek anında bilinen çerez gibi değerleri bileşenin dışında okuyup önbellekli fonksiyona argüman olarak geçirmek bir seçenektir. Yine de kullanıcı verisini, ancak neyin kimin önbellek anahtarına girdiğini net kurabiliyorsanız önbelleğe alın.
Aynı veriyi birden çok bileşende çekmek çift istek yapar mı?
Fetch ile yapılan aynı istekler aynı bileşen ağacında varsayılan olarak tek isteğe indirgenir. ORM gibi fetch kullanmayan erişimlerde React'in cache fonksiyonuyla aynı sonucu elde edersiniz. Bu önbellek yalnızca o isteğin ömrü boyunca geçerlidir ve istekler arasında paylaşılmaz.
Verinin güncellenmesi için sayfayı yeniden dağıtmam gerekir mi?
Hayır. Zamana dayalı yenilemeyi cacheLife, olaya dayalı yenilemeyi cacheTag ile geçersiz kılma sağlar; ikisi de dağıtım gerektirmez. Bir CMS ya da yönetim ekranı içerik değiştirdiğinde etiketi geçersiz kılan bir uç nokta kurmak yaygın bir yaklaşımdır.