GraphQL Code Generator Config vertieft: Typen, Hooks und Fragmente
AI generated
{ }
type
GraphQL · Code Generator · TypeScript · React
GraphQL Code Generator Config vertieft
Typen, Hooks und Fragmente sauber konfigurieren

Die Standard-codegen.yml aus dem Quickstart deckt gerade mal die Hälfte dessen ab, was GraphQL Code Generator wirklich kann. Custom Scalars, Fragment Masking und das near-operation-file Preset entscheiden darüber, ob generierter Code eine Freude oder eine Zumutung ist.

19 Min. Lesezeit codegen.yml · Presets · Custom Scalars · Fragment Masking TypeScript · React · Apollo Client

1. Warum die codegen.yml mehr Aufmerksamkeit verdient

Die meisten Teams richten GraphQL Code Generator einmal über den Init-Wizard ein und rühren die codegen.yml danach nie wieder an, obwohl genau diese Datei entscheidet, ob der generierte Code im Alltag Zeit spart oder zusätzliche Reibung erzeugt. Eine Standard-Konfiguration ohne Fragment Masking erzeugt Typen, die jede Komponente Zugriff auf das komplette Query-Ergebnis geben, statt nur auf die Felder, die die Komponente tatsächlich angefordert hat, was die eigentliche Stärke von GraphQL, kolokierte Datenanforderungen, im generierten Code wieder auflöst.

Eine durchdachte GraphQL Code Generator Config zahlt sich vor allem bei wachsenden Codebasen aus: Custom Scalars, die DateTime korrekt als Date statt als any typisieren, das near-operation-file Preset, das Typen neben die zugehörige Query-Datei legt statt in eine monolithische generated.ts, und Fragment Masking, das Komponentengrenzen im Typsystem sichtbar macht. Alle drei Themen behandelt dieser Artikel im Detail, jenseits dessen, was die offizielle Quickstart-Anleitung zeigt.

2. Grundaufbau einer codegen.yml: schema, documents, generates

Jede codegen.yml besteht aus drei Kernblöcken: schema definiert die Quelle des GraphQL-Schemas, entweder als lokale SDL-Datei, als Introspection-Endpoint oder als Kombination aus beidem für unterschiedliche Umgebungen. documents definiert, welche Dateien nach GraphQL-Operationen durchsucht werden, meist ein Glob-Pattern wie src/**/*.graphql oder src/**/*.tsx für Inline-Queries in Template Literals. generates ist die eigentliche Ausgabekonfiguration: für jede Zieldatei oder jedes Zielverzeichnis wird festgelegt, welche Plugins in welcher Reihenfolge laufen.

Der häufigste Anfängerfehler in der GraphQL Code Generator Config ist ein zu breites documents-Pattern, das versehentlich generierte Dateien selbst wieder einliest und dadurch bei jedem Lauf wachsende, sich selbst duplizierende Ausgaben erzeugt. Ein explizites !src/generated/** im Glob-Pattern verhindert diese Endlosschleife zuverlässig und sollte in jeder Konfiguration mit generierten Dateien im selben Verzeichnisbaum stehen.


# codegen.yml - base configuration
schema: "https://api.mironsoft.dev/graphql"
documents:
  - "src/**/*.graphql"
  - "src/**/*.tsx"
  - "!src/generated/**"
generates:
  src/generated/graphql.ts:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-react-apollo"
    config:
      withHooks: true

3. Plugins im Detail: typescript, typescript-operations, typescript-react-apollo

GraphQL Code Generator ist bewusst als Plugin-Pipeline aufgebaut, nicht als monolithisches Tool. Das Plugin typescript erzeugt die Basistypen direkt aus dem Schema, also Interfaces für jeden Typ, jedes Input und jedes Enum. Das Plugin typescript-operations baut darauf auf und erzeugt spezifische Typen für jede einzelne Query, Mutation und jedes Fragment, exakt zugeschnitten auf die tatsächlich angeforderten Felder statt auf den vollständigen Typ.

Das Plugin typescript-react-apollo setzt beide vorherigen Plugins voraus und generiert React-Hooks wie useProductQuery und useCreateProductMutation, vollständig typisiert für Variablen und Rückgabewert. Diese Plugin-Kette ist bewusst modular: Wer statt Apollo Client urql einsetzt, tauscht lediglich typescript-react-apollo gegen typescript-urql, während typescript und typescript-operations unverändert bleiben, weil sie clientunabhängig sind.

4. Named Operations vs. near-operation-file Preset

Die Standard-Konfiguration bündelt alle generierten Typen in einer einzigen, oft tausende Zeilen langen Datei. Bei mehr als fünfzig Queries wird diese Datei unübersichtlich, und jede kleine Schema-Änderung löst einen Merge-Konflikt in genau dieser einen Datei aus, selbst wenn zwei Entwickler an völlig unterschiedlichen Features arbeiten. Das near-operation-file-Preset löst dieses Problem, indem es für jede .graphql-Datei eine eigene .generated.ts-Datei direkt daneben erzeugt.

Diese Kolokation spiegelt exakt das GraphQL-Grundprinzip wider: eine Komponente definiert ihre Datenanforderung lokal, und die dazugehörigen Typen liegen ebenso lokal statt in einer zentralen, für alle Teams gemeinsam verwalteten Datei. Merge-Konflikte in generierten Dateien werden dadurch fast vollständig eliminiert, weil verschiedene Features fast nie dieselbe .generated.ts-Datei berühren.


# codegen.yml - near-operation-file preset for colocation
generates:
  src/:
    preset: "near-operation-file"
    presetConfig:
      extension: ".generated.ts"
      baseTypesPath: "generated/graphql.ts"
    plugins:
      - "typescript-operations"
      - "typescript-react-apollo"

5. Custom Scalars richtig mappen

Ohne explizite Konfiguration mappt GraphQL Code Generator jeden Custom Scalar, etwa DateTime, Decimal oder JSON, standardmäßig auf any. Das ist der schnellste Weg, TypeScripts Typsicherheit für genau die Felder zu verlieren, die in der Praxis am fehleranfälligsten sind: Datumsberechnungen und Geldbeträge. Der scalars-Block in der Konfiguration erlaubt es, jeden Custom Scalar auf einen konkreten TypeScript-Typ zu mappen.

Für DateTime ist string meist präziser als Date, weil GraphQL Scalars über die Leitung ohnehin immer als String serialisiert werden und die Konvertierung zu einem Date-Objekt explizit im Anwendungscode passieren sollte, nicht implizit im generierten Typ. Für Decimal-Felder, die Magento GraphQL etwa für Preise verwendet, ist string ebenfalls die sicherere Wahl gegenüber number, weil JavaScripts Fließkomma-Arithmetik bei Geldbeträgen zu Rundungsfehlern führen kann.


# codegen.yml - explicit scalar mapping instead of implicit "any"
config:
  scalars:
    DateTime: "string"
    Decimal: "string"
    JSON: "Record<string, unknown>"
    Upload: "File"

6. Fragment Masking für saubere Komponentengrenzen

Ohne Fragment Masking hat jede Komponente, die ein Fragment einbindet, vollen TypeScript-Zugriff auf alle Felder dieses Fragments, unabhängig davon, ob die Komponente sie tatsächlich verwendet. Das untergräbt die Idee, dass ein Fragment die private Datenanforderung einer Komponente ist. Fragment Masking, über das client-preset aktiviert, erzeugt stattdessen einen opaken FragmentType, den nur die useFragment-Hilfsfunktion desselben Fragments wieder auflösen kann.

Der praktische Effekt: Eine übergeordnete Komponente kann ein Fragment an eine Kindkomponente weiterreichen, ohne selbst auf dessen Felder zugreifen zu können. Ändert die Kindkomponente später, welche Felder sie benötigt, muss nur ihr eigenes Fragment angepasst werden, die übergeordnete Komponente bleibt unverändert und kompiliert weiterhin fehlerfrei, solange sie das Fragment nur durchreicht statt inhaltlich zu verwenden.


// ProductCard.tsx — fragment owns its own field selection
import { graphql, useFragment } from '../generated';

const ProductCardFragment = graphql(`
  fragment ProductCard on Product {
    id
    name
    price
  }
`);

export function ProductCard(props: { product: FragmentType<typeof ProductCardFragment> }) {
  // useFragment "unmasks" the opaque type, only inside this component
  const product = useFragment(ProductCardFragment, props.product);
  return <div>{product.name} — {product.price}</div>;
}

7. Hooks-Generierung konfigurieren: withHooks, withHOC, withComponent

Das Plugin typescript-react-apollo bietet drei unterschiedliche Integrationsstile, die sich über Konfigurationsflags aktivieren lassen: withHooks erzeugt Funktionen wie useProductQuery, der heute mit Abstand gängigste Stil in modernen React-Codebasen. withHOC erzeugt stattdessen Higher-Order-Components im Stil älterer Class-Component-Architekturen, und withComponent erzeugt Render-Prop-Komponenten, ein Muster, das vor React Hooks verbreitet war.

In neuen Projekten sollte ausschließlich withHooks: true aktiviert und die beiden anderen Optionen explizit deaktiviert werden, weil jede zusätzliche Ausgabevariante die Größe der generierten Datei erhöht, ohne einen Mehrwert zu bieten, solange keine Legacy-Class-Components im selben Codebase existieren, die auf HOCs angewiesen sind. Migrationsprojekte, die schrittweise von Class-Components auf Hooks umstellen, profitieren dagegen davon, für eine Übergangsphase beide Varianten gleichzeitig zu generieren.

8. Watch-Mode, Caching und Performance bei großen Schemas

Bei Schemas mit mehreren tausend Feldern wird ein vollständiger Codegen-Lauf spürbar langsam, besonders im --watch-Modus während der Entwicklung. Die overwrite: true-Option gemeinsam mit einem inkrementellen Watch-Modus, der nur geänderte Dokumente neu verarbeitet, reduziert die Latenz zwischen dem Speichern einer .graphql-Datei und dem verfügbaren generierten Code auf unter eine Sekunde in den meisten Setups.

Für CI-Pipelines lohnt sich ein separater, nicht-inkrementeller Lauf mit --check, der prüft, ob die eingecheckten generierten Dateien noch mit dem aktuellen Schema und den aktuellen Queries übereinstimmen, ohne die Dateien tatsächlich zu überschreiben. Ein Pull Request, der Schema oder Queries ändert, aber die generierten Dateien nicht aktualisiert hat, schlägt dadurch zuverlässig fehl, statt stillschweigend veraltete Typen in den main-Branch zu lassen.


{
  "scripts": {
    "codegen": "graphql-codegen --config codegen.yml",
    "codegen:watch": "graphql-codegen --config codegen.yml --watch",
    "codegen:check": "graphql-codegen --config codegen.yml --check"
  }
}

9. GraphQL Code Generator Presets im Vergleich

Die Wahl des richtigen Presets hat größeren Einfluss auf die Entwicklererfahrung als jede einzelne Plugin-Option. Die folgende Tabelle stellt die drei gängigen Ansätze gegenüber.

Ansatz Ausgabe Vorteil Nachteil
Single-File (Standard) Eine große generated.ts Einfaches Setup, ein Import-Pfad Merge-Konflikte, unübersichtlich ab ~50 Queries
near-operation-file .generated.ts neben jeder .graphql-Datei Kolokation, kaum Merge-Konflikte Mehr Dateien im Projektbaum
client-preset (Fragment Masking) Zentrales generated-Verzeichnis mit graphql()-Funktion Erzwingt saubere Komponentengrenzen Steilere Lernkurve, mehr Boilerplate pro Komponente

In der Praxis lassen sich near-operation-file und das client-preset kombinieren: Kolokation für die Dateistruktur, Fragment Masking für die Typsicherheit an Komponentengrenzen. Diese Kombination ist inzwischen die von der GraphQL Code Generator Dokumentation selbst empfohlene Standardkonfiguration für neue React-Projekte.

Mironsoft

GraphQL-Tooling, TypeScript-Codegen und React-Frontend-Architektur

Generierte Typen, die wirklich helfen statt nur zu existieren?

Wir konfigurieren GraphQL Code Generator für euer Projekt, inklusive Fragment Masking, Custom Scalars und Kolokation, und migrieren bestehende Single-File-Setups schrittweise ohne Build-Breakage.

Codegen-Setup

codegen.yml, Presets und Plugin-Auswahl für euer Frontend konfigurieren

Fragment Masking

Komponentengrenzen im Typsystem sichtbar machen und durchsetzen

CI-Absicherung

codegen --check als Pull-Request-Gate gegen veraltete generierte Typen

10. Zusammenfassung

Eine durchdachte GraphQL Code Generator Config unterscheidet sich von der Quickstart-Konfiguration in drei entscheidenden Punkten: dem near-operation-file Preset für Kolokation statt einer monolithischen Ausgabedatei, explizitem Custom-Scalar-Mapping statt stillschweigendem any, und Fragment Masking für Komponentengrenzen, die auch im Typsystem eingehalten werden. Jede dieser drei Entscheidungen wirkt sich direkt auf Wartbarkeit und Merge-Konflikt-Häufigkeit aus.

Die Plugin-Pipeline aus typescript, typescript-operations und typescript-react-apollo bleibt dabei clientunabhängig austauschbar, und --check in der CI-Pipeline stellt sicher, dass generierte Typen niemals hinter dem tatsächlichen Schema zurückbleiben. Wer diese Bausteine einmal richtig konfiguriert, gewinnt Typsicherheit, die tatsächlich zum Entwicklungsalltag passt statt ihn zu bremsen.

GraphQL Code Generator Config — Das Wichtigste auf einen Blick

Kolokation

near-operation-file Preset legt generierte Typen neben die zugehörige Query-Datei, statt in eine monolithische Datei.

Custom Scalars

DateTime, Decimal und JSON explizit im scalars-Block mappen, statt sie implizit auf any fallen zu lassen.

Fragment Masking

client-preset mit useFragment erzwingt, dass Komponenten nur ihre eigenen angeforderten Felder sehen.

CI-Absicherung

graphql-codegen --check verhindert veraltete generierte Typen im main-Branch.

11. FAQ: GraphQL Code Generator Config

1Was macht die codegen.yml?
Sie definiert Schema-Quelle, zu durchsuchende Dateien und die Plugin-Pipeline für generierten Code.
2Unterschied typescript vs. typescript-operations vs. typescript-react-apollo?
Basistypen, dann operation-spezifische Typen, dann darauf aufbauend typisierte React-Hooks.
3Was ist das near-operation-file Preset?
Erzeugt für jede .graphql-Datei eine eigene .generated.ts daneben, statt einer zentralen Sammeldatei.
4Wie mappe ich Custom Scalars wie DateTime?
Über den scalars-Block, meist auf string statt Date, um implizite Serialisierungsannahmen zu vermeiden.
5Was ist Fragment Masking?
Erzeugt opake Fragment-Typen, die nur useFragment im selben Fragment auflöst, für echte Komponentengrenzen.
6withHooks vs. withHOC vs. withComponent?
Drei Ausgabestile: Hooks für moderne Komponenten, HOCs für Class Components, Render Props als drittes Muster.
7Warum string statt Date für DateTime?
Weil Scalars über die Leitung als String serialisiert werden, Konvertierung sollte explizit im Anwendungscode passieren.
8Wie verhindere ich veraltete generierte Typen in CI?
Mit graphql-codegen --check als Pull-Request-Gate gegen Schema- oder Query-Abweichungen.
9Lassen sich near-operation-file und Fragment Masking kombinieren?
Ja, das ist die von der offiziellen Dokumentation empfohlene Standardkonfiguration für neue React-Projekte.
10Warum wird eine große Single-File-Ausgabe zum Problem?
Jede Änderung betrifft dieselbe Datei, was bei parallel arbeitenden Teams regelmäßig Merge-Konflikte erzeugt.