<v/>
{ }
Vue.js · GraphQL · Apollo Client · Caching
Vue GraphQL: Queries, Caching
und Tooling im Praxiseinsatz

REST-Endpunkte zu ersetzen ist nicht das Ziel von GraphQL – das Ziel ist, genau die Daten zu laden, die eine Komponente braucht, und sie im richtigen Moment zu cachen. Wer Vue GraphQL ohne Cache-Strategie betreibt, lädt dieselben Daten mehrfach und verliert den größten Vorteil des Ansatzes.

18 Min. Lesezeit Apollo Client · InMemoryCache · Fragments · optimistische Updates Vue 3 · Apollo 3 · GraphQL 16

1. Warum Vue und GraphQL so gut zusammenpassen

Vue GraphQL ist mehr als eine Technologiekombination – es ist ein Paradigmenwechsel in der Art, wie Komponenten Daten konsumieren. In einer klassischen REST-Architektur definiert der Server, welche Daten ein Endpunkt zurückgibt. In einer Vue GraphQL-Architektur definiert die Komponente selbst, welche Felder sie benötigt. Das führt zu weniger Over-Fetching, weniger Under-Fetching und direkt zu schlankerem JavaScript-Payload im Browser.

Der komponentenbasierte Aufbau von Vue passt besonders gut zur Fragment-Architektur von GraphQL. Jede Komponente deklariert ihren eigenen Datenbedarf als Fragment, und der Apollo Client setzt diese Fragmente zu vollständigen Queries zusammen. Das Ergebnis ist, dass Refactoring einer Komponente – das Hinzufügen oder Entfernen von Feldern – automatisch die Query anpasst, ohne dass ein Backend-Entwickler einen neuen Endpunkt implementieren muss. Für Teams, die schnell iterieren, ist Vue GraphQL ein erheblicher Geschwindigkeitsgewinn.

Gleichzeitig bringt Vue GraphQL echter Komplexität mit sich: Cache-Invalidierung, optimistische Updates, Subscription-Handling und das richtige Verständnis des normalisierten Caches sind Themen, die Entwickler ohne Vorbereitung schnell in Schwierigkeiten bringen. Dieser Artikel deckt alle diese Bereiche systematisch ab und zeigt, wie man eine Vue GraphQL-Anwendung so aufbaut, dass sie auch nach einem Jahr ohne grundlegende Umstrukturierung wartbar bleibt.

2. Apollo Client in Vue 3 korrekt einrichten

Der Einstieg in Vue GraphQL beginnt mit der korrekten Konfiguration des Apollo Clients. Das Paket @apollo/client zusammen mit @vue/apollo-composable bildet die Grundlage. Entscheidend ist, dass der Apollo Client als Plugin in die Vue-Applikation eingebunden wird und dabei eine InMemoryCache-Instanz erhält, die bereits initial für die eigene Schema-Struktur konfiguriert ist. Ein häufiger Fehler: Die InMemoryCache wird ohne typePolicies instanziiert, was dazu führt, dass Apollo den Cache nicht korrekt normalisieren kann, sobald Entitäten ohne Standard-id-Felder zurückgegeben werden.

Der ApolloLink ist das Middleware-System des Apollo Clients. Typischerweise besteht die Link-Chain aus einem authLink, der den Authorization-Header anhängt, und einem httpLink, der den eigentlichen HTTP-Request ausführt. Für WebSocket-basierte Subscriptions kommt ein splitLink hinzu, der HTTP-Requests und WebSocket-Verbindungen automatisch auf den richtigen Transport-Channel leitet. Wer diese Konfiguration sauber aufsetzt, vermeidet später schwer debuggbare Authentifizierungsprobleme und Race-Conditions beim Token-Refresh.


// src/plugins/apollo.js
// Apollo Client setup for Vue 3 with auth link and cache policies
import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client/core'
import { setContext } from '@apollo/client/link/context'
import { provideApolloClient } from '@vue/apollo-composable'

const httpLink = createHttpLink({
  uri: import.meta.env.VITE_GRAPHQL_URL,
})

// Attach JWT token to every request
const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem('auth_token')
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  }
})

const cache = new InMemoryCache({
  typePolicies: {
    // Normalize Product by slug, not by id
    Product: {
      keyFields: ['slug'],
    },
    // Merge paginated lists instead of replacing them
    Query: {
      fields: {
        products: {
          keyArgs: ['filter', 'category'],
          merge(existing = { items: [] }, incoming) {
            return { ...incoming, items: [...existing.items, ...incoming.items] }
          },
        },
      },
    },
  },
})

export const apolloClient = new ApolloClient({
  link: from([authLink, httpLink]),
  cache,
  defaultOptions: {
    watchQuery: { fetchPolicy: 'cache-and-network' },
    query: { fetchPolicy: 'network-only', errorPolicy: 'all' },
  },
})

// Install into Vue app
export function installApollo(app) {
  provideApolloClient(apolloClient)
}

3. Queries strukturieren: useQuery und Composables

In Vue GraphQL mit Composition API ist useQuery aus @vue/apollo-composable die zentrale Funktion für das Laden von Daten. useQuery gibt reaktive Refs zurück: result enthält die Antwortdaten, loading zeigt den Ladezustand an, error enthält aufgetretene Fehler. Diese Refs integrieren sich nahtlos in das reaktive System von Vue 3, sodass Templates automatisch aktualisiert werden, sobald neue Daten eintreffen. Der Query wird reaktiv – ändert sich eine Variable, führt Apollo automatisch einen neuen Fetch durch.

Queries gehören nicht direkt in Komponenten, sondern in dedizierte Composables im Ordner src/composables/. Das hat mehrere Vorteile: Das Composable kann in mehreren Komponenten wiederverwendet werden, die Query-Logik ist testbar ohne das Rendern einer Komponente, und die Komponente selbst bleibt schlank und auf die Darstellung fokussiert. Das Muster useProduct(slug) als Composable, das intern useQuery aufruft und nach außen nur { product, loading, error } exponiert, ist das Standardmuster in gut strukturierten Vue GraphQL-Projekten.

4. InMemoryCache: Normalisierung und Cache-Policies

Der InMemoryCache ist das Herzstück jeder Vue GraphQL-Anwendung mit Apollo Client. Er funktioniert als normalisierter, flacher Datenspeicher: Jede Entität wird unter einem Schlüssel aus Typ und ID gespeichert, zum Beispiel Product:42. Wenn mehrere Queries dieselbe Entität zurückgeben, wird sie nur einmal im Cache gespeichert. Jede Komponente, die diese Entität abonniert, erhält automatisch das Update, sobald eine Mutation die Entität verändert. Dieses Konzept ist der entscheidende Unterschied zwischen Apollo-basiertem Vue GraphQL und einem einfachen fetch-Aufruf in einem Composable.

Die typePolicies-Konfiguration ist der Schlüssel zu einem korrekt funktionierenden Cache. Mit keyFields legt man fest, welche Felder den eindeutigen Schlüssel einer Entität bilden. Mit merge-Funktionen steuert man, wie eingehende Daten mit bestehenden Daten zusammengeführt werden – entscheidend für Pagination, bei der neue Seiten an bestehende Listen angehängt werden sollen. Mit read-Funktionen transformiert man Daten beim Lesen aus dem Cache, zum Beispiel um clientseitig berechnete Felder hinzuzufügen. Wer typePolicies sorgfältig konfiguriert, baut eine Vue GraphQL-Anwendung, in der Datenkonsistenz automatisch gewährleistet ist.

5. Fragments: Queries modular und wiederverwendbar machen

GraphQL-Fragments sind in Vue GraphQL-Projekten das Mittel, um Queries modular und wartbar zu halten. Ein Fragment definiert eine benannte Auswahl von Feldern für einen bestimmten Typ. Die Komponente, die diese Daten darstellt, definiert ihr Fragment, und die übergeordnete Query schließt dieses Fragment ein. Das Ergebnis: Wenn die Produktkarte-Komponente ein neues Feld braucht, wird nur das Fragment der Produktkarte angepasst – die Query auf der Listenseite, die das Fragment einschließt, aktualisiert sich automatisch.

Apollo Client kann Fragment-Daten auch direkt aus dem Cache lesen und schreiben, ohne eine vollständige Query auszuführen. Die Methode cache.readFragment liest eine Entität nach Typ und ID aus dem Cache. cache.writeFragment schreibt Daten in den Cache, ohne einen Netzwerk-Request auszuführen. Dieses Muster ist für optimistische Updates unverzichtbar: Man schreibt die erwartete Antwort bereits vor der Mutation in den Cache, und die Komponente zeigt sofort den neuen Zustand an – erst wenn die Mutation abgeschlossen ist, wird der Cache mit der tatsächlichen Server-Antwort überschrieben.


// src/graphql/fragments/product.js
// Reusable product fragment — import in any query that needs product data
import { gql } from '@apollo/client/core'

export const PRODUCT_CARD_FRAGMENT = gql`
  fragment ProductCard on Product {
    id
    slug
    name
    price {
      amount
      currency
    }
    thumbnail {
      url
      altText
    }
    inStock
  }
`

export const PRODUCT_DETAIL_FRAGMENT = gql`
  fragment ProductDetail on Product {
    ...ProductCard
    description
    images {
      url
      altText
      width
      height
    }
    variants {
      id
      sku
      attributes { name value }
      price { amount currency }
    }
  }
  ${PRODUCT_CARD_FRAGMENT}
`

// src/composables/useProducts.js
// Composable that exposes paginated product list
import { useQuery } from '@vue/apollo-composable'
import { gql } from '@apollo/client/core'
import { PRODUCT_CARD_FRAGMENT } from '@/graphql/fragments/product'
import { computed, ref } from 'vue'

const PRODUCTS_QUERY = gql`
  query Products($filter: ProductFilter, $page: Int!) {
    products(filter: $filter, page: $page, perPage: 24) {
      total
      items { ...ProductCard }
    }
  }
  ${PRODUCT_CARD_FRAGMENT}
`

export function useProducts(filter) {
  const page = ref(1)
  const { result, loading, error, fetchMore } = useQuery(PRODUCTS_QUERY, () => ({
    filter: filter.value,
    page: page.value,
  }), { fetchPolicy: 'cache-and-network' })

  const products = computed(() => result.value?.products?.items ?? [])
  const total = computed(() => result.value?.products?.total ?? 0)

  function loadMore() {
    page.value++
    fetchMore({ variables: { page: page.value } })
  }

  return { products, total, loading, error, loadMore }
}

6. Mutations und optimistische Updates

Mutations in Vue GraphQL mit Apollo werden über useMutation aus @vue/apollo-composable ausgeführt. Im Gegensatz zu useQuery wird eine Mutation nicht automatisch ausgeführt, sondern nur auf expliziten Aufruf der zurückgegebenen mutate-Funktion. Das wichtigste Feature für eine gute User Experience ist die optimisticResponse-Option: Man gibt die erwartete Antwort der Mutation an, bevor der Request überhaupt gesendet wurde. Apollo schreibt diese optimistische Antwort sofort in den Cache, und alle abhängigen Queries und Komponenten aktualisieren sich unmittelbar. Sobald die echte Antwort vom Server eintrifft, überschreibt Apollo die optimistische Antwort mit den tatsächlichen Daten.

Cache-Updates nach Mutations sind in Vue GraphQL ein häufiges Thema. Wenn eine Mutation eine neue Entität erstellt, ist diese noch nicht im Cache. Man muss den Cache manuell aktualisieren, indem man in der update-Funktion der Mutation die bestehende Liste aus dem Cache liest, die neue Entität hinzufügt und die Liste zurück in den Cache schreibt. Wenn die Mutation eine bestehende Entität aktualisiert, geschieht das automatisch – Apollo normalisiert die Antwort und aktualisiert die Entität im Cache unter dem bekannten Schlüssel. Wer dieses Verhalten versteht, braucht nach Mutations nur selten einen expliziten refetch.

7. Fehlerbehandlung und Ladezustände richtig abbilden

Fehlerbehandlung in Vue GraphQL hat zwei Ebenen: Netzwerkfehler, bei denen der Request gar nicht oder mit einem HTTP-Fehlercode zurückkommt, und GraphQL-Errors, bei denen der Request erfolgreich war, aber das Schema Validierungsfehler oder applikationsseitige Fehler zurückgibt. Apollo unterscheidet zwischen diesen beiden Fehlerarten, und die richtige Konfiguration der errorPolicy entscheidet, wie mit gemischten Antworten umgegangen wird – also Antworten, die teilweise Daten und teilweise Errors enthalten. Mit errorPolicy: 'all' werden sowohl Daten als auch Errors zurückgegeben, was in komplexen Queries sinnvoll ist, bei denen ein Fehler in einem Zweig nicht die gesamte Query invalidieren soll.

Ladezustände in Vue GraphQL-Komponenten sollten explizit designed werden. Ein häufiger Fehler ist, einfach mit v-if="!loading" den gesamten Inhalt auszublenden. Das führt zu Layout-Shifts und einer schlechten User Experience. Besser ist ein Skeleton-Loading-Pattern: Die Komponente rendert bei loading === true eine Platzhalterversion mit derselben Struktur und denselben Dimensionen wie der eigentliche Inhalt. Vue GraphQL macht dieses Muster einfach, weil der reaktive loading-Ref direkt im Template ausgewertet werden kann.

8. Tooling: Apollo DevTools, GraphQL Codegen, VSCode

Das Tooling-Ökosystem rund um Vue GraphQL ist ausgereift und beschleunigt die Entwicklung erheblich. Die Apollo Client DevTools für Chrome und Firefox erlauben, den gesamten Cache zu inspizieren, aktive Queries und Mutations zu sehen und Queries direkt im Browser auszuführen. Wer einmal den normalisierten Cache in den DevTools gesehen hat – wie jede Entität unter ihrem Schlüssel gespeichert ist und wie Queries auf Cache-Einträge verweisen – versteht das Apollo-Caching-Modell viel schneller als durch bloßes Lesen der Dokumentation.

GraphQL Code Generator ist für größere Vue GraphQL-Projekte unverzichtbar. Das Tool liest das GraphQL-Schema und alle .graphql-Dateien im Projekt und generiert daraus TypeScript-Typen für alle Queries, Mutations und Fragments. Das Ergebnis: Vollständige Typsicherheit zwischen GraphQL-Schema und Vue-Komponenten. Wenn ein Feld im Schema umbenannt wird, zeigt TypeScript sofort alle betroffenen Stellen im Frontend an. Die Konfiguration ist minimal – ein codegen.yml reicht aus – und die Integration in den Entwicklungsserver mit --watch generiert Typen bei jeder Schemaänderung automatisch neu.


// codegen.yml — GraphQL Code Generator config for Vue 3 + TypeScript
// Run: npx graphql-code-generator --config codegen.yml --watch
// Generates typed hooks and fragment types from schema + operations

overwrite: true
schema: "${VITE_GRAPHQL_URL}"
documents: "src/**/*.{graphql,gql,ts,vue}"
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-vue-apollo
    config:
      vueCompositionApiImportFrom: vue
      withCompositionFunctions: true
      withSmartQuery: false
      useTypeImports: true

// After generation — fully typed composable usage
// src/composables/useProduct.ts
import { useProductQuery } from '@/generated/graphql'
import { computed } from 'vue'

export function useProduct(slug: string) {
  // useProductQuery is auto-generated — fully typed result, variables, etc.
  const { result, loading, error } = useProductQuery(
    () => ({ slug }),
    { fetchPolicy: 'cache-and-network' }
  )

  const product = computed(() => result.value?.product ?? null)
  return { product, loading, error }
}

9. Strategien im Vergleich: fetch-policy Optionen

Die fetchPolicy einer Query ist eine der wichtigsten Stellschrauben in jeder Vue GraphQL-Anwendung. Die falsche Policy führt entweder zu unnötig vielen Netzwerkanfragen oder zu veralteten Daten, die der User zu sehen bekommt. Das Verstehen der verfügbaren Optionen ist grundlegend für eine performante Vue GraphQL-Architektur.

fetchPolicy Cache lesen? Netzwerk-Request? Idealer Einsatz
cache-first Ja (primär) Nur bei Cache-Miss Statische Referenzdaten, Kategorien
cache-and-network Ja (sofort) Immer Listen mit häufigen Updates
network-only Nein Immer Kritische Daten, Checkout
cache-only Ja Nie Offline-Szenarien, optimistische UI
no-cache Nein Immer Sensitive Daten ohne Cache-Speicherung

Im Entwickleralltag empfiehlt sich folgende Faustregel für Vue GraphQL-Projekte: cache-and-network als Standard für alle Listen und Detailseiten, weil der User sofort Daten aus dem Cache sieht und im Hintergrund der aktuellste Stand geladen wird. network-only für transaktionale Queries wie Bestellstatus oder Kontosaldo, wo veraltete Daten kritische Konsequenzen haben können. cache-first für Konfigurationsdaten und statische Listen wie Kategorien oder Länderauswahlen, die sich nur bei Deploys ändern.

Mironsoft

Vue GraphQL · Apollo Client · TypeScript · Frontend-Architektur

Vue GraphQL-Architektur für euer Projekt?

Wir konzipieren und implementieren Vue GraphQL-Integrationen mit Apollo Client – von der Cache-Strategie über typisierte Composables bis zum vollständigen GraphQL Code Generator-Setup.

Schema-Design

GraphQL-Schema entwerfen, typePolicies konfigurieren und Fragments strukturieren

Performance-Optimierung

fetchPolicy-Strategie, Pagination mit fetchMore und optimistische Updates

Code-Generierung

GraphQL Code Generator einrichten und typsichere Composables generieren

10. Zusammenfassung

Vue GraphQL mit Apollo Client ist kein Drop-in-Ersatz für REST-Aufrufe, sondern ein vollständiges Datenverwaltungssystem für Frontend-Anwendungen. Der normalisierte Cache ist der zentrale Vorteil: Jede Entität wird einmal gespeichert, und Mutations aktualisieren automatisch alle abhängigen Views. Fragments machen Queries modular und an Komponenten gekoppelt. Die fetchPolicy steuert das Verhältnis zwischen Cache-Effizienz und Datenfreshness. Optimistische Updates geben dem User sofortiges Feedback, ohne auf die Server-Antwort zu warten.

Das Tooling rund um Vue GraphQL ist ausgereift: Apollo DevTools für Cache-Inspektion, GraphQL Code Generator für typsichere Composables und die VSCode-GraphQL-Extension für Syntax-Highlighting und Auto-Completion direkt in Query-Strings. Wer diese Tools von Anfang an in ein Projekt integriert, baut schneller, macht weniger Fehler und kann Refactorings mit Konfidenz durchführen, weil TypeScript und der Generator sofort auf Inkonsistenzen hinweisen.

Vue GraphQL — Das Wichtigste auf einen Blick

Cache-Normalisierung

typePolicies und keyFields konfigurieren – jede Entität wird einmal gespeichert und automatisch durch Mutations aktualisiert.

Fragments & Composables

Komponenten deklarieren eigene Fragments, Composables kapseln useQuery – Queries bleiben wartbar und wiederverwendbar.

fetchPolicy-Strategie

cache-and-network als Standard, network-only für transaktionale Daten, cache-first für statische Listen.

Code Generator

GraphQL Code Generator generiert TypeScript-Typen und typisierte Composables – vollständige Typsicherheit zwischen Schema und Vue.

11. FAQ: Vue GraphQL

1useQuery vs. useLazyQuery?
useQuery läuft sofort und reaktiv. useLazyQuery wird erst auf expliziten Aufruf von load() ausgeführt – für Suche oder bedingte Datenabrufe.
2Cache nach Mutation manuell aktualisieren?
update-Funktion: cache.readQuery, neue Entität hinzufügen, cache.writeQuery zurückschreiben. Bei Updates bestehender Entitäten läuft das automatisch.
3Wann optimistische Updates?
Bei vorhersehbaren Aktionen mit kurzer Latenz: Likes, Warenkorb, Formular-Submissions. Apollo rollt bei Fehlern automatisch zurück.
4Cache nach Seitenrefresh weg?
InMemoryCache ist nicht persistent. apollo3-cache-persist speichert den Cache in localStorage oder IndexedDB für PWA und Offline-Szenarien.
5Pagination mit fetchMore?
fetchMore lädt weitere Seiten, merge-Funktion in typePolicy fügt sie zusammen. keyArgs definiert den Cache-Key unabhängig von Pagination-Parametern.
6GraphQL Code Generator Setup?
codegen.yml erstellen, Schema-URL und Plugins konfigurieren. Mit --watch läuft der Generator im Hintergrund und generiert Typen bei Schemaänderungen.
7Feld nicht im Fragment — was passiert?
Nicht angeforderte Felder sind im Cache undefined. Code Generator macht diesen Fall durch TypeScript-Fehler zur Compilezeit sichtbar.
8Cache-Miss debuggen?
Apollo DevTools öffnen, Cache-Tab inspizieren. Wenn Entität fehlt, stimmt keyFields nicht. cache.extract() loggt den vollständigen Cache-Inhalt.
9Apollo Client mit Pinia kombinieren?
Ja – Apollo verwaltet Server-State, Pinia lokalen UI-State. Apollo-Daten nicht dupliziert in Pinia speichern.
10N+1 in Vue GraphQL verhindern?
Clientseitig: zusammenhängende Daten in einer Query mit Fragment-Baum laden statt in Schleifen. Serverseitig: DataLoader für Batching implementieren.