{ }
type
GraphQL · Code Generation · TypeScript · PHP · Magento
GraphQL Code Generation
für TypeScript und PHP richtig nutzen

Wer GraphQL-Queries manuell mit Typen synchron hält, baut technische Schulden auf – jeden Mal, wenn sich ein Schema-Feld ändert. Code-Generation aus Schema und Operations-Dateien macht Typen zur Automatisierung statt zur Wartungsaufgabe.

14 Min. Lesezeit graphql-codegen · TypeScript-Hooks · PHP-Interfaces · CI-Pipeline GraphQL · TypeScript · PHP 8.4 · Magento

1. Warum manuelle Typen bei GraphQL ein Anti-Pattern sind

GraphQL liefert ein typisiertes Schema: Jedes Feld, jeder Typ und jede Operation ist eindeutig definiert. Trotzdem ist es weit verbreitet, TypeScript-Interfaces für GraphQL-Responses manuell zu pflegen – parallel zum Schema, das sich unabhängig davon weiterentwickelt. Das führt zu einem systematischen Desynchronisierungsproblem: Sobald ein Schema-Feld umbenannt oder ein Typ erweitert wird, müssen alle manuell gepflegten Interfaces manuell nachgezogen werden. In einem Team mit mehreren Entwicklern und einem aktiv entwickelten Schema ist das kein Einzelfall, sondern Dauerbetrieb.

Das Problem ist nicht auf TypeScript beschränkt. Auch PHP-Code, der GraphQL-Responses verarbeitet – sei es in einem Headless-Frontend-BFF, einem CLI-Tool oder einem API-Integration-Layer – arbeitet ohne Code-Generation mit impliziten Array-Strukturen, bei denen ein Tippfehler im Feldnamen erst zur Laufzeit auffällt. Code-Generation aus dem Schema macht Typsicherheit zur Norm statt zur manuellen Disziplin.

2. graphql-codegen: Grundprinzip und Workflow

Das Tool @graphql-codegen/cli liest das GraphQL-Schema (als SDL-Datei, URL oder Introspection-Result) und die Operations-Dateien (Queries, Mutations, Fragments) und erzeugt daraus Code nach konfigurierbaren Plugins. Der Workflow ist einfach: Schema und Operations liegen im Repository, graphql-codegen wird als Build-Step ausgeführt und erzeugt eine oder mehrere Output-Dateien. Der generierte Code wird entweder eingecheckt oder ausschließlich in CI als Verifikationsschritt genutzt.

Die Konfiguration erfolgt in einer codegen.ts-Datei im Projekt-Root. Sie definiert, wo das Schema liegt, welche Documents (Operations-Dateien) eingelesen werden und welche Plugins mit welchen Einstellungen für welche Output-Dateien ausgeführt werden. Die Stärke des Tools liegt in der Plugin-Architektur: Für unterschiedliche Frameworks – React, Vue, urql, react-query – gibt es spezialisierte Plugins, die nicht nur TypeScript-Typen, sondern komplett typisierte Hooks erzeugen.


# Operations file: src/graphql/customer.graphql
# graphql-codegen reads this alongside the schema to generate typed hooks

query GetCustomer {
  customer {
    firstname
    lastname
    email
    addresses {
      id
      street
      city
      postcode
      country_code
    }
  }
}

mutation UpdateCustomerEmail($email: String!, $password: String!) {
  updateCustomerEmail(email: $email, password: $password) {
    customer {
      email
    }
  }
}

# Fragment reuse — codegen generates fragment types automatically
fragment CustomerBasic on Customer {
  firstname
  lastname
  email
}

3. TypeScript-Plugins: typed-document-node, react-query und urql

Das Plugin @graphql-codegen/typed-document-node erzeugt typisierte DocumentNode-Objekte, die direkt an Apollo Client, urql oder fetch-basierte Clients übergeben werden können. Das Ergebnis: Der Compiler kennt den exakten Typ des Response-Objekts, inklusive aller verschachtelten Felder und optionalen Nullable-Typen. Tippfehler bei Feldnamen werden sofort zur Compile-Zeit erkannt, nicht erst zur Laufzeit in der Produktion.

Für react-query erzeugt das Plugin @graphql-codegen/typescript-react-query fertige, typisierte useQuery- und useMutation-Hooks, die direkt im Component-Code verwendet werden können. Der Developer importiert den generierten Hook, übergibt die Variablen und erhält ein vollständig typisiertes Response-Objekt – ohne eine Zeile TypeScript-Interface selbst schreiben zu müssen. Das Plugin für urql funktioniert analog mit useQuery- und useMutation-Komposables, die dem urql-API entsprechen.

4. PHP-Code-Generation: Interfaces und Value Objects aus dem Schema

Für PHP existieren weniger etablierte Codegen-Tools als für TypeScript, aber der Ansatz ist derselbe. Ein PHP-GraphQL-Client, der Responses verarbeitet, profitiert von generierten Value-Objects, die das Schema widerspiegeln. Tools wie spawnia/sailor oder softonic/graphql-client mit eigenem Codegen-Layer erzeugen PHP-Klassen aus SDL-Definitionen, die Responses direkt deserialisieren und als typisierte Objekte zurückgeben. Das eliminiert die typische $response['data']['customer']['email']-Kette zugunsten von $response->customer->email mit IDE-Autocomplete.

In Magento selbst wird Code-Generation auf der Server-Seite nicht eingesetzt – die Resolver-Architektur von Magento ist nicht von einem externen Schema abhängig, das generiert werden müsste. Aber in einem Headless-Setup, wo ein PHP-BFF das Magento-GraphQL-Schema konsumiert, kann PHP-Codegen erheblichen Mehrwert liefern. Das Magento-Schema wird per Introspection abgerufen, als SDL-Datei gespeichert und dann als Input für den PHP-Codegen-Prozess genutzt.


# codegen.ts — configuration for TypeScript + PHP generation

# TypeScript output: typed document nodes + react-query hooks
# generates: src/generated/graphql.ts
#
# documents: 'src/graphql/**/*.graphql'
# schema: https://shop.example.com/graphql
# plugins:
#   - typescript
#   - typescript-operations
#   - typed-document-node
#   - typescript-react-query

# Usage in React component (generated hook):
# import { useGetCustomerQuery } from '../generated/graphql'
#
# const { data, isLoading } = useGetCustomerQuery()
# data?.customer?.email  // fully typed, nullable-aware

# PHP output (sailor pattern): generates PHP value objects
# CustomerResponse::class with ->customer->email typed as string|null

5. Konfiguration: codegen.ts sauber strukturieren

Eine sinnvolle codegen.ts-Konfiguration trennt Schema-Typen von Operations-Typen und ermöglicht mehrere Output-Dateien für unterschiedliche Schichten. Der häufigste Fehler: alle Plugins in eine einzige Output-Datei zu schreiben, die dann als monolithisches File hunderte von generierten Typen enthält. Besser ist eine Aufteilung in eine Basis-Typen-Datei (aus dem Schema, ohne Operations) und Operations-spezifische Dateien, die Fragment-Typen und Hooks für einzelne Domänen enthalten.

Skalare müssen in der Konfiguration explizit gemappt werden. Das Magento-Schema enthält Custom Scalars wie String für Preise (die intern als Float behandelt werden) und Boolean. Ohne explizites Scalar-Mapping erzeugt codegen für unbekannte Skalare den Typ any – was den Typ-Sicherheitsgewinn zunichte macht. Ein guter Ansatz: alle Custom Scalars auf konkrete TypeScript-Typen oder Branded Types mappen, die versehentliche Verwechslungen zwischen ähnlichen Feldern verhindern.

6. Magento-Schema als Codegen-Quelle nutzen

Das Magento-GraphQL-Schema kann über den Introspection-Endpunkt als SDL-Datei exportiert werden und als Schema-Quelle für graphql-codegen dienen. Das ist sinnvoll für Headless-Setups, wo das Frontend direkt gegen das Magento-Schema entwickelt. Der Workflow: Schema per Introspection in CI abrufen, als schema.graphql speichern und in codegen als Quelle angeben. So ist das generierte Typsystem immer synchron mit dem tatsächlichen Magento-Schema.

Ein praktisches Problem dabei: Das Magento-Schema ist sehr groß und enthält hunderte von Typen, die ein durchschnittliches Frontend-Projekt nicht benötigt. Die graphql-codegen-Option onlyOperationTypes: true begrenzt die generierten Typen auf die Typen, die tatsächlich in den Operations verwendet werden – das reduziert die Output-Größe erheblich und macht die generierte Datei wartbarer. Für Magento-Projekte mit vielen Modulen lohnt es sich außerdem, das Schema lokal zu cachen statt bei jedem codegen-Aufruf neu per Introspection abzurufen.

7. CI-Integration: Generated Code prüfen ohne ihn einzuchecken

Ob generierter Code ins Repository eingecheckt werden soll, ist eine Teamentscheidung. Für eingecheckten Code gilt: Der npm run codegen-Befehl muss in CI ausgeführt werden, und ein git diff --exit-code danach stellt sicher, dass der eingecheckte Code mit dem generierten Code übereinstimmt. Weicht der eingecheckte Code ab, schlägt der CI-Step fehl und signalisiert, dass Operations geändert wurden ohne die Typen zu regenerieren.

Alternativ kann generierter Code komplett aus dem Repository ausgeschlossen und nur in CI erzeugt werden. Das vermeidet Merge-Konflikte in generierten Dateien, erfordert aber einen Build-Step vor der Entwicklung. In der Praxis ist der erste Ansatz (eingecheckt + CI-Check) für Teams einfacher, weil IDE-Autocomplete sofort funktioniert und keine lokale Build-Infrastruktur für neue Team-Mitglieder erforderlich ist. Wichtig in beiden Fällen: graphql-codegen mit dem Flag --check in CI ausführen, das den Prozess mit Exit-Code 1 abbricht, wenn Output-Dateien sich ändern würden.


# CI pipeline step — verify generated types are up to date
# Run codegen in check mode: fails if output would change

# package.json scripts:
# "codegen": "graphql-codegen --config codegen.ts"
# "codegen:check": "graphql-codegen --config codegen.ts --check"

# CI YAML (GitHub Actions pattern):
# - name: Check GraphQL types are up to date
#   run: npm run codegen:check
#   # Fails with exit code 1 if schema or operations changed
#   # without regenerating the typed files

# Schema introspection for Magento:
query IntrospectSchema {
  __schema {
    types {
      name
      kind
      fields {
        name
        type { name kind }
      }
    }
  }
}

# Export schema to SDL file (alternative to introspection at runtime):
# npx get-graphql-schema https://shop.example.com/graphql > schema.graphql

8. Codegen-Ansätze im Vergleich

Unterschiedliche Setups erfordern unterschiedliche Codegen-Strategien. Die Wahl des richtigen Ansatzes hängt von Framework, Team-Größe und Schema-Komplexität ab.

Ansatz Framework Output Besonderheit
typed-document-node Apollo, urql, fetch DocumentNode + Typen Framework-agnostisch, minimal
typescript-react-query React + react-query Typisierte useQuery-Hooks Keine manuelle Hook-Schicht nötig
typescript-urql Vue, React + urql Typisierte Composables Gut für Vue-Setups
spawnia/sailor (PHP) PHP BFF/CLI PHP Value Objects Typsichere PHP-Clients
Manuelle Typen Beliebig Handgeschriebene Interfaces Desynchronisiert bei Schema-Änderungen

In Magento-Headless-Projekten mit React-Frontend ist typed-document-node kombiniert mit einem fetch-basierten Client oft die wartbarste Wahl, weil sie keine Apollo-Abhängigkeit erfordert. Für Teams, die bereits react-query nutzen, liefert das react-query-Plugin den höchsten Produktivitätsvorteil durch komplett fertige Hooks. PHP-Codegen ist sinnvoll, sobald ein PHP-Layer das Magento-GraphQL-Schema konsumiert.

9. Typische Fehler bei der Code-Generation

Der häufigste Fehler ist das Ignorieren von Nullable-Typen im generierten Code. GraphQL-Typen sind standardmäßig nullable, was im generierten TypeScript zu vielen string | null | undefined-Typen führt. Teams, die das als störend empfinden und die Typen in manuelle Interfaces überführen, verlieren den Wartbarkeitsvorteil der Codegen. Die richtige Reaktion: den optionalen Chaining-Operator (?.) konsequent nutzen und Null-Checks explizit modellieren, statt die Nullability durch Typ-Casts wegzudenken.

Ein zweiter verbreiteter Fehler ist, Fragments nicht zu nutzen. Ohne Fragments erzeugt codegen für identische Feldmengen in verschiedenen Queries separate Typen ohne Wiederverwendung. Mit Fragments wird ein CustomerBasicFragment-Typ einmal generiert und in allen Queries, die dieses Fragment verwenden, als gemeinsamer Basis-Typ eingesetzt. Das macht den generierten Code konsistenter und macht refactoring einfacher, weil eine Fragment-Änderung automatisch alle abgeleiteten Typen aktualisiert.


# WRONG — duplicate field sets without fragment, generates separate types
query GetCustomerForHeader {
  customer { firstname lastname email }
}
query GetCustomerForProfile {
  customer { firstname lastname email addresses { city } }
}

# RIGHT — shared fragment, codegen generates reusable CustomerBasicFragment type
fragment CustomerBasic on Customer {
  firstname
  lastname
  email
}

query GetCustomerForHeader {
  customer { ...CustomerBasic }
}

query GetCustomerForProfile {
  customer {
    ...CustomerBasic
    addresses { city postcode }
  }
}

# Generated TypeScript (simplified):
# type CustomerBasicFragment = { firstname: string; lastname: string; email: string }
# type GetCustomerForProfileQuery = { customer: CustomerBasicFragment & { addresses: ... } }

10. Zusammenfassung

GraphQL Code Generation löst ein systematisches Problem: Manuelle Typen sind immer eine Schritt hinter dem Schema. graphql-codegen macht aus diesem manuellen Prozess einen automatisierten Build-Step, der typsichere TypeScript-Hooks und PHP-Value-Objects direkt aus Schema und Operations erzeugt. Der Aufwand für die initiale Konfiguration amortisiert sich schon nach den ersten Schema-Änderungen, weil kein Entwickler mehr manuell prüfen muss, ob Typen noch aktuell sind.

Für Magento-Headless-Projekte ist das Magento-Schema als codegen-Quelle besonders wertvoll, weil das Schema groß und häufig durch Modul-Updates erweitert wird. CI-Integration mit --check-Flag stellt sicher, dass Teams nicht versehentlich veraltete Typen produktiv bringen. Die Kombination aus typisierte-document-node-Outputs und konsequentem Fragment-Einsatz ergibt den kompaktesten und wartbarsten generierten Code.

GraphQL Code Generation — Das Wichtigste auf einen Blick

graphql-codegen

Liest Schema + Operations, erzeugt typisierte TypeScript-Hooks und PHP-Value-Objects. Plugin-Architektur für framework-spezifische Outputs.

CI-Integration

graphql-codegen --check als CI-Step – schlägt fehl, wenn Schema oder Operations geändert wurden ohne Typen zu regenerieren.

Fragments nutzen

Fragments erzeugen wiederverwendbare Basis-Typen statt duplizierter Interfaces. Eine Fragment-Änderung aktualisiert automatisch alle abhängigen Typen.

Nullable-Typen

GraphQL-Nullability in TypeScript nicht wegcasten. Optional Chaining (?.) konsequent nutzen, Null-Checks explizit modellieren.

11. FAQ: GraphQL Code Generation für TypeScript und PHP

1Muss generierter Code ins Repository eingecheckt werden?
Nein, das ist eine Teamentscheidung. Eingecheckt ermöglicht sofortiges IDE-Autocomplete, nicht eingecheckt vermeidet Merge-Konflikte. CI muss in beiden Fällen prüfen, ob der Code aktuell ist.
2Was ist typed-document-node und wann verwenden?
Typisierte DocumentNode-Objekte für Apollo, urql oder fetch-Clients. Framework-agnostischste Option, ohne Binding an spezifisches State-Management.
3Wie bekomme ich das Magento-Schema für codegen?
npx get-graphql-schema https://shop.example.com/graphql > schema.graphql, oder direkt die URL in codegen.ts angeben.
4Was passiert wenn sich das Schema ändert und codegen nicht läuft?
Code kompiliert, aber umbenannte oder entfernte Felder erzeugen Laufzeitfehler. CI-Check mit --check verhindert das.
5Kann codegen Custom Scalars aus Magento verarbeiten?
Ja, mit explizitem Scalar-Mapping. Ohne Mapping werden unbekannte Skalare als any generiert.
6Vorteile von PHP-Codegen gegenüber manuellen Arrays?
IDE-Autocomplete, Tippfehler als Compile-Fehler, strukturierte Dokumentation der Datenform. Weniger Wartungsaufwand bei Schema-Änderungen.
7Wie verkleinert man den Output bei großen Schemas?
onlyOperationTypes: true in der codegen-Konfiguration – erzeugt nur Typen für tatsächlich verwendete Operations.
8Kann codegen mehrere Schema-Quellen kombinieren?
Ja. Mehrere Schema-Quellen werden zusammengemergt. Nützlich für Federation-Setups oder lokale Schema-Erweiterungen.
9Unterschied typescript vs. typescript-operations Plugin?
typescript erzeugt Typen aus dem Schema (alle Typen, Enums). typescript-operations erzeugt Typen für die Operations-Dateien (Query-Results, Variablen). Beide zusammen für vollständige Typsicherheit.
10Sollte man Fragments immer verwenden?
Besonders sinnvoll bei wiederkehrenden Feldmengen – erzeugt gemeinsame Basis-Typen statt duplizierter Interfaces und vereinfacht Refactoring.