GraphQL SWR Caching (NestJS, Code-First)
Übersicht
Cache nach Intention, nicht Mechanismus. Eine Policy-Form, ein beobachtbarer Event-Stream, eine Ablaufverfolgung pro Anfrage. Standardmäßig auf Service-Ebene montieren; nur bei gemessenem Bedarf auf Feld-/Operations-/Transportebene eskalieren.
Drei harte Wahrheiten, die jede Entscheidung prägen:
- Apollo Server hat kein eingebautes Stale-While-Revalidate. Sein
responseCachePluginist nur TTL-basiert und auf Operationsebene. Echter SWR kommt von Ihrer eigenen Schicht (async-cache-dedupeodermercurius-cacheauf dem Mercurius-Treiber) oder von einem CDN. - Scope standardmäßig
public. Ein öffentlicher Eintrag wird von jedem Aufrufer unverändert geteilt, daher ist er nur korrekt, wenn das Ergebnis für alle byte-identisch ist. Sobald ein Wert aufruferabhängig ist, muss er alsperUsermarkiert werden – sonst kommt es zu einem Datenleck. - Nest-Interzeptoren umschließen keine Service-Methoden, und
@nestjs/cache-manager'sCacheInterceptorfunktioniert in GraphQL nicht. Der gewünschte Decorator auf Service-Ebene ist daher ein Methoden-Wrapper über einer Deduplizierungs-Engine – kein Framework-Interzeptor.
Wann verwenden
- NestJS GraphQL (Code-First) mit Resolvern/Services, die wiederholt eine DB oder externe API aufrufen.
- Sie müssen Latenzspitzen und Stampedes bei TTL-Ablauf vermeiden (echter SWR).
- Sie müssen die Sicherheit von Multi-User-Caches / Lecksicherheit beurteilen.
- Sie benötigen Transparenz über Hit-/Stale-/Miss-Entscheidungen.
- Sie möchten das Caching vollständig deaktivieren können (Dev, CI oder Incident) mit einem einzigen Flag (siehe Abschnitt 1,
setup-disable-switch). - Sie rüsten die Caching-Funktionalität zum ersten Mal in eine App ein (siehe Abschnitt 1).
Wann NICHT verwenden:
- Sie sind bereits hinter einem CDN, das öffentliche GET/APQ-Response-Caching durchführt – dort zuerst ansetzen.
- Write-/Mutations-Pfade oder per-User-Daten ohne Wiederverwendung und geringer Read-Amplifikation (Caching erhöht das Risiko bei geringem Nutzen).
Der Callback-Vertrag
key, @CacheTag und @InvalidateTags akzeptieren Funktionen. Setzen Sie deren Argumente richtig, sonst
fehlt der Cache still und die Tags bereinigen nicht.
| Callback | Signatur | Was args ist |
|---|---|---|
key | (args, ctx) => unknown[] | Promise<unknown[]> | die Argumentliste der Methode in Aufrufreihenfolge — args[0], niemals args.id |
@CacheTag | (result, args) => string[] | null | dieselbe Liste; result ist der Rückgabewert der Methode |
@InvalidateTags | (result, args) => string[] | dieselbe Liste; result ist der Rückgabewert des Schreibzugriffs |
- Positional, nicht benannt. Für
getTitle(id: string)ist die idargs[0]. - Result kommt zuerst bei beiden Tag-Callbacks. Die rohe Engine
referencesist(args, key, result)— andere Reihenfolge, anderes erstes Argument (siehedriver-async-cache-dedupe). ctx(userId,tenantId,locale, plusgen) wird pro Anfrage abgeleitet und vonbuildCacheContextzusammengestellt; außerhalb einer Anfrage ist es leer, daher übergibt ein Job, was er benötigt, als Argument.- Typisierung: die Standardwerte sind
unknown[]/unknown— annotieren Sie das Tuple (@Cacheable<[string]>,@CacheTag<[string]>) oder die Callback-Körper lassen sich nicht kompilieren. - Auf Feld-Ebene ist es anders: dort ist
argsdas benannte Argumentobjekt des Resolvers undctxist der GraphQL-Kontext (driver-mercurius-cache).
Vollständiger Vertrag, das Key-Bundle { a, k, u } und die Randfälle bei der Serialisierung:
policy-key-derivation. Laufzeitimplementierung: setup-decorator-runtime.
Regelkategorien nach Priorität
| Priorität | Kategorie | Auswirkung | Präfix |
|---|---|---|---|
| 1 | Einmal-Setup | KRITISCH | setup- |
| 2 | Cache-Policy | KRITISCH | policy- |
| 3 | Leck-Sicherheit | KRITISCH | safety- |
| 4 | Invalidierung | HOCH | invalidation- |
| 5 | Beobachtbarkeit | MITTEL | observability- |
| 6 | Treiber | MITTEL | driver- |
Kurzreferenz
1. Einmal-Setup (KRITISCH)
setup-decorator-runtime- Implementieren Sie die Decorators als Methoden-Wrapper mit stabilen Namensetup-disable-switch- Liefern Sie einen Konfigurationsschalter, der den Service-Level-Cache umgehtsetup-engine-choice- Verwenden Sie eine deduplizierende Engine, und zählen Sie TTL in Sekundensetup-fail-open- Fail-Open und niemals Fehler cachensetup-file-placement- Platzieren Sie die Funktionalität nach dem, der sie importiertsetup-identity-context- Lesen Sie den Aufrufer aus AsyncLocalStorage, nicht aus Scope.REQUESTsetup-module-wiring- Verdrahten Sie das Cache-Modul einmal mit forRootAsyncsetup-storage-and-invalidation- Aktivieren Sie Invalidierung und setzen Sie referencesTTL und GC
2. Cache-Policy (KRITISCH)
policy-five-knobs- Kennen Sie die fünf Stellschraubenpolicy-key-derivation- Leiten Sie den Key automatisch ab, niemals mit JSON.stringifypolicy-mount-at-service- Montieren Sie standardmäßig auf Service-Ebenepolicy-one-writing-pattern- Die Decorators sind das eine Schreibmusterpolicy-service-escape-hatch- Die Service-API ist eine Ausweichmöglichkeit, kein zweites Muster
3. Leck-Sicherheit (KRITISCH)
safety-default-public- Scope standardmäßig public — markieren Sie aufrufervariante Reads als perUsersafety-key-dimensions- Scope deckt nur den Benutzer ab — jede andere Dimension kommt in den Keysafety-no-leak-checklist- Führen Sie bei jedem gecachten Read die No-Leak-Checkliste durch
4. Invalidierung (HOCH)
invalidation-after-success- Nur nach einem erfolgreichen Write invalidiereninvalidation-atomic-purge- Atomar bereinigen und Tag-Kardinalität begrenzeninvalidation-generation-prefix- Verwenden Sie ein Generations-Präfix für große oder grobe Domäneninvalidation-tags-purge-only- Tags sind nur zum Bereinigen, niemals zum Nachschlagen
5. Beobachtbarkeit (MITTEL)
observability-limits-of-events- Wissen, was die Engine nicht melden kannobservability-metrics-and-tests- Verfolgen Sie die Metriken, die den Fix beweisen, und testen Sie Ergebnisseobservability-per-request-trace- Geben Sie eine Ablaufverfolgung pro Anfrage aus, dev-only bei GraphQLobservability-structured-events- Emittieren Sie strukturierte Cache-Ereignisse, keine Log-Strings
6. Treiber (MITTEL)
driver-apollo-response-cache- Option C — Apollo responseCachePlugin (Operationsebene, nur TTL)driver-apollo-vs-mercurius- Wählen Sie die Feld-Level-Option aus Ihrem Treiberdriver-async-cache-dedupe- Option A — async-cache-dedupe (Service-Ebene, Transport-agnostisch)driver-cdn-transport- Option E — Transport / CDN SWR (nur öffentliche Reads)driver-envelop-response-cache- Option D — Envelop useResponseCache (nicht nativ in NestJS)driver-federation-router- Nur Federation — Apollo Router Response Cachingdriver-mercurius-cache- Option B — mercurius-cache (Feldebene, echter SWR)driver-version-pinning- Versionen fixieren — dieser Bereich driftet
Wie verwenden
Beginnen Sie mit dem Abschnitt „Einmal-Setup“, lesen Sie dann die Policy-, Safety- und Invalidierungsregeln
für die vor Ihnen liegende Entscheidung. Regeldateien befinden sich unter rules/:
rules/setup-module-wiring.md
rules/_sections.md
Jede Regeldatei enthält:
- Was die Regel besagt und welchen Fehler sie verhindert
- Ein inkorrektes Beispiel mit Erklärung
- Ein korrektes Beispiel mit Erklärung
- Referenzen
Vollständig kompiliertes Dokument
Die vollständige Anleitung mit allen erweiterten Regeln in einem Dokument finden Sie unter AGENTS.md.