API Platform GraphQL met Symfony: Schema's, Mutations en Sollicitatievragen 2026
Complete handleiding voor API Platform GraphQL met Symfony: schemageneratie, queries, mutations, custom resolvers, beveiliging en technische sollicitatievragen voor 2026.

API Platform GraphQL transformeert Symfony-applicaties in krachtige, type-safe API's die de over-fetching en under-fetching problemen oplossen die inherent zijn aan REST. Met automatische schemageneratie uit PHP-attributen en volledige Relay-specificatie ondersteuning biedt API Platform 4.x een productieklare GraphQL-implementatie die minimale configuratie vereist.
GraphQL vraagt precies de benodigde velden op in één enkele query, terwijl REST vaste responsstructuren retourneert. API Platform genereert beide endpoints vanuit dezelfde resourcedefinitie, waardoor clients het protocol kunnen kiezen dat past bij hun use case.
Installatie en Activering van GraphQL-ondersteuning in Symfony
API Platform scheidt GraphQL-functionaliteit in een apart pakket. Deze modulaire aanpak houdt de kern lichtgewicht voor projecten die alleen REST nodig hebben.
# Installeer GraphQL-ondersteuning
composer require api-platform/graphqlNa installatie wordt het /graphql-endpoint automatisch beschikbaar. Het schema wordt gegenereerd uit bestaande #[ApiResource]-attributen zonder extra configuratie.
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;
// Getters en setters...
}Deze enkele entiteitsdefinitie stelt queries beschikbaar voor het ophalen van boeken op ID of als collecties, plus mutations voor create, update en delete operaties. Het GraphQL-schema weerspiegelt PHP-types direct: string wordt String!, nullable types worden optionele velden.
GraphQL Queries en Mutations Schrijven
GraphQL-queries specificeren precies welke velden geretourneerd moeten worden. Deze precisie elimineert verspilde bandbreedte en vermindert client-side datatransformaties.
# Haal een enkel boek op met specifieke velden
query GetBook {
book(id: "/books/42") {
title
isbn
publishedAt
}
}
# Haal een collectie op met paginering
query ListBooks {
books(first: 10, after: "cursor123") {
edges {
node {
id
title
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}Mutations volgen de Relay-specificatie met input-objecten en clientMutationId voor het traceren van verzoeken.
# Maak een nieuw boek aan
mutation CreateBook {
createBook(input: {
title: "Domain-Driven Design"
isbn: "9780321125217"
publishedAt: "2003-08-30"
clientMutationId: "create-1"
}) {
book {
id
title
}
clientMutationId
}
}
# Werk een bestaand boek bij
mutation UpdateBook {
updateBook(input: {
id: "/books/42"
title: "Bijgewerkte Titel"
clientMutationId: "update-1"
}) {
book {
id
title
}
}
}De clientMutationId helpt clients om responses te correleren met verzoeken in batch-scenario's. API Platform retourneert deze ongewijzigd in de response.
Implementatie van Custom Resolvers voor Complexe Businesslogica
Standaard CRUD-operaties dekken basiscases, maar echte applicaties hebben aangepaste businesslogica nodig. API Platform biedt resolver-interfaces voor queries en mutations.
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
{
// Toegang tot GraphQL-argumenten vanuit de context
$limit = $context['args']['limit'] ?? 10;
$period = $context['args']['period'] ?? 'month';
return $this->bookRepository->findBestSellers($limit, $period);
}
}De custom resolver wordt geregistreerd in de entiteitsconfiguratie:
#[ApiResource(
graphQlOperations: [
new QueryCollection(
name: 'bestSellers',
resolver: BookBestSellerResolver::class,
args: [
'limit' => ['type' => 'Int', 'default_value' => 10],
'period' => ['type' => 'String', 'default_value' => 'month'],
]
),
]
)]
class Book
{
// ...
}Dit stelt een bestSellers-query beschikbaar die limit- en period-argumenten accepteert, en aangepaste repository-logica uitvoert in plaats van standaard Doctrine-queries.
Klaar om je Symfony gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
GraphQL-operaties Beveiligen met Voters en Expressies
De beveiligingsconfiguratie voor GraphQL werkt onafhankelijk van REST. Elke operatie kan zijn eigen toegangsregels definiëren met behulp van Symfony's expressietaal.
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: "Alleen de auteur kan dit boek bijwerken."
),
new Mutation(
name: 'delete',
security: "is_granted('ROLE_ADMIN')"
),
]
)]
class Book
{
// ...
}De object-variabele in beveiligingsexpressies verwijst naar de entiteit die wordt benaderd. Dit maakt fijnmazige eigendomscontroles mogelijk. Voor complexe autorisatielogica bieden Symfony Security Voters een schonere oplossing dan inline expressies.
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,
};
}
}Real-time Updates met GraphQL Subscriptions
API Platform implementeert GraphQL-subscriptions via Mercure, een protocol voor server-sent events. Subscriptions pushen data naar clients wanneer resources veranderen.
use ApiPlatform\Metadata\GraphQl\Subscription;
#[ApiResource(
mercure: true,
graphQlOperations: [
new Query(),
new Mutation(name: 'update'),
new Subscription(),
]
)]
class Book
{
// ...
}Clients abonneren zich op wijzigingen met standaard GraphQL-subscription syntax:
subscription BookUpdates {
updateBookSubscribe(input: { id: "/books/42" }) {
book {
id
title
updatedAt
}
}
}Wanneer een mutation het boek bijwerkt, broadcast Mercure de wijziging naar alle geabonneerde clients. Dit patroon is geschikt voor collaboratieve applicaties, live dashboards en real-time notificaties.
Paginering en Filtering in GraphQL-queries
API Platform implementeert standaard Relay-conforme cursor-gebaseerde paginering. Deze aanpak biedt consistente resultaten, zelfs bij veranderende datasets.
# Haal de eerste pagina op
query FirstPage {
books(first: 20) {
edges {
node {
id
title
author {
name
}
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
# Haal de volgende pagina op met cursor
query NextPage {
books(first: 20, after: "YXJyYXljb25uZWN0aW9uOjE5") {
edges {
node {
id
title
}
}
pageInfo {
hasNextPage
endCursor
}
}
}Filtering gebruikt dezelfde configuratie als REST-filters, maar wordt toegepast via query-argumenten:
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
{
// ...
}# Gefilterde query met sortering
query FilteredBooks {
books(
title: "Symfony"
publishedAt: { after: "2024-01-01" }
order: { publishedAt: "DESC" }
first: 10
) {
edges {
node {
title
publishedAt
}
}
}
}Foutafhandeling en Validatie
API Platform integreert naadloos Symfony Validator-constraints met GraphQL-mutations. Validatiefouten worden geretourneerd als gestructureerde GraphQL-fouten.
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ApiResource]
class Book
{
#[ORM\Column(length: 255)]
#[Assert\NotBlank(message: 'De titel mag niet leeg zijn.')]
#[Assert\Length(max: 255, maxMessage: 'De titel mag maximaal 255 tekens bevatten.')]
private string $title;
#[ORM\Column(length: 13)]
#[Assert\Isbn(message: 'Ongeldig ISBN-formaat.')]
private string $isbn;
#[ORM\Column]
#[Assert\NotNull]
#[Assert\LessThanOrEqual('today', message: 'Publicatiedatum kan niet in de toekomst liggen.')]
private \DateTimeImmutable $publishedAt;
}Bij validatiefouten retourneert API Platform gestructureerde foutdetails:
{
"errors": [
{
"message": "title: De titel mag niet leeg zijn.",
"extensions": {
"category": "user",
"violations": [
{
"path": "title",
"message": "De titel mag niet leeg zijn."
}
]
}
}
]
}Sollicitatievragen: API Platform GraphQL
Technische sollicitaties voor Symfony-posities behandelen steeds vaker GraphQL-integratie. Deze vragen testen begrip van zowel de specificatie als de API Platform-implementatie.
V: Hoe genereert API Platform het GraphQL-schema?
API Platform inspecteert #[ApiResource]-attributen en PHP-typedeclaraties om het schema te bouwen. Entiteitseigenschappen worden velden, waarbij PHP-types worden gemapped naar GraphQL-types. Het schema wordt bij elke request opnieuw gegenereerd in ontwikkelmodus en gecachet in productie.
V: Wat is het verschil tussen Query- en QueryCollection-operaties?
Query haalt een enkel item op per identifier en vereist een id-argument. QueryCollection retourneert meerdere items met optionele filtering, paginering en sortering. Beide kunnen custom resolvers hebben, maar hun interfaces verschillen: QueryItemResolverInterface vs. QueryCollectionResolverInterface.
V: Hoe worden relaties tussen entiteiten opgelost in GraphQL?
API Platform lost relaties automatisch op wanneer ze als velden worden opgevraagd. Doctrine-relaties (ManyToOne, OneToMany) worden gerepresenteerd als geneste types in het schema. Het N+1-queryprobleem wordt automatisch geoptimaliseerd door Data Loaders, die meerdere relatie-queries batchen.
V: Wanneer een custom resolver gebruiken in plaats van standaard CRUD?
Custom resolvers zijn noodzakelijk wanneer de businesslogica verder gaat dan simpel ophalen of persisteren. Voorbeelden zijn: geaggregeerde queries, queries met complexe berekeningen, integratie met externe API's of domeinspecifieke operaties die niet het CRUD-patroon volgen.
V: Hoe werken GraphQL-subscriptions met Mercure?
Wanneer een client een subscription opzet, maakt API Platform verbinding met een Mercure-hub. Tijdens mutations publiceert de server updates via Mercure, die naar alle geabonneerde clients worden gestreamd. Dit vereist een draaiende Mercure-hub en configuratie van de MERCURE_URL-omgevingsvariabele.
V: Hoe verschilt de beveiligingsconfiguratie tussen REST en GraphQL?
REST en GraphQL kunnen verschillende beveiligingsregels hebben voor dezelfde resource. GraphQL-operaties worden apart geconfigureerd in graphQlOperations, terwijl REST-operaties worden gedefinieerd in operations. Dit maakt verschillende toegangsniveaus mogelijk afhankelijk van het protocol.
V: Hoe wordt validatie afgehandeld in GraphQL-mutations?
Symfony Validator-constraints worden automatisch uitgevoerd voor persistentie. Validatiefouten worden geretourneerd als GraphQL-fouten met categorie "user" en gedetailleerde overtredingsinformatie. Clients kunnen deze fouten parsen en veldspecifieke foutmeldingen tonen.
Klaar om je Symfony gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Prestatie-optimalisatie voor GraphQL API's
GraphQL API's vereisen specifieke optimalisatiestrategieën vanwege hun flexibele querystructuur.
Query Complexity Limiting voorkomt resource-intensieve queries:
# 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 benut Symfony's HTTP-cache:
#[ApiResource(
graphQlOperations: [
new Query(
cacheHeaders: [
'max_age' => 3600,
'shared_max_age' => 7200,
]
),
]
)]Persisted Queries reduceren payload-grootte door query-strings te vervangen door hashes. Dit is vooral nuttig voor mobiele applicaties met beperkte bandbreedte.
Conclusie
API Platform GraphQL biedt een complete GraphQL-implementatie voor Symfony-applicaties met minimale configuratie-inspanning. De automatische schemageneratie uit PHP-attributen, Relay-conforme paginering en naadloze integratie met Symfony Security maken het een productieklare oplossing.
De belangrijkste inzichten voor ontwikkelaars en sollicitanten:
- GraphQL en REST kunnen worden bediend vanuit dezelfde
#[ApiResource]-definitie, waarbij elk protocol onafhankelijk configureerbaar is - Custom resolvers maken complexe businesslogica mogelijk buiten standaard CRUD-operaties
- Beveiligingsexpressies en voters bieden flexibele, fijnmazige toegangscontrole
- Mercure-gebaseerde subscriptions maken real-time updates mogelijk zonder WebSocket-complexiteit
- Validatie, filtering en paginering werken naadloos met de GraphQL-specificatie
Het beheersen van deze concepten is essentieel voor moderne Symfony-ontwikkelaars, aangezien GraphQL API's steeds gangbaarder worden in enterprise-applicaties.
Zie jij de bug in Symfony?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter van SharpSkill
Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.
Bijgewerkt op 27 augustus 2026
Delen
Gerelateerde artikelen

Symfony REST API Beveiliging in 2026: OAuth2, Rate Limiting en Sollicitatievragen
Uitgebreide gids voor het beveiligen van Symfony REST APIs met OAuth2 Token Introspection, RateLimiter-component, Voters en beveiligingsbest practices.

API Platform met Symfony in 2026: Architectuur en Sollicitatievragen voor Ontwikkelaars
Uitgebreide gids over API Platform met Symfony in 2026. Leer REST API-architectuur, State Providers, Processors en veelgestelde sollicitatievragen voor Symfony-ontwikkelaars.

Symfony REST API Beveiliging: JWT-Authenticatie en Best Practices 2026
Uitgebreide gids voor het beveiligen van Symfony REST APIs met JWT-authenticatie, LexikJWTAuthenticationBundle en bewezen beveiligingspraktijken voor productie-omgevingen.