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.

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.
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.
# GraphQL-Unterstützung installieren
composer require api-platform/graphqlNach der Installation wird der /graphql-Endpunkt automatisch verfügbar. Das Schema wird aus vorhandenen #[ApiResource]-Attributen ohne zusätzliche Konfiguration generiert.
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.
# 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.
# 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.
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<Book> $collection
* @return iterable<Book>
*/
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:
#[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.
Bereit für deine Symfony-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
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.
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.
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.
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:
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.
# 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:
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
{
// ...
}# 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.
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:
{
"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.
Bereit für deine Symfony-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Performance-Optimierung für GraphQL-APIs
GraphQL-APIs erfordern spezifische Optimierungsstrategien aufgrund ihrer flexiblen Abfragestruktur.
Query Complexity Limiting verhindert ressourcenintensive Abfragen:
# 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:
#[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.
Findest du den Bug in Symfony?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 27. August 2026
Teilen
Verwandte Artikel

Symfony REST API Sicherheit 2026: OAuth2, Rate Limiting und Interviewfragen
Umfassender Leitfaden zur Absicherung von Symfony REST APIs mit OAuth2 Token Introspection, RateLimiter-Komponente, Voters und bewährten Sicherheitspraktiken.

API Platform mit Symfony 2026: Architektur und Interview-Fragen für Entwickler
Umfassender Leitfaden zu API Platform mit Symfony 2026. Lernen Sie REST-API-Architektur, State Providers, Processors und häufige Interview-Fragen für Symfony-Entwickler.

Symfony REST API Sicherheit: JWT-Authentifizierung und Best Practices 2026
Umfassender Leitfaden zur Absicherung von Symfony REST APIs mit JWT-Authentifizierung, LexikJWTAuthenticationBundle und bewährten Sicherheitspraktiken für produktionsreife Anwendungen.