# API Platform GraphQL mit Symfony: Schemas, Mutationen und Interviewfragen 2026 > Komplette Anleitung zu API Platform GraphQL mit Symfony: Schema-Generierung, Queries, Mutationen, Custom Resolver, Sicherheit und technische Interviewfragen für 2026. - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 8 min --- API Platform GraphQL verwandelt Symfony-Anwendungen in leistungsstarke, typsichere APIs, die Over-Fetching- und Under-Fetching-Probleme lösen, die REST-APIs inhärent sind. Mit automatischer Schema-Generierung aus PHP-Attributen und vollständiger Relay-Spezifikationsunterstützung bietet API Platform 4.x eine produktionsreife GraphQL-Implementierung, die minimale Konfiguration erfordert. > **Hauptunterschied: GraphQL vs REST in API Platform** > > GraphQL fordert genau die benötigten Felder in einer einzigen Abfrage an, während REST feste Antwortstrukturen zurückgibt. API Platform generiert beide Endpunkte aus derselben Ressourcendefinition, sodass Clients das Protokoll wählen können, das ihrem Anwendungsfall entspricht. ## Installation und Aktivierung der GraphQL-Unterstützung in Symfony API Platform trennt die GraphQL-Funktionalität in ein dediziertes Paket. Dieser modulare Ansatz hält den Kern schlank für Projekte, die nur REST benötigen. ```bash # GraphQL-Unterstützung installieren composer require api-platform/graphql ``` Nach der Installation wird der `/graphql`-Endpunkt automatisch verfügbar. Das Schema wird aus vorhandenen `#[ApiResource]`-Attributen ohne zusätzliche Konfiguration generiert. ```php // src/Entity/Book.php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GraphQl\Query; use ApiPlatform\Metadata\GraphQl\QueryCollection; use ApiPlatform\Metadata\GraphQl\Mutation; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] #[ApiResource( graphQlOperations: [ new Query(), new QueryCollection(), new Mutation(name: 'create'), new Mutation(name: 'update'), new Mutation(name: 'delete'), ] )] class Book { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 255)] private string $title; #[ORM\Column(length: 13)] private string $isbn; #[ORM\Column] private \DateTimeImmutable $publishedAt; // Getter und Setter... } ``` Diese einzelne Entity-Definition stellt Queries zum Abrufen von Büchern nach ID oder als Sammlungen bereit, plus Mutationen für Erstellen, Aktualisieren und Löschen. Das GraphQL-Schema spiegelt PHP-Typen direkt wider: `string` wird zu `String!`, nullable Typen werden zu optionalen Feldern. ## GraphQL-Queries und Mutationen schreiben GraphQL-Queries spezifizieren genau, welche Felder zurückgegeben werden sollen. Diese Präzision eliminiert verschwendete Bandbreite und reduziert clientseitige Datentransformationen. ```graphql # Ein einzelnes Buch mit spezifischen Feldern abrufen query GetBook { book(id: "/books/42") { title isbn publishedAt } } # Eine Sammlung mit Paginierung abrufen query ListBooks { books(first: 10, after: "cursor123") { edges { node { id title } cursor } pageInfo { hasNextPage endCursor } } } ``` Mutationen folgen der Relay-Spezifikation mit `input`-Objekten und `clientMutationId` zur Anfrageverfolgung. ```graphql # Ein neues Buch erstellen mutation CreateBook { createBook(input: { title: "Domain-Driven Design" isbn: "9780321125217" publishedAt: "2003-08-30" clientMutationId: "create-1" }) { book { id title } clientMutationId } } # Ein vorhandenes Buch aktualisieren mutation UpdateBook { updateBook(input: { id: "/books/42" title: "Aktualisierter Titel" clientMutationId: "update-1" }) { book { id title } } } ``` Die `clientMutationId` hilft Clients, Antworten mit Anfragen in Batch-Szenarien zu korrelieren. API Platform gibt sie unverändert in der Antwort zurück. ## Implementierung von Custom Resolvern für komplexe Geschäftslogik Standard-CRUD-Operationen decken grundlegende Fälle ab, aber echte Anwendungen benötigen benutzerdefinierte Geschäftslogik. API Platform bietet Resolver-Interfaces für Queries und Mutationen. ```php // src/Resolver/BookBestSellerResolver.php namespace App\Resolver; use ApiPlatform\GraphQl\Resolver\QueryCollectionResolverInterface; use App\Repository\BookRepository; final class BookBestSellerResolver implements QueryCollectionResolverInterface { public function __construct( private readonly BookRepository $bookRepository ) {} /** * @param iterable $collection * @return iterable */ public function __invoke(iterable $collection, array $context): iterable { // GraphQL-Argumente aus dem Kontext abrufen $limit = $context['args']['limit'] ?? 10; $period = $context['args']['period'] ?? 'month'; return $this->bookRepository->findBestSellers($limit, $period); } } ``` Der Custom Resolver wird in der Entity-Konfiguration registriert: ```php // src/Entity/Book.php #[ApiResource( graphQlOperations: [ new QueryCollection( name: 'bestSellers', resolver: BookBestSellerResolver::class, args: [ 'limit' => ['type' => 'Int', 'default_value' => 10], 'period' => ['type' => 'String', 'default_value' => 'month'], ] ), ] )] class Book { // ... } ``` Dies stellt eine `bestSellers`-Query bereit, die `limit`- und `period`-Argumente akzeptiert und benutzerdefinierte Repository-Logik anstelle von Standard-Doctrine-Queries ausführt. ## GraphQL-Operationen mit Votern und Expressions absichern Die Sicherheitskonfiguration für GraphQL arbeitet unabhängig von REST. Jede Operation kann ihre eigenen Zugriffsregeln mithilfe der Expression-Sprache von Symfony definieren. ```php // src/Entity/Book.php use ApiPlatform\Metadata\GraphQl\Query; use ApiPlatform\Metadata\GraphQl\Mutation; #[ApiResource( graphQlOperations: [ new Query( security: "is_granted('ROLE_USER')" ), new QueryCollection( security: "is_granted('ROLE_USER')" ), new Mutation( name: 'create', security: "is_granted('ROLE_EDITOR')" ), new Mutation( name: 'update', security: "is_granted('ROLE_EDITOR') and object.getAuthor() == user", securityMessage: "Nur der Autor kann dieses Buch aktualisieren." ), new Mutation( name: 'delete', security: "is_granted('ROLE_ADMIN')" ), ] )] class Book { // ... } ``` Die `object`-Variable in Sicherheitsausdrücken bezieht sich auf die Entity, auf die zugegriffen wird. Dies ermöglicht feinkörnige Eigentumsüberprüfungen. Für komplexe Autorisierungslogik bieten Symfony Security Voter eine sauberere Lösung als Inline-Ausdrücke. ```php // src/Security/Voter/BookVoter.php namespace App\Security\Voter; use App\Entity\Book; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\User\UserInterface; class BookVoter extends Voter { public const EDIT = 'BOOK_EDIT'; public const DELETE = 'BOOK_DELETE'; protected function supports(string $attribute, mixed $subject): bool { return in_array($attribute, [self::EDIT, self::DELETE]) && $subject instanceof Book; } protected function voteOnAttribute( string $attribute, mixed $subject, TokenInterface $token ): bool { $user = $token->getUser(); if (!$user instanceof UserInterface) { return false; } /** @var Book $book */ $book = $subject; return match($attribute) { self::EDIT => $book->getAuthor() === $user, self::DELETE => in_array('ROLE_ADMIN', $user->getRoles()), default => false, }; } } ``` ## Echtzeit-Updates mit GraphQL Subscriptions API Platform implementiert GraphQL Subscriptions über Mercure, ein Protokoll für Server-Sent Events. Subscriptions senden Daten an Clients, wenn sich Ressourcen ändern. ```php // src/Entity/Book.php use ApiPlatform\Metadata\GraphQl\Subscription; #[ApiResource( mercure: true, graphQlOperations: [ new Query(), new Mutation(name: 'update'), new Subscription(), ] )] class Book { // ... } ``` Clients abonnieren Änderungen mit Standard-GraphQL-Subscription-Syntax: ```graphql subscription BookUpdates { updateBookSubscribe(input: { id: "/books/42" }) { book { id title updatedAt } } } ``` Wenn eine Mutation das Buch aktualisiert, sendet Mercure die Änderung an alle abonnierten Clients. Dieses Muster eignet sich für kollaborative Anwendungen, Live-Dashboards und Echtzeit-Benachrichtigungen. ## Paginierung und Filterung in GraphQL-Queries API Platform implementiert Relay-konforme Cursor-basierte Paginierung standardmäßig. Dieser Ansatz bietet konsistente Ergebnisse auch bei sich ändernden Datensätzen. ```graphql # Erste Seite abrufen query FirstPage { books(first: 20) { edges { node { id title author { name } } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } } # Nächste Seite mit Cursor abrufen query NextPage { books(first: 20, after: "YXJyYXljb25uZWN0aW9uOjE5") { edges { node { id title } } pageInfo { hasNextPage endCursor } } } ``` Filterung nutzt dieselbe Konfiguration wie REST-Filter, wird aber über Query-Argumente angewendet: ```php // src/Entity/Book.php use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; #[ApiResource] #[ApiFilter(SearchFilter::class, properties: ['title' => 'partial', 'author.name' => 'exact'])] #[ApiFilter(DateFilter::class, properties: ['publishedAt'])] #[ApiFilter(OrderFilter::class, properties: ['title', 'publishedAt'])] class Book { // ... } ``` ```graphql # Gefilterte Query mit Sortierung query FilteredBooks { books( title: "Symfony" publishedAt: { after: "2024-01-01" } order: { publishedAt: "DESC" } first: 10 ) { edges { node { title publishedAt } } } } ``` ## Fehlerbehandlung und Validierung API Platform integriert Symfony Validator-Constraints nahtlos mit GraphQL-Mutationen. Validierungsfehler werden als strukturierte GraphQL-Fehler zurückgegeben. ```php // src/Entity/Book.php use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity] #[ApiResource] class Book { #[ORM\Column(length: 255)] #[Assert\NotBlank(message: 'Der Titel darf nicht leer sein.')] #[Assert\Length(max: 255, maxMessage: 'Der Titel darf maximal 255 Zeichen haben.')] private string $title; #[ORM\Column(length: 13)] #[Assert\Isbn(message: 'Ungültiges ISBN-Format.')] private string $isbn; #[ORM\Column] #[Assert\NotNull] #[Assert\LessThanOrEqual('today', message: 'Veröffentlichungsdatum kann nicht in der Zukunft liegen.')] private \DateTimeImmutable $publishedAt; } ``` Bei Validierungsfehlern gibt API Platform strukturierte Fehlerdetails zurück: ```json { "errors": [ { "message": "title: Der Titel darf nicht leer sein.", "extensions": { "category": "user", "violations": [ { "path": "title", "message": "Der Titel darf nicht leer sein." } ] } } ] } ``` ## Interviewfragen: API Platform GraphQL Technische Interviews für Symfony-Positionen behandeln zunehmend GraphQL-Integration. Diese Fragen testen das Verständnis sowohl der Spezifikation als auch der API Platform-Implementierung. **F: Wie generiert API Platform das GraphQL-Schema?** API Platform inspiziert `#[ApiResource]`-Attribute und PHP-Typdeklarationen, um das Schema zu erstellen. Entity-Eigenschaften werden zu Feldern, wobei PHP-Typen auf GraphQL-Typen abgebildet werden. Das Schema wird im Entwicklungsmodus bei jeder Anfrage neu generiert und in der Produktion zwischengespeichert. **F: Was ist der Unterschied zwischen `Query`- und `QueryCollection`-Operationen?** `Query` ruft ein einzelnes Element nach Bezeichner ab und erfordert ein `id`-Argument. `QueryCollection` gibt mehrere Elemente mit optionaler Filterung, Paginierung und Sortierung zurück. Beide können benutzerdefinierte Resolver haben, aber ihre Interfaces unterscheiden sich: `QueryItemResolverInterface` vs. `QueryCollectionResolverInterface`. **F: Wie werden Beziehungen zwischen Entitäten in GraphQL aufgelöst?** API Platform löst Beziehungen automatisch auf, wenn sie als Felder angefordert werden. Doctrine-Beziehungen (`ManyToOne`, `OneToMany`) werden als verschachtelte Typen im Schema dargestellt. Die N+1-Query-Problematik wird durch Data Loader automatisch optimiert, die mehrere Beziehungsabfragen stapeln. **F: Wann sollte ein Custom Resolver anstelle von Standard-CRUD verwendet werden?** Custom Resolver sind notwendig, wenn die Geschäftslogik über einfaches Abrufen oder Persistieren hinausgeht. Beispiele sind: aggregierte Abfragen, Abfragen mit komplexen Berechnungen, Integration externer APIs oder domänenspezifische Operationen, die nicht dem CRUD-Muster entsprechen. **F: Wie funktionieren GraphQL Subscriptions mit Mercure?** Wenn ein Client eine Subscription einrichtet, stellt API Platform eine Verbindung zu einem Mercure-Hub her. Bei Mutationen veröffentlicht der Server Aktualisierungen über Mercure, die an alle abonnierten Clients gestreamt werden. Dies erfordert einen laufenden Mercure-Hub und die Konfiguration der `MERCURE_URL`-Umgebungsvariable. **F: Wie unterscheidet sich die Sicherheitskonfiguration zwischen REST und GraphQL?** REST und GraphQL können unterschiedliche Sicherheitsregeln für dieselbe Ressource haben. GraphQL-Operationen werden separat in `graphQlOperations` konfiguriert, während REST-Operationen in `operations` definiert werden. Dies ermöglicht unterschiedliche Zugriffsebenen je nach Protokoll. **F: Wie wird die Validierung in GraphQL-Mutationen gehandhabt?** Symfony Validator-Constraints werden vor der Persistierung automatisch ausgeführt. Validierungsfehler werden als GraphQL-Fehler mit der Kategorie "user" und detaillierten Verletzungsinformationen zurückgegeben. Clients können diese Fehler parsen und feldspezifische Fehlermeldungen anzeigen. ## Performance-Optimierung für GraphQL-APIs GraphQL-APIs erfordern spezifische Optimierungsstrategien aufgrund ihrer flexiblen Abfragestruktur. **Query Complexity Limiting** verhindert ressourcenintensive Abfragen: ```yaml # config/packages/api_platform.yaml api_platform: graphql: enabled: true graphiql: enabled: '%kernel.debug%' default_ide: graphiql nesting_separator: '_' collection: pagination: enabled: true introspection: enabled: '%kernel.debug%' ``` **Field-Level Caching** nutzt Symfonys HTTP-Cache: ```php #[ApiResource( graphQlOperations: [ new Query( cacheHeaders: [ 'max_age' => 3600, 'shared_max_age' => 7200, ] ), ] )] ``` **Persisted Queries** reduzieren die Payload-Größe, indem Query-Strings durch Hashes ersetzt werden. Dies ist besonders nützlich für mobile Anwendungen mit begrenzter Bandbreite. ## Fazit API Platform GraphQL bietet eine vollständige GraphQL-Implementierung für Symfony-Anwendungen mit minimalem Konfigurationsaufwand. Die automatische Schema-Generierung aus PHP-Attributen, die Relay-konforme Paginierung und die nahtlose Integration mit Symfony Security machen es zu einer produktionsreifen Lösung. Die wichtigsten Erkenntnisse für Entwickler und Interviewkandidaten: - GraphQL und REST können aus derselben `#[ApiResource]`-Definition bedient werden, wobei jedes Protokoll unabhängig konfiguriert werden kann - Custom Resolver ermöglichen komplexe Geschäftslogik jenseits von Standard-CRUD-Operationen - Sicherheitsausdrücke und Voter bieten flexible, feinkörnige Zugriffssteuerung - Mercure-basierte Subscriptions ermöglichen Echtzeit-Updates ohne WebSocket-Komplexität - Validierung, Filterung und Paginierung funktionieren nahtlos mit der GraphQL-Spezifikation Die Beherrschung dieser Konzepte ist für moderne Symfony-Entwickler unerlässlich, da GraphQL-APIs in Unternehmensanwendungen zunehmend verbreitet sind. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/symfony/api-platform-graphql-symfony-schemas-mutations-interview