Merhaba arkadaşlar, bugün başımı çok ağrıtan bir sorunu ve nasıl çözdüğümü anlatacağım. Bir React/Next.js projesinde Apollo Client kullanıyordum ve her sayfa değişiminde, her component mount olduğunda aynı GraphQL sorgularının tekrar tekrar sunucuya gittiğini fark ettim. Bu durum hem kullanıcı deneyimini yavaşlatıyor hem de sunucuya gereksiz yük bindiriyordu. İşte benim kullandığım en temiz çözüm: Apollo Client cache yapılandırmasını doğru bir şekilde yapmak.
Karşılaştığım Sorun ve Cache'in Önemi
Apollo Client varsayılan olarak harika bir cache mekanizması sunar, ancak onu doğru yapılandırmazsanız potansiyelini kullanamazsınız. Benim sorunum, `fetchPolicy` ve `nextFetchPolicy` gibi ayarları hiç kullanmamamdı. Her sorgu, cache'te aynı veri olsa bile, network'e gidip "Ben buradayım, bana yeni data ver!" diye bağırıyordu. Bu, özellikle kullanıcı profil bilgileri veya sabit site ayarları gibi sık sorgulanan ama nadiren değişen verilerde çok can sıkıcıydı.
Apollo Client Kurulumumu ve Cache Politikamı Nasıl Yapılandırdım?
İlk adım, Apollo Client instance'ımı oluştururken default fetch politikalarını belirlemek oldu. Ana `InMemoryCache` ve `ApolloClient` yapılandırmamı şu şekilde güncelledim:
Bu yapılandırmadaki sihirli nokta, `defaultOptions` kısmı. Burada tüm sorgular için varsayılan politikayı `'cache-first'` yapıyorum. Yani Apollo, bir sorgu yapıldığında önce cache'ine bakar, eğer cevap orada varsa network'e hiç gitmez. `nextFetchPolicy` ise ilk sorgudan sonraki sorgular için geçerli olan politikayı belirler. Bu ikili, gereksiz istekleri kesmek için birebir.
Sorgu Bazında İnce Ayar: fetchPolicy Kullanımı
Tabii ki her sorgu aynı değil. Bazı veriler gerçek zamanlı olmalı (örneğin anlık mesajlaşma), bazıları ise cache'te kalabilir. Bu nedenle, component seviyesinde `fetchPolicy` belirleyerek default ayarı geçersiz kılabilirsiniz. İşte iki farklı senaryo:
Gördüğünüz gibi, `'cache-and-network'` ve `'network-only'` gibi politikalarla ince ayar yapmak mümkün. Kritik nokta, hangi sorgunun ne sıklıkla değiştiğini analiz edip buna uygun politika seçmek.
Cache'i Manuel Yönetmek ve Performans İpuçları
Bazen cache'i manuel olarak güncellemek veya temizlemek gerekebilir. Örneğin, kullanıcı çıkış yaptığında cache'i temizlemek iyi bir pratiktir. Ya da bir mutation (örneğin yeni yorum ekleme) sonrası, ilgili sorgunun cache'ini güncellemek gerekir.
Bu manuel müdahaleler, kullanıcı arayüzünün anında tepki vermesini sağlar ve network isteğinin tamamlanmasını beklemeden cache'i güncelleyerek harika bir kullanıcı deneyimi sunar.
Sonuç olarak, Apollo Client cache'ini doğru yapılandırmak, uygulamanın performansını gözle görülür şekilde artırıyor. Artık sayfalar arasında gezinirken eski sayfaya döndüğümde veriler anında yükleniyor ve sunucu istek sayım ciddi oranda azaldı.
Siz Apollo Client veya başka bir GraphQL istemcisi (Relay, URQL) kullanırken cache konusunda benzer sorunlar yaşadınız mı? Sizin kullandığınız farklı bir cache stratejisi veya performans ipucu var mı? Yorumlarda paylaşalım!
Apollo Client varsayılan olarak harika bir cache mekanizması sunar, ancak onu doğru yapılandırmazsanız potansiyelini kullanamazsınız. Benim sorunum, `fetchPolicy` ve `nextFetchPolicy` gibi ayarları hiç kullanmamamdı. Her sorgu, cache'te aynı veri olsa bile, network'e gidip "Ben buradayım, bana yeni data ver!" diye bağırıyordu. Bu, özellikle kullanıcı profil bilgileri veya sabit site ayarları gibi sık sorgulanan ama nadiren değişen verilerde çok can sıkıcıydı.
İlk adım, Apollo Client instance'ımı oluştururken default fetch politikalarını belirlemek oldu. Ana `InMemoryCache` ve `ApolloClient` yapılandırmamı şu şekilde güncelledim:
JavaScript:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = createHttpLink({
uri: 'https://api.orneksitem.com/graphql',
});
const authLink = setContext((_, { headers }) => {
// ... token logic
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
};
});
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
// Burada spesifik alanlar için cache politikası yazabilirsiniz
posts: {
keyArgs: false, // Args farklı olsa bile aynı cache'i kullan (sayfalama için önemli!)
merge(existing = [], incoming) {
return [...existing, ...incoming];
},
}
}
}
}
});
export const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: cache,
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-first',
nextFetchPolicy: 'cache-first',
},
query: {
fetchPolicy: 'cache-first',
nextFetchPolicy: 'cache-first',
},
},
});
Bu yapılandırmadaki sihirli nokta, `defaultOptions` kısmı. Burada tüm sorgular için varsayılan politikayı `'cache-first'` yapıyorum. Yani Apollo, bir sorgu yapıldığında önce cache'ine bakar, eğer cevap orada varsa network'e hiç gitmez. `nextFetchPolicy` ise ilk sorgudan sonraki sorgular için geçerli olan politikayı belirler. Bu ikili, gereksiz istekleri kesmek için birebir.
Tabii ki her sorgu aynı değil. Bazı veriler gerçek zamanlı olmalı (örneğin anlık mesajlaşma), bazıları ise cache'te kalabilir. Bu nedenle, component seviyesinde `fetchPolicy` belirleyerek default ayarı geçersiz kılabilirsiniz. İşte iki farklı senaryo:
JavaScript:
// SENARYO 1: SADECE İLK SEFER NETWORK, SONRASI CACHE (Site Ayarları)
import { gql, useQuery } from '@apollo/client';
const GET_SITE_SETTINGS = gql`
query GetSiteSettings {
siteSettings {
title
logoUrl
theme
}
}
`;
function Header() {
const { data } = useQuery(GET_SITE_SETTINGS, {
fetchPolicy: 'cache-and-network', // İlk yüklemede hem cache'i göster hem network'ten tazele
nextFetchPolicy: 'cache-first', // Sonraki yüklemelerde sadece cache'e bak
});
// ... component logic
}
JavaScript:
// SENARYO 2: HER ZAMAN TAZE DATA (Canlı Borsa Verisi)
const GET_LIVE_STOCK = gql`
query GetLiveStock($symbol: String!) {
liveStock(symbol: $symbol) {
price
change
}
}
`;
function StockTicker({ symbol }) {
const { data } = useQuery(GET_LIVE_STOCK, {
variables: { symbol },
fetchPolicy: 'network-only', // Cache'i umursama, her zaman network'ten al
pollInterval: 10000, // Hatta her 10 saniyede bir otomatik yenile
});
// ... component logic
}
Gördüğünüz gibi, `'cache-and-network'` ve `'network-only'` gibi politikalarla ince ayar yapmak mümkün. Kritik nokta, hangi sorgunun ne sıklıkla değiştiğini analiz edip buna uygun politika seçmek.
Bazen cache'i manuel olarak güncellemek veya temizlemek gerekebilir. Örneğin, kullanıcı çıkış yaptığında cache'i temizlemek iyi bir pratiktir. Ya da bir mutation (örneğin yeni yorum ekleme) sonrası, ilgili sorgunun cache'ini güncellemek gerekir.
JavaScript:
// Cache'i tamamen temizleme
import { client } from './apolloClient';
const handleLogout = async () => {
await client.clearStore();
};
// Mutation sonrası cache'i güncelleme (Optimistic UI için)
const [addComment] = useMutation(ADD_COMMENT_MUTATION, {
update(cache, { data: { addComment } }) {
// Mevcut yorumlar sorgusunun cache'ini oku
const existingComments = cache.readQuery({
query: GET_COMMENTS,
variables: { postId }
});
// Yeni yorumu ekleyerek cache'i yeniden yaz
cache.writeQuery({
query: GET_COMMENTS,
variables: { postId },
data: {
comments: [addComment, ...existingComments.comments]
}
});
},
optimisticResponse: {
// Kullanıcıya anında geri bildirim için
addComment: {
__typename: 'Comment',
id: 'temp-id',
body: inputValue,
// ... diğer alanlar
}
}
});
Bu manuel müdahaleler, kullanıcı arayüzünün anında tepki vermesini sağlar ve network isteğinin tamamlanmasını beklemeden cache'i güncelleyerek harika bir kullanıcı deneyimi sunar.
Sonuç olarak, Apollo Client cache'ini doğru yapılandırmak, uygulamanın performansını gözle görülür şekilde artırıyor. Artık sayfalar arasında gezinirken eski sayfaya döndüğümde veriler anında yükleniyor ve sunucu istek sayım ciddi oranda azaldı.
Siz Apollo Client veya başka bir GraphQL istemcisi (Relay, URQL) kullanırken cache konusunda benzer sorunlar yaşadınız mı? Sizin kullandığınız farklı bir cache stratejisi veya performans ipucu var mı? Yorumlarda paylaşalım!