SWR, TanStack Query und Apollo Client
Caching Strategien entscheiden darüber, wie oft eine React-Anwendung Daten neu lädt, wie schnell Änderungen sichtbar werden und wie viel Speicher der Client dafür verbraucht. SWR, TanStack Query und Apollo Client lösen dieselbe Grundaufgabe mit unterschiedlichen Caching Strategien, von einfachem Stale-While-Revalidate bis zur vollständigen Objektnormalisierung, mit spürbaren Konsequenzen für Konsistenz und Wartungsaufwand.
Inhaltsverzeichnis
- 1. Warum Caching Strategien den größten Performance-Hebel bilden
- 2. SWR: Stale-While-Revalidate in seiner reinsten Form
- 3. TanStack Query: Query-Keys als Cache-Adressierung
- 4. Apollo Client: Normalisierter Objektgraph statt Query-Cache
- 5. Invalidierung im Vergleich: Tags, Keys und Objekt-IDs
- 6. Staleness-Konfiguration: staleTime, dedupingInterval und fetchPolicy
- 7. Speicherverhalten und Garbage Collection
- 8. Persistenz und Offline-Verhalten der drei Caches
- 9. Caching Strategien im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Caching Strategien den größten Performance-Hebel bilden
Bevor eine konkrete Bibliothek betrachtet wird, lohnt sich ein Blick auf das gemeinsame Grundproblem, das jede der drei Lösungen zu lösen versucht.
Die Caching Strategien einer React-Anwendung entscheiden über gefühlte Geschwindigkeit stärker als fast jede andere Architekturentscheidung. Eine Komponente, die bei jedem Mount neu vom Server lädt, fühlt sich langsam an, selbst wenn der Server in unter hundert Millisekunden antwortet, weil Nutzerinnen einen sichtbaren Ladezustand wahrnehmen. Eine durchdachte Caching-Strategie zeigt stattdessen sofort die zuletzt bekannten Daten an und aktualisiert im Hintergrund, was die Anwendung praktisch verzögerungsfrei wirken lässt.
SWR, TanStack Query und Apollo Client verfolgen unterschiedliche Caching Strategien, die auf den ersten Blick ähnlich wirken, sich in den Details aber deutlich unterscheiden: von der Adressierung einzelner Cache-Einträge über die Invalidierungslogik bis zur Frage, ob überhaupt Objektnormalisierung stattfindet. Dieser Artikel vergleicht die drei Ansätze entlang genau dieser Kriterien und zeigt, wann welche Caching-Strategie den größten praktischen Vorteil bringt.
Ein weiterer Grund, weshalb sich der genaue Vergleich lohnt, ist die Tatsache, dass alle drei Bibliotheken denselben Marketingbegriff verwenden, nämlich Caching, aber unterschiedliche technische Realitäten dahinter verbergen. Wer eine Bibliothek allein nach Bekanntheit oder GitHub-Sternen auswählt, übersieht häufig, dass die zugrunde liegende Caching Strategie eine völlig andere Antwort auf dieselbe Frage gibt, nämlich wie lange Daten gültig bleiben und wann sie erneuert werden.
2. SWR: Stale-While-Revalidate in seiner reinsten Form
SWR, entwickelt von Vercel, ist nach genau dem HTTP-Cache-Header-Konzept benannt, das es implementiert: Stale-While-Revalidate. Die Caching-Strategie zeigt sofort die im Cache vorhandenen, potenziell veralteten Daten an, während im Hintergrund eine neue Anfrage läuft, und aktualisiert die UI, sobald die Antwort eintrifft. Diese Caching Strategie ist bewusst minimalistisch gehalten: Ein Cache-Key, meist die URL, adressiert einen Eintrag, ohne komplexere Query-Objekte oder Normalisierung.
SWR wird oft als erste Caching-Bibliothek in kleineren Projekten oder Prototypen eingesetzt, gerade weil der Einstieg minimal ist und kaum Konzepte vorausgesetzt werden.
Diese Einfachheit ist SWRs größte Stärke und gleichzeitig seine größte Grenze. Für Anwendungen mit überwiegend unabhängigen Datenquellen, bei denen ein Objekt selten über mehrere unterschiedliche Endpoints geladen wird, ist die schlanke Caching-Strategie von SWR ausreichend und erzeugt minimalen Overhead. Für Anwendungen mit stark verflochtenen Datenmodellen, in denen dasselbe Objekt über mehrere Views hinweg konsistent bleiben muss, stößt diese Caching Strategie ohne zusätzliche manuelle Arbeit an ihre Grenzen.
// useUser.ts — SWR's minimal caching strategy, keyed by URL
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((res) => res.json());
function useUser(userId: string) {
return useSWR(`/api/users/${userId}`, fetcher, {
revalidateOnFocus: true,
dedupingInterval: 5000, // Ignore duplicate requests within 5s
});
}
// Manual revalidation after a mutation, since SWR has no automatic normalization
import { mutate } from "swr";
async function updateUserName(userId: string, name: string) {
await fetch(`/api/users/${userId}`, { method: "PATCH", body: JSON.stringify({ name }) });
// Revalidate this specific cache key
mutate(`/api/users/${userId}`);
}
SWR ist außerdem eng mit Next.js verwoben, wurde aber bewusst framework-unabhängig gehalten und funktioniert genauso gut in reinen Client-Side-React-Anwendungen. Für Teams, die bereits das Vercel-Ökosystem nutzen, ist die Integration oft nahtlos, weil Dokumentation und Beispiele häufig direkt auf Next.js-Patterns zugeschnitten sind, was den Einstieg in diese Caching Strategie zusätzlich beschleunigt.
3. TanStack Query: Query-Keys als Cache-Adressierung
TanStack Query erweitert das Stale-While-Revalidate-Prinzip von SWR um ein deutlich ausdrucksstärkeres Adressierungssystem: Query-Keys sind Arrays statt einzelner Strings, wodurch sich hierarchische Beziehungen zwischen Cache-Einträgen abbilden lassen, etwa ["users", userId, "posts"]. Diese Caching Strategie erlaubt es, mit invalidateQueries nicht nur einen exakten Key, sondern ganze Teilbäume von Query-Keys gezielt zu invalidieren, ohne jeden betroffenen Key einzeln aufzählen zu müssen.
Ein weiterer Unterschied zu SWR ist die eingebaute Unterstützung für Mutations als First-Class-Konzept mit onMutate, onError und onSettled, statt Mutations dem Entwickler komplett selbst zu überlassen. Diese Caching Strategie bringt strukturierte Werkzeuge für genau die Fälle mit, die bei SWR manuell nachgebaut werden müssten, etwa Optimistic Updates mit Rollback. Für mittelgroße bis große Anwendungen mit vielen Mutationen ist dieser strukturierte Ansatz meist der praktikablere.
// useUserPosts.ts — TanStack Query's hierarchical query-key strategy
import { useQuery, useQueryClient } from "@tanstack/react-query";
function useUserPosts(userId: string) {
return useQuery({
queryKey: ["users", userId, "posts"],
queryFn: () => fetchUserPosts(userId),
staleTime: 60_000, // Data considered fresh for 60s, no background refetch
});
}
function useInvalidateUser(userId: string) {
const queryClient = useQueryClient();
return () => {
// Invalidates the whole subtree: user data, posts, comments, everything nested
queryClient.invalidateQueries({ queryKey: ["users", userId] });
};
}
Ein oft übersehener Vorteil von TanStack Query ist die eingebaute Unterstützung für parallele und abhängige Queries über useQueries und die enabled-Option, mit der eine Query erst startet, sobald eine vorherige Query erfolgreich abgeschlossen ist. Diese Bausteine der Caching Strategie von TanStack Query decken Anwendungsfälle ab, die bei SWR häufig zusätzliche, selbstgeschriebene Koordinationslogik erfordern.
4. Apollo Client: Normalisierter Objektgraph statt Query-Cache
Apollo Client verfolgt eine fundamental andere Caching Strategie als SWR und TanStack Query: Statt Ergebnisse pro Anfrage oder Query-Key zu cachen, zerlegt InMemoryCache jede Antwort in einzelne, normalisierte Objekte, referenziert über __typename und id. Diese Caching-Strategie sorgt dafür, dass ein einzelnes Objekt, unabhängig davon, über welche Query es ursprünglich geladen wurde, überall im Cache konsistent bleibt, sobald es sich ändert.
Der Preis für diese Konsistenz ist zusätzliche Komplexität: Type Policies, keyFields und Feld-Level-merge-Funktionen müssen korrekt konfiguriert sein, damit die Normalisierung tatsächlich funktioniert. Für Anwendungen, in denen dasselbe Produkt oder derselbe Nutzer in Dutzenden von Views auftaucht, zahlt sich diese aufwendigere Caching Strategie aus, weil ein einzelnes Update automatisch überall propagiert wird, ohne dass Entwickler manuell nachdenken müssen, welche Query-Keys betroffen sein könnten.
Diese objektbasierte Caching Strategie zeigt ihre Stärke besonders bei Formularen, die dasselbe Feld eines Objekts in mehreren Ansichten gleichzeitig bearbeitbar machen, etwa ein Inline-Edit in einer Tabelle und ein vollständiges Bearbeitungsformular in einem Modal. Beide Ansichten lesen automatisch denselben normalisierten Cache-Eintrag, sodass eine Änderung an einer Stelle sofort und ohne zusätzlichen Code an der anderen Stelle sichtbar wird.
5. Invalidierung im Vergleich: Tags, Keys und Objekt-IDs
Die drei Bibliotheken invalidieren Cache-Einträge auf grundlegend unterschiedliche Weise. SWR invalidiert über den exakten Cache-Key, meist die URL, was bedeutet, dass Entwickler bei einer Mutation genau wissen müssen, welche URLs betroffen sind. TanStack Query invalidiert über Query-Key-Präfixe, was ganze Teilbäume auf einmal erfasst und deutlich weniger manuelle Buchhaltung erfordert, solange die Key-Struktur von Anfang an durchdacht ist.
Apollo Client invalidiert implizit, indem es das betroffene Objekt direkt im normalisierten Cache aktualisiert, statt eine Query zu invalidieren: Jede Komponente, die dieses Objekt über irgendeine Query referenziert, sieht die Änderung automatisch. Diese Caching Strategie braucht kein explizites Invalidierungssystem für einzelne Objektänderungen, dafür aber sorgfältige Konfiguration der Normalisierung selbst, damit dieser Automatismus überhaupt greift.
// Comparing invalidation across the three caching strategies
// SWR: invalidate by exact key
mutate(`/api/users/${userId}`);
// TanStack Query: invalidate a whole subtree by key prefix
queryClient.invalidateQueries({ queryKey: ["users", userId] });
// Apollo Client: no explicit invalidation call needed for a single field change,
// cache.modify updates the object directly, all referencing views update automatically
cache.modify({
id: cache.identify({ __typename: "User", id: userId }),
fields: { name: () => newName },
});
Ein praktischer Zwischenweg für Teams, die TanStack Query nutzen, aber gelegentlich von objektbasierter Konsistenz profitieren möchten, ist eine dünne, selbstgeschriebene Normalisierungsschicht, die bekannte Entitäten wie Nutzer oder Produkte anhand ihrer ID in einer zentralen Map hält und bei jedem Query-Ergebnis synchron aktualisiert. Das ist kein Ersatz für Apollos vollautomatische Caching Strategie, reduziert aber Inkonsistenzen bei kritischen, häufig wiederverwendeten Entitäten spürbar, ohne gleich das komplette Caching-Modell zu wechseln.
6. Staleness-Konfiguration: staleTime, dedupingInterval und fetchPolicy
Jede der drei Bibliotheken bietet einen eigenen Mechanismus, um zu definieren, wie lange Daten als frisch gelten, bevor ein Hintergrund-Refetch ausgelöst wird. SWR nutzt dedupingInterval, um mehrfache identische Anfragen innerhalb eines kurzen Zeitfensters zu einer einzigen zusammenzufassen, revalidiert aber standardmäßig bei jedem Fenster-Fokus und jeder Netzwerk-Wiederverbindung. TanStack Query nutzt staleTime als expliziten Zeitraum, während dessen Daten als frisch gelten und kein automatischer Refetch stattfindet, was feingranularere Kontrolle pro Query erlaubt.
Apollo Client konfiguriert dieses Verhalten über fetchPolicy, etwa cache-first, cache-and-network oder network-only, wobei diese Einstellung eher grundsätzlich pro Query als über eine Zeitdauer gesteuert wird. Diese unterschiedlichen Modelle für dieselbe grundlegende Caching Strategie spiegeln unterschiedliche Philosophien wider: SWR optimiert für Einfachheit mit sinnvollen Defaults, TanStack Query für explizite Kontrolle, und Apollo Client für Konsistenz des Objektgraphen über die Zeitdauer hinweg.
Ein praktischer Tipp für alle drei Bibliotheken: unterschiedliche Datenarten sollten unterschiedliche Staleness-Werte bekommen. Nutzerprofile ändern sich selten und vertragen ein hohes staleTime von mehreren Minuten, während Preisinformationen in einem Warenkorb nahezu in Echtzeit aktuell sein müssen. Eine einheitliche, globale Staleness-Konfiguration für die gesamte Caching Strategie übersieht diese Unterschiede und führt entweder zu unnötigen Refetches oder zu spürbar veralteten Daten an kritischer Stelle.
7. Speicherverhalten und Garbage Collection
Ohne Garbage Collection würde jede Caching Strategie unbegrenzt wachsen, sobald eine Anwendung über Stunden hinweg genutzt wird und immer neue Daten in den Cache gelangen. TanStack Query löst dies über gcTime, früher cacheTime genannt: Ungenutzte Query-Ergebnisse, auf die keine aktive Komponente mehr zugreift, werden nach Ablauf dieser Zeitspanne automatisch aus dem Speicher entfernt. SWR verfolgt einen ähnlichen Ansatz, ist aber standardmäßig großzügiger und erfordert oft explizite Konfiguration für aggressive Speicherbereinigung.
Apollo Client bietet cache.gc() als manuell auslösbare Garbage Collection, die alle Objekte entfernt, auf die keine aktive Query mehr verweist, muss aber typischerweise nach cache.evict()-Aufrufen explizit aufgerufen werden. Für Anwendungen mit sehr großen Datenmengen und langen Sitzungen, etwa Admin-Dashboards, die stundenlang geöffnet bleiben, ist dieses Speicherverhalten ein oft unterschätzter Faktor bei der Wahl der Caching Strategie, weil ein unkontrolliert wachsender Cache selbst bei korrekter Datenanzeige die Anwendung im Laufe der Zeit spürbar verlangsamt.
Ein oft übersehener Nebeneffekt fehlender Garbage Collection zeigt sich in Single-Page-Applications mit clientseitigem Routing: Wechselt eine Nutzerin zwischen vielen unterschiedlichen Detailseiten, sammeln sich ohne Bereinigung die Cache-Einträge aller jemals besuchten Seiten an, auch wenn die Nutzerin längst zu einem völlig anderen Bereich der Anwendung navigiert ist. Eine sinnvoll konfigurierte Caching Strategie mit aktiver Garbage Collection hält den Speicherverbrauch dagegen proportional zur tatsächlich aktiven Nutzung, nicht zur gesamten Interaktionshistorie einer Sitzung.
8. Persistenz und Offline-Verhalten der drei Caches
Alle drei Bibliotheken unterstützen Cache-Persistenz über Storage-Adapter, mit denen der In-Memory-Cache periodisch in localStorage oder IndexedDB gesichert wird, um bei einem Seiten-Reload sofort mit bekannten Daten starten zu können. TanStack Query bietet dafür persistQueryClient als offizielles Plugin, SWR nutzt einen konfigurierbaren provider, und Apollo Client bietet apollo3-cache-persist als community-getragene Erweiterung.
Für echte Offline-Funktionalität, bei der Mutationen während einer Netzwerkunterbrechung zwischengespeichert und später automatisch nachgesendet werden, bietet TanStack Query mit seiner Mutation-Queue-Funktionalität den ausgereiftesten eingebauten Mechanismus unter den dreien. Apollo Client bietet mit der Apollo Link-Architektur zwar die Bausteine für eine eigene Offline-Queue, verlangt dafür aber deutlich mehr eigene Implementierungsarbeit als die eingebaute Lösung von TanStack Query.
Für Progressive Web Apps mit expliziten Offline-Anforderungen lohnt es sich, die Cache-Persistenz unabhängig von der gewählten Caching Strategie früh im Projekt zu planen, statt sie nachträglich einzubauen. Die Wahl des Storage-Adapters, IndexedDB für größere Datenmengen, localStorage für kleinere, beeinflusst direkt, wie viele Daten offline verfügbar bleiben und wie schnell die Anwendung nach einem Cold Start wieder nutzbar ist.
9. Caching Strategien im direkten Vergleich
Die folgende Tabelle fasst die entscheidenden Unterschiede der drei Caching Strategien zusammen und dient als Entscheidungsgrundlage für die nächste Projektarchitektur. Sie ersetzt keine individuelle Analyse des eigenen Datenmodells, macht aber die zentralen Kompromisse auf einen Blick sichtbar.
| Kriterium | SWR | TanStack Query | Apollo Client |
|---|---|---|---|
| Cache-Adressierung | URL-String | Hierarchische Query-Keys | Objektbasiert (Typename:id) |
| Normalisierung | Keine | Keine | Vollständig |
| Mutation-Werkzeuge | Minimal, manuell | onMutate/onError/onSettled | optimisticResponse, cache.modify |
| Bundle-Größe | Sehr klein | Klein bis mittel | Groß |
| Lernkurve | Niedrig | Mittel | Hoch |
| Idealer Einsatzbereich | Einfache REST-Anwendungen | REST und gemischte Datenquellen | Komplexe, GraphQL-basierte Datenmodelle |
Ein Aspekt, der in der Tabelle nicht direkt sichtbar wird, ist die Migrationsfreundlichkeit: Der Wechsel von SWR zu TanStack Query ist meist unkompliziert, weil beide auf ähnlichen Konzepten wie Hooks und Cache-Keys basieren. Ein Wechsel von oder zu Apollo Client ist deutlich aufwendiger, weil er in der Regel auch einen Wechsel der zugrunde liegenden Datenschicht von REST zu GraphQL oder umgekehrt bedeutet, was weit über den reinen Austausch der Caching Strategie hinausgeht.
Keine dieser Caching Strategien ist in jedem Kontext überlegen. SWR eignet sich für Teams, die minimalen Overhead und schnellen Einstieg wollen. TanStack Query bietet die beste Balance aus Kontrolle und Einfachheit für die meisten REST- und gemischten Anwendungen. Apollo Client lohnt sich, sobald ein GraphQL-Datenmodell mit stark verflochtenen Objekten über viele Views hinweg konsistent gehalten werden muss.
Diese Entscheidung sollte, wie bei jeder grundlegenden Architekturfrage, dokumentiert und im Team geteilt werden, damit spätere Erweiterungen der Anwendung dieselbe Caching Strategie konsequent weiterführen, statt mehrere konkurrierende Ansätze parallel im selben Projekt zu etablieren.
Mironsoft
Caching-Architektur für React mit SWR, TanStack Query und Apollo Client
Die richtige Caching-Strategie für eure Datenmenge?
Wir analysieren euer Datenmodell und empfehlen die Caching-Bibliothek, die zu Konsistenzanforderungen, Teamgröße und Bundle-Budget tatsächlich passt.
Cache-Audit
Analyse bestehender Invalidierungslogik auf redundante und fehlende Refetches
Migration
Wechsel zwischen SWR, TanStack Query und Apollo Client ohne Big-Bang-Risiko
Speicher-Tuning
gcTime, cache.gc() und Persistenz für lang laufende Sitzungen konfigurieren
10. Zusammenfassung
Die Wahl zwischen SWR, TanStack Query und Apollo Client bleibt am Ende eine Frage der tatsächlichen Anforderungen an Konsistenz, nicht der reinen Popularität einer Bibliothek.
Wer unsicher ist, sollte mit der kleinsten sinnvollen Caching Strategie für den aktuellen Anwendungsfall starten und erst bei konkretem Konsistenz- oder Performance-Bedarf zu einer aufwendigeren Lösung wechseln, statt von Beginn an die komplexeste Option zu wählen.
Die drei verglichenen Caching Strategien lösen dieselbe Grundaufgabe mit unterschiedlichem Aufwand und unterschiedlicher Präzision. SWRs Stale-While-Revalidate über einfache URL-Keys eignet sich für schlanke Anwendungen mit überwiegend unabhängigen Datenquellen. TanStack Querys hierarchische Query-Keys bieten mehr Kontrolle bei Invalidierung und strukturierte Mutation-Werkzeuge, ohne die Komplexität einer vollständigen Objektnormalisierung.
Unabhängig von der gewählten Bibliothek gilt: Unterschiedliche Datenarten verdienen unterschiedliche Staleness-Werte, und Garbage Collection sollte nie dem Zufall überlassen werden, besonders in lang laufenden Sitzungen wie Admin-Dashboards.
Apollo Clients normalisierter Objektgraph bleibt die aufwendigste, aber auch konsistenteste Caching Strategie für stark verflochtene GraphQL-Datenmodelle. Staleness-Konfiguration über staleTime, dedupingInterval oder fetchPolicy, Garbage Collection über gcTime oder cache.gc(), und Persistenz-Adapter für Offline-Szenarien runden das jeweilige Caching-Modell ab. Die richtige Wahl hängt weniger von persönlicher Präferenz ab als von der tatsächlichen Struktur der Daten und der Größe der Anwendung.
Ein Wechsel der Caching Strategie im laufenden Betrieb bleibt möglich, sollte aber immer schrittweise und mit klaren Erfolgskriterien pro Migrationsabschnitt erfolgen, statt die gesamte Datenschicht in einem einzigen, riskanten Schritt auszutauschen.
Caching Strategien im Vergleich — Das Wichtigste auf einen Blick
SWR
Minimalistisches Stale-While-Revalidate über URL-Keys, ideal für schlanke Anwendungen.
TanStack Query
Hierarchische Query-Keys, strukturierte Mutations, gute Balance aus Kontrolle und Einfachheit.
Apollo Client
Vollständige Objektnormalisierung, ideal für komplexe GraphQL-Datenmodelle.
Entscheidung
Datenmodell-Komplexität und Konsistenzanforderungen entscheiden, nicht Bekanntheit der Bibliothek.
Alle drei Caching Strategien profitieren von datenartspezifischer Staleness-Konfiguration statt eines globalen Einheitswertes für die gesamte Anwendung.
Die folgenden häufig gestellten Fragen fassen die wichtigsten Entscheidungspunkte rund um Caching Strategien noch einmal kompakt zusammen.