Magento 2 · GraphQL

GraphQL Resolver selbst schreiben
Query und Mutation in Magento 2

Eigene Magento 2 GraphQL Resolver verbinden Schema, Query-Logik und Service-Schicht. Entscheidend sind dabei klare Typen, saubere Resolver-Klassen und eine Architektur, die nicht direkt Business-Logik in den Resolver drückt.

14 Min. Lesezeit PHP 8.4 Magento 2.4.8

1. Was ein guter GraphQL Resolver leisten muss

Ein GraphQL Resolver Magento 2 ist die Verbindung zwischen Schema und Anwendungslogik. Er entscheidet nicht nur, welche Daten für ein Feld geladen werden, sondern auch, wie Eingaben validiert, Services aufgerufen und Ergebnisse strukturiert zurückgegeben werden. Gerade in Magento ist das wichtig, weil GraphQL oft für Headless-Frontends, Hyvä-nahe AJAX-Flows oder externe Clients genutzt wird.

Der häufigste Architekturfehler ist, zu viel Logik direkt in den Resolver zu schreiben. Ein Resolver sollte eher orchestration sein: Argumente entgegennehmen, Kontext prüfen, Service aufrufen, Fehler sauber übersetzen und die Rückgabe in das erwartete Schema formatieren. Persistenz, Validierung und Business-Regeln gehören besser in Services oder Repositories. Genau so bleibt ein GraphQL Resolver Magento 2 testbar und erweiterbar.

Für dieses Tutorial bauen wir ein kleines Beispielmodul mit einer Query und einer Mutation. Die Query liefert eine Nachricht, die Mutation speichert eine Eingabe über einen Service. Dadurch sieht man beide Richtungen: lesender Zugriff und schreibender Zugriff. Beide nutzen dieselbe Service-Schicht, aber unterschiedliche Resolver-Klassen und unterschiedliche Schema-Einträge.

2. schema.graphqls definieren

Der Einstieg beginnt mit etc/schema.graphqls. Dort beschreibst du Typen, Query-Felder, Mutation-Felder und deren Argumente. Wenn du einen GraphQL Resolver Magento 2 bauen willst, sollte das Schema so klar wie möglich sein. GraphQL lebt davon, dass Clients genau sehen können, welche Felder existieren und welche Typen zurückkommen.

Für das Beispiel definieren wir einen Typ MironsoftMessage, eine Query mironsoftHello und eine Mutation saveMironsoftMessage. Das Schema muss nicht groß sein, aber es sollte fachlich verständlich benannt sein. Query- und Mutation-Namen wie getData oder saveItem sind technisch möglich, aber als API unnötig unscharf.


type MironsoftMessage {
    message: String!
    status: String!
}

type Query {
    mironsoftHello: MironsoftMessage
        @resolver(class: "Mironsoft\\GraphQlDemo\\Model\\Resolver\\HelloQuery")
}

type Mutation {
    saveMironsoftMessage(message: String!): MironsoftMessage
        @resolver(class: "Mironsoft\\GraphQlDemo\\Model\\Resolver\\SaveMessageMutation")
}

Das Schema ist nicht nur technische Konfiguration, sondern ein API-Vertrag. Wer an dieser Stelle klar denkt, spart sich später viele Rückfragen und Versionsprobleme. Ein GraphQL Resolver Magento 2 profitiert stark davon, wenn Typen klein, sprechend und fachlich sauber sind. Das gilt noch mehr, wenn mehrere Frontends oder Teams dieselbe API nutzen.

3. Query Resolver schreiben

Für lesende Zugriffe implementierst du typischerweise einen Resolver, der ResolverInterface umsetzt. Die Resolver-Methode erhält Feld, Kontext, Resolve-Info, Parent-Value und Argumente. In der Praxis brauchst du davon selten alles. Wichtig ist, dass du den Resolver nicht als Ablage für beliebige Logik missbrauchst. Ein GraphQL Resolver Magento 2 sollte minimal bleiben und an eine Service-Klasse delegieren.

Im Query-Beispiel ruft der Resolver einen Service auf, der die Nachricht liefert. Der Resolver selbst formt nur die Rückgabe so, wie das Schema sie erwartet. Dadurch bleibt die Business-Logik an einer Stelle und kann später auch von REST, CLI oder internem Code genutzt werden.


<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlDemo\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Mironsoft\GraphQlDemo\Service\MessageProvider;

/**
 * GraphQL query resolver for the hello message.
 */
final class HelloQuery implements ResolverInterface
{
    public function __construct(
        private readonly MessageProvider $messageProvider
    ) {}

    /**
     * Resolves the hello query result.
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        array $value = null,
        array $args = null
    ): array {
        return [
            'message' => $this->messageProvider->getMessage(),
            'status' => 'success'
        ];
    }
}

Der Resolver ist hier absichtlich knapp. Das ist kein Nachteil, sondern das Ziel. Ein sauberer GraphQL Resolver Magento 2 ist meistens klein. Je mehr Fachlogik du im Resolver findest, desto eher gehört sie in einen Service. Diese Trennung verbessert Testbarkeit und verhindert, dass Query-Resolver später zu einer zweiten Business-Schicht werden.

4. Mutation Resolver schreiben

Mutations unterscheiden sich von Queries vor allem dadurch, dass sie schreibende Aktionen auslösen. Hier wird saubere Validierung noch wichtiger. Ein GraphQL Resolver Magento 2 für eine Mutation sollte Eingaben prüfen, ungültige Requests klar ablehnen und nur über Services schreiben. Direkte Datenbanklogik im Resolver ist fast immer ein Fehler.

Im Beispiel akzeptiert die Mutation ein Feld message. Der Resolver prüft, ob der Wert vorhanden ist, und delegiert dann an einen Service. Für Produktivcode kann zusätzlich Kontextprüfung relevant sein: Ist der Nutzer eingeloggt? Darf die Rolle diese Aktion ausführen? Muss Store-Kontext oder Website-Kontext beachtet werden? All das gehört in Resolver und Services, nicht in das Schema selbst.


<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlDemo\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Mironsoft\GraphQlDemo\Service\MessageManager;

/**
 * GraphQL mutation resolver for saving a message.
 */
final class SaveMessageMutation implements ResolverInterface
{
    public function __construct(
        private readonly MessageManager $messageManager
    ) {}

    /**
     * Resolves the save message mutation.
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        array $value = null,
        array $args = null
    ): array {
        $message = trim((string) ($args['message'] ?? ''));

        if ($message === '') {
            throw new GraphQlInputException(__('The "message" argument is required.'));
        }

        $savedMessage = $this->messageManager->save($message);

        return [
            'message' => $savedMessage,
            'status' => 'saved'
        ];
    }
}

Die Exception-Wahl ist hier nicht nebensächlich. Ein GraphQL Resolver Magento 2 sollte fachliche Fehler so werfen, dass der Client sie klar interpretieren kann. Für ungültige Eingaben passt oft GraphQlInputException. Für Berechtigungen oder fehlende Entitäten gibt es andere Exception-Typen. Wenn jede Ausnahme als generischer Fehler endet, verliert die API an Qualität.

5. Service-Schicht und Fehlerbehandlung

Resolver werden besser, wenn sie gegen eine Service-Schicht arbeiten. Das gilt besonders in Magento, weil dieselbe Fachlogik oft in mehreren Kanälen gebraucht wird: GraphQL, REST, CLI, Cron oder internem Anwendungscode. Ein GraphQL Resolver Magento 2 sollte deshalb lieber Services orchestrieren als selbst Entscheidungen treffen, die auch anderswo gelten.

Im Beispiel nutzen wir zwei Services: einen Provider für lesende Daten und einen Manager für schreibende Logik. In realen Modulen würden dort Repositories, Validatoren, SearchCriteriaBuilder oder Event-Dispatching zum Einsatz kommen. Wichtig ist, dass der Resolver nicht selbst entscheidet, wie gespeichert wird oder welche Nebenwirkungen ausgelöst werden.


<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlDemo\Service;

/**
 * Provides a message for the GraphQL query.
 */
final class MessageProvider
{
    /**
     * Returns the example message.
     */
    public function getMessage(): string
    {
        return 'Hello from Magento GraphQL.';
    }
}

<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlDemo\Service;

/**
 * Handles write operations for the GraphQL mutation.
 */
final class MessageManager
{
    /**
     * Saves and returns the provided message.
     */
    public function save(string $message): string
    {
        return $message;
    }
}

Fehlerbehandlung sollte ebenfalls strukturiert sein. Eingabefehler sind etwas anderes als Berechtigungsfehler oder Systemfehler. Für API-Clients ist diese Unterscheidung entscheidend. Ein gut gebauter GraphQL Resolver Magento 2 gibt nicht nur Daten korrekt zurück, sondern scheitert auch kontrolliert und verständlich.

6. Typische Fehler

Die häufigsten Fehler bei GraphQL in Magento sehen ähnlich aus wie bei REST, aber mit einigen eigenen Besonderheiten. Erstens wird das Schema unscharf benannt und zu generisch aufgebaut. Zweitens steckt zu viel Logik direkt im Resolver. Drittens werden Exceptions unkontrolliert durchgereicht. Viertens wird keine saubere Trennung zwischen Query und Mutation beachtet. Fünftens werden Rückgaben als lose Arrays entworfen, ohne dass das Schema diese Struktur wirklich stabil beschreibt.

Ein weiterer häufiger Fehler ist N+1-Denken nicht mitzudenken. Gerade bei komplexeren GraphQL-Feldern kann ein Resolver schnell viele einzelne Datenbankabfragen auslösen. In kleinen Beispielen sieht man das kaum, in echten Katalog- oder Kundenabfragen aber sofort. Deshalb sollte ein GraphQL Resolver Magento 2 nicht nur korrekt, sondern auch performancebewusst gebaut werden.

Auch die Frage nach Sicherheit wird oft unterschätzt. Nur weil GraphQL technisch intern wirkt, ist es nicht automatisch sicher. Kontext, Kundengruppe, Store, ACL-nahe Logik und sensible Felder müssen sauber geprüft werden. Sonst entsteht schnell eine API, die intern bequem, aber extern zu offen ist.

7. GraphQL Resolver vs. REST Endpoint

Die Entscheidung zwischen GraphQL und REST ist in Magento keine reine Geschmacksfrage. Ein GraphQL Resolver Magento 2 ist stark, wenn Clients flexible Feldabfragen brauchen und Frontends nur genau die Daten laden sollen, die sie wirklich rendern. REST passt besser zu klassischen Ressourcen, stabilen Integrationsflüssen und vielen bestehenden Drittanbindungen.

Aspekt GraphQL Resolver REST Endpoint
Client-Flexibilität Sehr hoch, Felder gezielt abfragbar Fester pro Endpoint definierter Output
Definition schema.graphqls + Resolver webapi.xml + Service Contract
Typisierung Explizit im Schema sichtbar Vertrag stärker über API und Services beschrieben
Passend für Headless-Frontends, flexible UI-Daten Systemintegrationen, klassische API-Flows

In vielen Magento-Projekten existieren beide Ansätze nebeneinander. Dann lohnt es sich besonders, die Fachlogik in Services zu zentralisieren. So können GraphQL und REST denselben Kern nutzen, ohne doppelte Regeln oder divergierende Implementierungen aufzubauen.

Mironsoft

Magento 2 GraphQL, APIs und Headless-Architektur

Eigene Magento GraphQL APIs sauber aufbauen?

Wir entwickeln Magento 2 GraphQL Resolver mit klaren Schemas, sauberer Service-Schicht, performanten Datenzugriffen und stabilen Schnittstellen für Headless-Frontends und Integrationen.

Schema Design

Klare Typen, Queries und Mutations mit nachvollziehbaren Verträgen

Resolver

Schlanke Resolver-Klassen statt gemischter Business-Logik

Performance

Services, Repositories und Query-Strategien ohne unnötige N+1-Effekte

9. Zusammenfassung

Ein GraphQL Resolver Magento 2 sollte klein, klar und serviceorientiert sein. Das Schema definiert Typen und Felder, Resolver orchestrieren nur den Request, und die eigentliche Fachlogik bleibt in dedizierten Services. So entstehen Queries und Mutations, die stabil, testbar und erweiterbar bleiben.

Wer Resolver als Sammelstelle für Logik missbraucht, bekommt schnell schwer wartbare APIs. Besser ist eine Architektur, in der Schema, Resolver, Service-Schicht und Datenzugriff getrennt sind. Das passt zu Magento 2.4.8 und macht GraphQL langfristig nutzbar.

GraphQL Resolver Magento 2 — Das Wichtigste auf einen Blick

Schema

schema.graphqls definiert Typen, Queries und Mutations als sichtbaren API-Vertrag.

Resolver

Resolver sollten orchestrieren, nicht die gesamte Business-Logik selbst enthalten.

Services

Fachlogik in Services oder Repositories kapseln, damit GraphQL und andere Kanäle denselben Kern nutzen.

Fehler

Input-, Berechtigungs- und Systemfehler bewusst unterscheiden und passend an Clients zurückgeben.

10. FAQ: GraphQL Resolver in Magento 2

1 Was ist ein GraphQL Resolver in Magento 2?
Er verarbeitet Query oder Mutation und verbindet das GraphQL-Schema mit Service- oder Datenlogik.
2 Wofür ist schema.graphqls zuständig?
Für Typen, Queries, Mutations, Argumente und die Zuordnung zu Resolver-Klassen.
3 Sollte Business-Logik direkt in den Resolver?
Nein. Resolver sollten an Services delegieren und selbst möglichst schlank bleiben.
4 Query und Mutation: der Unterschied?
Queries lesen Daten. Mutations verändern Daten oder lösen schreibende Prozesse aus.
5 Welche Exception passt bei ungültigen Eingaben?
Oft GraphQlInputException, damit der Client den Fehler klar als Input-Problem lesen kann.
6 Darf ein Resolver Repositories nutzen?
Ja. Repositories und Services sind typische DI-Abhängigkeiten für Resolver.
7 Wann ist GraphQL besser als REST?
Wenn Clients flexibel nur genau die Felder laden sollen, die sie brauchen.
8 Was ist ein typischer Performance-Fehler?
N+1-artige Abfragen durch viele kleine Datenzugriffe innerhalb verschachtelter Feldauflösungen.
9 Braucht eine Mutation Validierung?
Ja. Eingaben, Berechtigungen und Seiteneffekte müssen bewusst geprüft werden.
10 Wie testet man einen GraphQL Resolver?
Mit echter GraphQL-Abfrage plus Tests der Service-Schicht, Eingaben und Fehlerfälle.