Fachfehler, Transportfehler und Extensions richtig trennen
GraphQL kennt kein HTTP-Äquivalent für fachliche Fehler. Wer Fachfehler als Resolver-Exceptions modelliert, Transportfehler nicht vom Netzwerkproblem unterscheidet und extensions.category ignoriert, baut ein Fehlerhandling, das das Frontend zwingt, auf Heuristiken statt auf Struktur zu reagieren.
Inhaltsverzeichnis
- 1. Die drei Fehler-Ebenen in GraphQL
- 2. Transportfehler: Netzwerk, HTTP und GraphQL-Protokollfehler
- 3. Validierungsfehler: Schema-Verletzungen vor der Ausführung
- 4. Fachfehler: Geschäftslogik-Fehler im Resolver
- 5. extensions.category: strukturierte Fehlerkategorisierung
- 6. Partial Success: Daten und Fehler gleichzeitig
- 7. Union-Typen als Fehlermodell: der bessere Weg für Fachfehler
- 8. Fehlermodelle im Vergleich
- 9. Frontend-Auswertung: Fehlertypen programmatisch unterscheiden
- 10. Zusammenfassung
- 11. FAQ
1. Die drei Fehler-Ebenen in GraphQL
GraphQL unterscheidet drei grundlegende Fehler-Ebenen, die unterschiedliche Reaktionen erfordern: Transportfehler auf HTTP-Ebene (Netzwerkausfall, Server nicht erreichbar, HTTP 500), Validierungsfehler auf Protokollebene (Query-Syntax falsch, Feld existiert nicht im Schema) und fachliche Fehler in der Resolver-Schicht (Produkt nicht mehr verfügbar, Berechtigung fehlt, Validation eines Eingabewertes schlägt fehl). Wer diese drei Ebenen durcheinander bringt, schreibt Fehlerbehandlung, die in bestimmten Szenarien keine sinnvolle Reaktion produziert.
Das Besondere an GraphQL: Alle drei Fehlertypen können in derselben HTTP-200-Antwort vorkommen. Eine Query, die teilweise erfolgreich ist, kann data-Felder mit Werten und gleichzeitig ein errors-Array mit Fehlern für einzelne Felder zurückgeben – das sogenannte Partial-Success-Szenario. Das ist ein fundamentaler Unterschied zu REST, wo eine Antwort entweder Erfolg oder Fehler signalisiert, aber nicht beides gleichzeitig.
2. Transportfehler: Netzwerk, HTTP und GraphQL-Protokollfehler
Transportfehler sind Fehler, die den GraphQL-Request nie bis zur Ausführungsebene bringen. Dazu gehören Netzwerkausfälle (keine Verbindung zum Server), HTTP-Fehler (HTTP 503 Service Unavailable, HTTP 429 Rate Limited) und schwere Serverfehler (HTTP 500, die einen PHP Fatal Error signalisieren). Diese Fehler erscheinen nicht im errors-Array einer GraphQL-Antwort, weil gar keine gültige GraphQL-Antwort zurückkommt – der HTTP-Client erhält stattdessen einen Netzwerkfehler oder einen nicht-200-Statuscode.
Die praktische Implikation: Fehlerbehandlung auf Client-Seite muss beide Schichten abdecken. Zuerst prüfen, ob überhaupt eine HTTP-Antwort angekommen ist und ob der Statuscode 200 ist. Dann prüfen, ob die JSON-Antwort ein errors-Array enthält. Clients, die nur das errors-Array prüfen, verpassen Transportfehler. Clients, die nur HTTP-Statuscodes prüfen, verpassen GraphQL-Fachfehler. Beide Schichten müssen explizit behandelt werden.
# HTTP-level errors — NOT in GraphQL errors array:
# HTTP 503: Server unavailable (Magento maintenance mode, Docker restart)
# HTTP 429: Rate limiting (too many requests from one IP)
# Network error: no response at all
# GraphQL-level validation error — in errors array, data is null:
{
"errors": [
{
"message": "Cannot query field \"nonexistent\" on type \"Customer\".",
"locations": [{ "line": 3, "column": 5 }],
"extensions": { "category": "graphql" }
}
]
}
# Note: validation errors occur BEFORE resolver execution
# The resolver never runs for an invalid query
# data will be null for validation errors
3. Validierungsfehler: Schema-Verletzungen vor der Ausführung
Validierungsfehler entstehen, wenn eine Query das Schema verletzt: Ein Feld existiert nicht, ein Argument hat den falschen Typ, ein non-nullable Feld fehlt im Input-Typ. Diese Fehler werden von der GraphQL-Engine erkannt, bevor ein einziger Resolver aufgerufen wird. Das Ergebnis ist eine Antwort mit data: null und einem oder mehreren Einträgen im errors-Array mit extensions.category: "graphql".
Für die Praxis bedeutet das: Validierungsfehler sind immer Programmierfehler auf Client-Seite – der Client sendet eine Query, die dem Schema nicht entspricht. In Produktionssystemen sollten Validierungsfehler nicht vorkommen, wenn Code Generation und CI-Checks korrekt aufgesetzt sind. In der Entwicklung sind sie hingegen wertvolles Feedback: Sie zeigen genau, welche Felder oder Typen im Schema nicht existieren, ohne dass ein Resolver auch nur gestartet wird.
4. Fachfehler: Geschäftslogik-Fehler im Resolver
Fachfehler entstehen während der Resolver-Ausführung und repräsentieren Zustände der Geschäftslogik: Produkt ist ausverkauft, Promo-Code ist bereits verwendet, Mindestbestellwert nicht erreicht. In Magento werden diese Fehler als typisierte Exceptions geworfen, die das GraphQL-Framework in strukturierte Fehlereinträge im errors-Array überführt. Anders als Transportfehler können Fachfehler mit partiell gültigen Daten koexistieren.
Das grundlegende Design-Problem: GraphQL-Fachfehler als Exceptions zu modellieren, macht sie schwer typisiert. Das Frontend weiß nicht, welche Fachfehler für eine Mutation möglich sind, bis es den API-Code liest oder testet. Die Typen im Schema beschreiben Erfolgsszenarien, nicht Fehlerzustände. Der modernere Ansatz – Union-Typen als Fehlermodell – löst dieses Problem, erfordert aber Schema-Anpassungen und ist in Magento nicht standardmäßig implementiert.
5. extensions.category: strukturierte Fehlerkategorisierung
Das extensions-Feld in GraphQL-Fehlern ist ein offenes Objekt, das beliebige maschinenlesbare Zusatzinformationen enthalten kann. Magento nutzt dieses Feld mit einer category-Property, die die Art des Fehlers klassifiziert. Die wichtigsten Kategorien: graphql für Schema-Validierungsfehler, graphql-authorization für fehlende Berechtigungen, graphql-authentication für fehlende Identität, graphql-input für ungültige Eingabewerte und graphql-no-such-entity für nicht gefundene Ressourcen.
Diese Kategorisierung ist der Schlüssel für sinnvolles Error-Handling im Frontend. Anstatt auf Fehlermeldungs-Texte zu reagieren (die sich ändern können und lokalisiert sind), reagiert das Frontend auf die maschinenlesbare Kategorie. graphql-authorization führt zur Login-Weiterleitung, graphql-no-such-entity zeigt eine 404-ähnliche Meldung, graphql-input markiert das entsprechende Formularfeld als fehlerhaft. Das macht die Fehlerbehandlung stabil gegenüber Textänderungen und Lokalisierung.
# Magento GraphQL error response — structured with extensions.category
# Authentication error (no token):
{
"errors": [{
"message": "The current customer isn't authorized.",
"extensions": { "category": "graphql-authentication" }
}],
"data": { "customer": null }
}
# Not found error:
{
"errors": [{
"message": "No such entity with id = 99999",
"extensions": { "category": "graphql-no-such-entity" }
}],
"data": { "product": null }
}
# Input validation error:
{
"errors": [{
"message": "Please enter a valid email address (Ex: johndoe@domain.com).",
"extensions": { "category": "graphql-input" }
}],
"data": { "createCustomer": null }
}
# Frontend error handling based on category (not message text):
# switch (error.extensions?.category) {
# case 'graphql-authentication': redirectToLogin(); break;
# case 'graphql-no-such-entity': show404(); break;
# case 'graphql-input': markFieldInvalid(error.message); break;
# }
6. Partial Success: Daten und Fehler gleichzeitig
Partial Success ist eines der unintuitiven, aber mächtigen Features von GraphQL: Eine Anfrage kann teilweise erfolgreich sein – einige Felder werden aufgelöst, andere scheitern. Das Ergebnis ist eine Antwort, die sowohl ein befülltes data-Objekt als auch ein nicht-leeres errors-Array enthält. Das Frontend muss beide parallel verarbeiten und entscheiden, welche Teilantwort angezeigt werden kann und welche Fehlermeldungen dem Nutzer präsentiert werden.
Ein konkretes Szenario in Magento: Eine Query lädt gleichzeitig Produktdaten und Kundendaten. Der Produktteil gelingt, aber der Kunden-Resolver schlägt fehl, weil der Token abgelaufen ist. Partial Success bedeutet: data.products enthält valide Produktdaten, data.customer ist null, errors enthält einen Eintrag mit category: graphql-authentication. Das Frontend kann die Produktseite anzeigen und parallel die Auth-Fehlermeldung rendern – anstatt die gesamte Seite als Fehler zu behandeln.
7. Union-Typen als Fehlermodell: der bessere Weg für Fachfehler
Das GraphQL-Community-Pattern für typisierte Fachfehler sind Union-Return-Typen in Mutations. Statt einer Mutation, die bei Fehlern eine Exception wirft, gibt die Mutation einen Union-Typ zurück, der entweder den Erfolgstyp oder einen dedizierten Fehlertyp enthält. Das macht Fehlerzustände zu einem expliziten Teil des Schemas und ermöglicht dem Frontend, typsichere Fehlerbehandlung zu implementieren.
In der Praxis sieht das so aus: Eine addToCart-Mutation gibt AddToCartResult zurück, der ein Union aus CartItems und möglichen Fehlertypen wie OutOfStockError und MinimumOrderError ist. Das Frontend kann mit __typename prüfen, welchen Typ die Antwort hat, und entsprechend reagieren. Dieses Muster ist in Magento nicht eingebaut, kann aber in eigenen Modulen und Mutationen implementiert werden und ist für neue Module die empfehlenswerte Variante.
8. Fehlermodelle im Vergleich
Die Wahl des Fehlermodells beeinflusst, wie typsicher und wartbar das Error-Handling im Frontend ist. Beide Ansätze haben Berechtigung – die Wahl hängt vom Schema-Kontext ab.
| Fehlermodell | Typsicherheit | Schema-Dokumentation | Magento-Support |
|---|---|---|---|
| exceptions.category | String-basiert, schwach | Nicht im Schema sichtbar | Eingebaut, Standard |
| Union-Return-Typen | Stark, __typename-basiert | Vollständig im Schema dokumentiert | Nur in eigenen Modulen |
| Generische Fehler | Keine | Keine | Immer möglich |
| Nullable Felder | Implizit (null = Fehler) | Schwach (null = Fehler oder leer) | Immer verfügbar |
| Error-als-Data-Pattern | Stark, vollständig typisiert | Explizit im Schema | Nur in eigenen Modulen |
Für bestehenden Magento-Code ist extensions.category der pragmatische Standard, der stabil und gut dokumentiert ist. Für neue Magento-Module mit komplexer Geschäftslogik lohnt sich der Aufwand für Union-Return-Typen, weil sie das Frontend-Fehlerhandling erheblich robuster machen.
9. Frontend-Auswertung: Fehlertypen programmatisch unterscheiden
Ein sauberes Frontend-Fehlerhandling für GraphQL besteht aus drei Schichten. Schicht eins: HTTP-Transport prüfen – kein Response oder nicht-200-Statuscode sind Netzwerk- oder Server-Fehler, die unabhängig von GraphQL-Fehlerstrukturen behandelt werden müssen. Schicht zwei: errors-Array prüfen und nach extensions.category kategorisieren. Schicht drei: Bei Partial-Success-Szenarien entscheiden, welche Teile der Antwort trotz Fehler nutzbar sind.
Ein häufiger Fehler im Frontend ist, alle GraphQL-Fehler als gleichwertig zu behandeln und pauschal eine generische Fehlermeldung anzuzeigen. Das ist für Nutzer nicht hilfreich: Ein abgelaufener Session-Token erfordert eine Login-Weiterleitung, ein ausverkauftes Produkt erfordert eine spezifische Produktmeldung, ein Netzwerkfehler erfordert einen Retry-Hinweis. Die Fehlerstruktur von GraphQL – und insbesondere extensions.category – ist dafür gemacht, diese Unterscheidungen programmatisch zu ermöglichen. Wer sie nicht nutzt, muss auf Fehlermeldungs-Texte matchen, was bei mehrsprachigen Shops sofort bricht.
# Partial success example: products loaded, customer failed
# Single query, mixed result — both data and errors present
query DashboardData {
products(search: "laptop", pageSize: 3) {
items { sku name }
}
customer {
firstname
orders { items { order_number } }
}
}
# Response when customer token is expired:
# {
# "data": {
# "products": { "items": [{ "sku": "...", "name": "..." }] },
# "customer": null
# },
# "errors": [{
# "message": "The current customer isn't authorized.",
# "path": ["customer"],
# "extensions": { "category": "graphql-authentication" }
# }]
# }
# Frontend logic:
# 1. data.products is valid — render product section
# 2. errors[0].path = ["customer"] — only customer section failed
# 3. errors[0].extensions.category = "graphql-authentication" — trigger re-auth
# 4. Do NOT discard entire page because of partial failure
10. Zusammenfassung
GraphQL-Fehlerhandling erfordert das Denken in drei Ebenen: Transportfehler (kein HTTP-200), Validierungsfehler (Schema-Verletzung vor Ausführung) und Fachfehler (Resolver-Logik). Das extensions.category-Feld von Magento macht Fachfehler maschinenlesbar und ermöglicht dem Frontend, ohne Text-Matching auf Fehlerkategorien zu reagieren. Partial Success – die gleichzeitige Rückgabe von Daten und Fehlern – ist eine Stärke von GraphQL, erfordert aber explizites Partial-Success-Handling im Frontend.
Für neue Module ist das Union-Return-Typen-Muster die bessere Wahl, weil es Fehlerzustände in das Schema selbst integriert und vollständig typisiert. Für bestehenden Magento-Code ist der extensions.category-Ansatz der pragmatische Standard. In beiden Fällen gilt: Generische Fehlerbehandlung, die alle Fehler gleich behandelt, ist kein ausreichendes Fehlerhandling für produktive GraphQL-APIs.
GraphQL Error Handling — Das Wichtigste auf einen Blick
Drei Fehler-Ebenen
Transportfehler (HTTP), Validierungsfehler (Schema) und Fachfehler (Resolver) sind unterschiedliche Ebenen mit unterschiedlichen Reaktionen. Alle drei müssen explizit behandelt werden.
extensions.category
Magento-Standard für maschinenlesbare Fehlerkategorien. Frontend reagiert auf Kategorie, nicht auf Fehlermeldungstext – stabil bei Übersetzungen und Textänderungen.
Partial Success
Daten und Fehler können gleichzeitig in einer GraphQL-Antwort vorkommen. errors[].path zeigt, welcher Teil der Query fehlschlug. Nicht die gesamte Seite verwerfen.
Union-Typen für Neuentwicklung
Für neue Module Union-Return-Typen für Mutations verwenden – vollständig typisierte Fehlerzustände im Schema, ohne exceptions.category-Konventionen.