GraphQL Fehlerbehandlung in Magento 2: Best Practices
AI generated
M2
di.xml
Magento 2 · GraphQL · API-Entwicklung
GraphQL Fehlerbehandlung in Magento 2: Best Practices
strukturierte Errors statt "Internal server error"

Eine durchdachte GraphQL Fehlerbehandlung entscheidet darüber, ob ein Frontend gezielt auf einzelne Probleme reagieren kann oder bei jedem Fehler nur eine generische Meldung anzeigt. Wer eigene Exception-Klassen, Error-Extensions und Partial Responses konsequent nutzt, gibt Frontend-Teams die Informationen, die sie für gute Fehlermeldungen tatsächlich brauchen.

17 Min. Lesezeit GraphQlInputException · Error-Extensions · Partial Response · Monitoring Magento 2.4.x · GraphQL

1. Warum GraphQL Fehlerbehandlung anders funktioniert als REST

Bei einer REST-API signalisiert der HTTP-Status-Code den Erfolg oder Misserfolg eines Requests, ein 404 oder 500 macht sofort klar, dass etwas schiefgelaufen ist. GraphQL funktioniert grundlegend anders: nahezu jede Antwort kommt mit HTTP-Status 200 zurück, unabhängig davon, ob die Query vollständig, teilweise oder gar nicht erfolgreich war. Die eigentliche GraphQL Fehlerbehandlung passiert im Response-Body selbst, über ein separates errors-Array neben dem data-Feld.

Diese Eigenheit überrascht Entwickler, die aus der REST-Welt kommen, regelmäßig. Ein Frontend, das nur den HTTP-Status prüft, übersieht Fehler komplett, weil auch eine Antwort mit gescheiterten Teilanfragen technisch als Erfolg (Status 200) zurückkommt. Saubere GraphQL Fehlerbehandlung in Magento bedeutet deshalb, das errors-Array aktiv auszuwerten und Resolver so zu schreiben, dass sie aussagekräftige, strukturierte Fehlerobjekte statt generischer Meldungen produzieren.

Ein zweiter Unterschied betrifft die Granularität: eine einzelne GraphQL-Query kann mehrere Felder gleichzeitig anfragen, von denen einige erfolgreich und andere fehlerhaft sein können. Diese Möglichkeit für Partial Responses ist einer der mächtigsten Aspekte von GraphQL, macht die Fehlerbehandlung aber komplexer als das binäre Erfolg-oder-Fehler-Modell von REST.

2. Aufbau eines GraphQL Error-Objekts in Magento

Jeder Fehler im errors-Array einer Magento-GraphQL-Antwort folgt der GraphQL-Spezifikation: ein Pflichtfeld message mit menschenlesbarem Text, ein optionales locations-Array mit Zeile und Spalte in der Query, ein path-Array, das den genauen Feldpfad im Query-Baum angibt, und ein extensions-Objekt für zusätzliche, strukturierte Metadaten. Magento nutzt extensions.category, um Fehler in Kategorien wie graphql-input, graphql-authorization oder graphql-no-such-entity einzuordnen.


{
  "errors": [
    {
      "message": "Rating must be between 1 and 5.",
      "locations": [{ "line": 3, "column": 5 }],
      "path": ["createProductReview"],
      "extensions": {
        "category": "graphql-input"
      }
    }
  ],
  "data": {
    "createProductReview": null
  }
}

Diese Struktur ist die Grundlage jeder durchdachten GraphQL Fehlerbehandlung: das Frontend kann anhand von extensions.category unterscheiden, ob ein Nutzer eine falsche Eingabe gemacht hat, ob eine Berechtigung fehlt, oder ob eine referenzierte Entität nicht existiert, und je nach Kategorie unterschiedliche UI-Reaktionen zeigen. Ohne diese Kategorisierung landet jeder Fehler in derselben generischen Fehlermeldung, unabhängig von der tatsächlichen Ursache.

3. Die eingebauten GraphQL-Exception-Klassen

Magento stellt für die häufigsten Fehlerfälle bereits passende Exception-Klassen bereit, die automatisch die richtige extensions.category setzen. GraphQlInputException deckt ungültige Client-Eingaben ab und wird zu graphql-input. GraphQlAuthorizationException signalisiert fehlende Berechtigungen und wird zu graphql-authorization. GraphQlNoSuchEntityException wird geworfen, wenn eine referenzierte Entität, etwa ein Produkt oder eine Bestellung, nicht gefunden wird, und ergibt graphql-no-such-entity.

Der entscheidende Vorteil dieser eingebauten Klassen liegt darin, dass sie konsistent über den gesamten Magento-Kern hinweg verwendet werden. Ein Frontend, das die Kategorien einmal für die native createCustomer-Mutation auswertet, kann dieselbe Logik für eine eigene Mutation wiederverwenden, solange auch dort dieselben Exception-Klassen zum Einsatz kommen. Wer stattdessen generische \Exception-Objekte wirft, verliert diese Konsistenz und zwingt das Frontend zu Fehlertext-Parsing, einer der fragilsten Formen von Fehlerbehandlung überhaupt.

4. Eigene Exception-Klassen und Error-Kategorien

Für fachlich spezifische Fehlerfälle, die über die eingebauten Kategorien hinausgehen, lohnt sich eine eigene Exception-Klasse mit eigener extensions.category. Das Interface Magento\Framework\GraphQl\Exception\GraphQlExceptionInterface definiert die Methode getExtensions(), über die zusätzliche strukturierte Daten in die Fehlerantwort eingebracht werden können, etwa ein maschinenlesbarer Fehlercode oder betroffene Feldnamen.

Eine gute eigene Exception-Klasse für GraphQL bleibt spezifisch genug, um im Frontend gezielt behandelt zu werden, aber generisch genug, um über mehrere ähnliche Fehlerfälle hinweg wiederverwendbar zu sein. Für ein Treuepunkte-Modul könnte das eine InsufficientLoyaltyPointsException sein, die neben der Nachricht auch die aktuell verfügbare Punktzahl als Extension mitliefert, sodass das Frontend ohne zusätzliche Query direkt die richtige Zahl anzeigen kann.


<?php
declare(strict_types=1);

namespace Mironsoft\LoyaltyPoints\Model\Exception;

use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Exception\GraphQlExceptionInterface;
use Magento\Framework\Phrase;

/**
 * Thrown when a customer does not have enough loyalty points for redemption.
 */
final class InsufficientLoyaltyPointsException extends LocalizedException implements GraphQlExceptionInterface
{
    /**
     * @param Phrase $phrase Human-readable error message
     * @param int $availablePoints Points currently available to the customer
     */
    public function __construct(
        Phrase $phrase,
        private readonly int $availablePoints
    ) {
        parent::__construct($phrase);
    }

    /**
     * Return structured extension data for the GraphQL error response.
     *
     * @return array<string, mixed>
     */
    public function getExtensions(): array
    {
        return [
            'category' => 'loyalty-insufficient-points',
            'available_points' => $this->availablePoints,
        ];
    }
}

5. Partial Responses: Daten und Fehler gleichzeitig

Eine der stärksten Eigenschaften von GraphQL ist die Fähigkeit, Teilerfolge zurückzugeben: eine Query mit mehreren Feldern kann für einige Felder Daten liefern und für andere einen Fehler, in derselben Antwort. Ein Beispiel: eine Produktseiten-Query fragt gleichzeitig Basisdaten, Bewertungen und einen individuellen Rabatt aus einem externen System an. Fällt der externe Rabatt-Service aus, sollten Basisdaten und Bewertungen trotzdem in data ankommen, während nur das Rabattfeld null ist und im errors-Array einen entsprechenden Eintrag hat.

Diese Partial-Response-Fähigkeit setzt voraus, dass Resolver Fehler isoliert pro Feld behandeln, statt eine Exception aus einem einzelnen Resolver die gesamte Query zum Absturz zu bringen. In der Praxis bedeutet das: Abhängigkeiten zu instabilen externen Diensten sollten in eigenen, klar abgegrenzten Resolvern gekapselt werden, damit ein Ausfall dort nicht die restliche Antwort gefährdet. Wer alle Daten in einem einzigen monolithischen Resolver zusammenfasst, verliert diesen Vorteil von GraphQL vollständig.

6. Error-Extensions für strukturierte Client-Logik

Über das extensions-Objekt hinaus können GraphQL-Fehler beliebige zusätzliche Metadaten transportieren, solange sie mit dem Frontend-Team abgestimmt sind. Häufig sinnvolle Extensions sind ein maschinenlesbarer code für i18n-Übersetzungen im Frontend, ein field-Hinweis bei Validierungsfehlern in Formularen, und bei Rate-Limiting ein retry_after-Wert in Sekunden. Diese strukturierten Daten ermöglichen dem Frontend, gezielt zu reagieren, statt den message-Text zu parsen, der sich jederzeit ändern kann und für Übersetzung ungeeignet ist.

Ein wichtiger Grundsatz für jede GraphQL Fehlerbehandlung: die message ist für Entwickler und Logs gedacht, nicht direkt für die Anzeige im UI. Übersetzte, nutzerfreundliche Texte sollten im Frontend anhand von extensions.code generiert werden, nicht durch direkte Anzeige der Backend-Fehlermeldung. Das entkoppelt Backend-Sprachänderungen von der Frontend-Lokalisierung und verhindert technische Fehlermeldungen im UI.

7. Logging und Monitoring von GraphQL-Fehlern

Ohne systematisches Logging bleiben GraphQL-Fehler im Blindflug. Magento loggt unbehandelte Exceptions standardmäßig in var/log/exception.log, aber bewusst geworfene GraphQlInputException-Fehler erscheinen dort oft nicht, weil sie als erwartete Business-Fehler behandelt werden. Für Monitoring lohnt sich ein eigener Plugin auf Magento\Framework\GraphQl\Exception\ExceptionFormatter, der jeden Fehler zusätzlich strukturiert an ein Monitoring-System wie Sentry oder an einen eigenen Log-Kanal weiterreicht.

Wichtig für die Priorisierung: nicht jeder GraphQL-Fehler ist gleich relevant. Ein graphql-input-Fehler durch eine falsche Nutzereingabe ist normal und muss nicht alarmieren. Ein wiederholt auftretender graphql-no-such-entity-Fehler auf ein bestimmtes Produkt kann dagegen auf ein Datenproblem im Katalog hindeuten. Ein Monitoring-Setup, das nach extensions.category filtert und aggregiert, liefert hier deutlich aussagekräftigere Signale als ein simples Zählen aller Fehler.


<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlMonitoring\Plugin;

use Magento\Framework\GraphQl\Exception\ExceptionFormatter;
use Magento\Framework\GraphQl\Query\Resolver\Value;
use Psr\Log\LoggerInterface;

/**
 * Plugin that forwards structured GraphQL error data to a dedicated log channel.
 */
final class LogFormattedErrors
{
    /**
     * @param LoggerInterface $graphQlLogger Dedicated logger channel for GraphQL errors
     */
    public function __construct(
        private readonly LoggerInterface $graphQlLogger
    ) {
    }

    /**
     * Log the formatted error alongside its category before returning it unchanged.
     *
     * @param ExceptionFormatter $subject
     * @param array $result
     * @return array
     */
    public function afterFormat(ExceptionFormatter $subject, array $result): array
    {
        $this->graphQlLogger->info('GraphQL error', [
            'category' => $result['extensions']['category'] ?? 'unknown',
            'message' => $result['message'] ?? '',
        ]);

        return $result;
    }
}

8. Fehler im Frontend konsumieren

Ein Hyvä-Frontend, das über Alpine.js GraphQL-Requests ausführt, sollte grundsätzlich beide Teile der Antwort auswerten: data für erfolgreiche Felder und errors für fehlgeschlagene. Eine robuste Fetch-Funktion prüft zuerst, ob errors im Response-Body vorhanden ist, gruppiert die Fehler nach path, und zeigt nur die Fehlermeldungen an, die zu tatsächlich angefragten und fehlgeschlagenen Feldern gehören. Felder, die erfolgreich Daten geliefert haben, werden trotz vorhandener Fehler an anderer Stelle normal gerendert.

Für Formulare, die eine eigene GraphQL Mutation aufrufen, lohnt sich eine Zuordnungstabelle von extensions.category zu UI-Verhalten: graphql-input zeigt eine Inline-Validierungsmeldung am betroffenen Feld, graphql-authorization löst eine Weiterleitung zum Login aus, unbekannte Kategorien zeigen eine generische Fehlermeldung mit Support-Kontakt. Diese Zuordnung macht die Fehlerbehandlung im Frontend vorhersehbar und wartbar, statt für jeden neuen Fehlerfall neuen Sonderfall-Code zu schreiben.

9. Schlechte vs. gute Fehlerbehandlung im Vergleich

Die folgende Tabelle zeigt typische Muster schlechter GraphQL Fehlerbehandlung im Vergleich zur empfohlenen Vorgehensweise.

Aspekt Schlechte Praxis Empfohlene Praxis
Exception-Typ Generische \Exception werfen GraphQlInputException und verwandte Klassen
Fehlerkategorisierung Keine extensions.category Konsistente Kategorien je Fehlertyp
Partial Responses Ein Feld-Fehler bricht die gesamte Query ab Fehler isoliert pro Feld, restliche Daten bleiben erhalten
Frontend-Anzeige message-Text direkt im UI angezeigt Übersetzte Texte über extensions.code generiert
Monitoring Nur var/log/exception.log, keine Kategorisierung Strukturiertes Logging nach Kategorie mit Alerting

Der Unterschied zwischen den beiden Spalten entscheidet in der Praxis darüber, wie schnell ein Team Produktionsprobleme erkennt und wie präzise Nutzer auf Fehler hingewiesen werden. Investitionen in strukturierte Fehlerbehandlung zahlen sich bereits ab der zweiten oder dritten eigenen Mutation im Projekt aus.

Mironsoft

Magento 2 GraphQL-Entwicklung und API-Architektur

Unklare GraphQL-Fehler im Shop-Frontend?

Wir bringen strukturierte GraphQL Fehlerbehandlung in euren Magento-Shop: eigene Exception-Klassen, konsistente Error-Kategorien und Monitoring, das echte Probleme von normalen Nutzereingaben unterscheidet.

Exception-Design

Eigene, strukturierte Exception-Klassen mit sinnvollen Extensions

Frontend-Integration

Konsistente Zuordnung von Error-Kategorien zu UI-Verhalten

Monitoring

Strukturiertes Error-Logging mit Alerting für kritische Kategorien

10. Zusammenfassung

Gute GraphQL Fehlerbehandlung in Magento 2 beginnt mit dem Verständnis, dass HTTP-Status 200 nichts über Erfolg oder Misserfolg einer Query aussagt, entscheidend ist das errors-Array im Response-Body. Die eingebauten Exception-Klassen GraphQlInputException, GraphQlAuthorizationException und GraphQlNoSuchEntityException decken die häufigsten Fälle konsistent ab, eigene Exception-Klassen mit passenden Extensions erweitern das Muster für fachspezifische Fehler.

Partial Responses erlauben, Fehler isoliert pro Feld zu behandeln, statt eine gesamte Query wegen eines instabilen externen Dienstes scheitern zu lassen. Strukturierte Error-Extensions ermöglichen dem Frontend, ohne Textparsing gezielt zu reagieren, und systematisches Logging nach Fehlerkategorie macht Produktionsprobleme sichtbar, bevor Nutzer sie in großer Zahl melden. Diese Bausteine zusammen verwandeln GraphQL-Fehler von einer Blackbox in ein nachvollziehbares, wartbares System.

GraphQL Fehlerbehandlung in Magento 2 — Das Wichtigste auf einen Blick

Errors-Array

HTTP 200 sagt nichts über Erfolg aus. Das errors-Array neben data enthält die eigentliche Fehlerinformation.

Exception-Klassen

GraphQlInputException, GraphQlAuthorizationException, GraphQlNoSuchEntityException für konsistente Fehlerkategorien.

Partial Responses

Fehler isoliert pro Feld behandeln, damit ein Ausfall nicht die gesamte Antwort gefährdet.

Monitoring

Strukturiertes Logging nach extensions.category für aussagekräftiges Alerting statt reiner Fehlerzählung.

11. FAQ: GraphQL Fehlerbehandlung in Magento 2

1Warum HTTP 200 bei Fehlern?
GraphQL signalisiert Fehler über das errors-Array im Body, nicht über den HTTP-Status.
2Was enthält ein Error-Objekt?
message, locations, path und extensions mit strukturierten Metadaten wie der Kategorie.
3Welche eingebauten Exceptions gibt es?
GraphQlInputException, GraphQlAuthorizationException und GraphQlNoSuchEntityException.
4Wann eine eigene Exception-Klasse?
Bei fachspezifischen Fehlern mit zusätzlichen strukturierten Daten, über GraphQlExceptionInterface.
5Was ist eine Partial Response?
Eine Antwort mit erfolgreichen und fehlerhaften Feldern gleichzeitig, dank isolierter Fehlerbehandlung pro Feld.
6Message direkt im UI anzeigen?
Nein, message ist für Entwickler gedacht. Übersetzte Texte über extensions.code generieren.
7Wo werden GraphQL-Fehler geloggt?
Oft gar nicht standardmäßig, deshalb lohnt sich ein Plugin auf ExceptionFormatter für strukturiertes Logging.
8Kritische vs. normale Fehler unterscheiden?
Über Filterung nach extensions.category statt einer pauschalen Fehlerzählung.
9Wie mit unbekannten Kategorien umgehen?
Generische Fehlermeldung mit Support-Kontakt als Fallback, bekannte Kategorien gezielt behandeln.
10Was passiert bei generischer Exception?
Ein undifferenzierter Internal Server Error ohne aussagekräftige Kategorie, das Frontend muss Text parsen.