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.
Inhaltsverzeichnis
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?
2 Wofür ist schema.graphqls zuständig?
3 Sollte Business-Logik direkt in den Resolver?
4 Query und Mutation: der Unterschied?
5 Welche Exception passt bei ungültigen Eingaben?
GraphQlInputException, damit der Client den Fehler klar als Input-Problem lesen kann.