# 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. - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 8 min --- 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. > **Kernverschil: GraphQL vs REST in API Platform** > > 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. ```bash # Installeer GraphQL-ondersteuning composer require api-platform/graphql ``` Na installatie wordt het `/graphql`-endpoint automatisch beschikbaar. Het schema wordt gegenereerd uit bestaande `#[ApiResource]`-attributen zonder extra configuratie. ```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; // 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. ```graphql # 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. ```graphql # 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. ```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 { // 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: ```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 { // ... } ``` Dit stelt een `bestSellers`-query beschikbaar die `limit`- en `period`-argumenten accepteert, en aangepaste repository-logica uitvoert in plaats van standaard Doctrine-queries. ## 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. ```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: "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. ```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, }; } } ``` ## 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. ```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 abonneren zich op wijzigingen met standaard GraphQL-subscription syntax: ```graphql 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. ```graphql # 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: ```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 # 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. ```php // src/Entity/Book.php 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: ```json { "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. ## Prestatie-optimalisatie voor GraphQL API's GraphQL API's vereisen specifieke optimalisatiestrategieën vanwege hun flexibele querystructuur. **Query Complexity Limiting** voorkomt resource-intensieve queries: ```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** benut Symfony's HTTP-cache: ```php #[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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/symfony/api-platform-graphql-symfony-schemas-mutations-interview