v3.1 The Event System

The Event System

Note: using Kernel event with API Platform should be mostly limited to tweaking the generated HTTP response. Also, GraphQL is not supported. For most use cases, better extension points, working both with REST and GraphQL, are available.

API Platform Core implements the Action-Domain-Responder pattern. This implementation is covered in depth in the Creating custom operations and controllers chapter.

Basically, API Platform Core executes an action class that will return an entity or a collection of entities. Then a series of event listeners are executed which validate the data, persist it in database, serialize it (typically in a JSON-LD document) and create an HTTP response that will be sent to the client.

To do so, API Platform Core leverages events triggered by the Symfony HTTP Kernel. You can also hook your own code to those events. There are handy and powerful extension points available at all points of the request lifecycle.

If you are using Doctrine, lifecycle events (ORM, MongoDB ODM) are also available if you want to hook into the persistence layer’s object lifecycle.

# Built-in Event Listeners

These built-in event listeners are registered for routes managed by API Platform:

NameEventPre & Post hooksPriorityDescription
AddFormatListenerkernel.requestNone28Guesses the best response format (content negotiation)
ReadListenerkernel.requestPRE_READ, POST_READ4Retrieves data from the persistence system using the state providers (GET, PUT, PATCH, DELETE)
QueryParameterValidateListenerkernel.requestNone2Validates query parameters
DeserializeListenerkernel.requestPRE_DESERIALIZE, POST_DESERIALIZE2Deserializes data into a PHP entity (POST); updates the entity retrieved using the state provider (PUT, PATCH)
DenyAccessListenerkernel.requestNone1Enforces access control using Security expressions
ValidateListenerkernel.viewPRE_VALIDATE, POST_VALIDATE64Validates data (POST, PUT, PATCH)
WriteListenerkernel.viewPRE_WRITE, POST_WRITE32Persists changes in the persistence system using the state processors (POST, PUT, PATCH, DELETE)
SerializeListenerkernel.viewPRE_SERIALIZE, POST_SERIALIZE16Serializes the PHP entity in string according to the request format
RespondListenerkernel.viewPRE_RESPOND, POST_RESPOND8Transforms serialized to a Symfony\Component\HttpFoundation\Response instance
AddLinkHeaderListenerkernel.responseNone0Adds a Link HTTP header pointing to the Hydra documentation
ValidationExceptionListenerkernel.exceptionNone0Serializes validation exceptions in the Hydra format
ExceptionListenerkernel.exceptionNone-96Serializes PHP exceptions in the Hydra format (including the stack trace in debug mode)

Some of these built-in listeners can be enabled/disabled by setting operation attributes:

AttributeTypeDefaultDescription
query_parameter_validatebooltrueEnables or disables QueryParameterValidateListener
readbooltrueEnables or disables ReadListener
deserializebooltrueEnables or disables DeserializeListener
validatebooltrueEnables or disables ValidateListener
writebooltrueEnables or disables WriteListener
serializebooltrueEnables or disables SerializeListener

Some of these built-in listeners can be enabled/disabled by setting request attributes (for instance in the defaults attribute of an operation):

AttributeTypeDefaultDescription
_api_receivebooltrueEnables or disables ReadListener, DeserializeListener, ValidateListener
_api_respondbooltrueEnables or disables SerializeListener, RespondListener
_api_persistbooltrueEnables or disables WriteListener

# Custom Event Listeners

Registering your own event listeners to add extra logic is convenient.

The ApiPlatform\Symfony\EventListener\EventPriorities class comes with a convenient set of class constants corresponding to commonly used priorities:

ConstantEventPriority
PRE_READkernel.request5
POST_READkernel.request3
PRE_DESERIALIZEkernel.request3
POST_DESERIALIZEkernel.request1
PRE_VALIDATEkernel.view65
POST_VALIDATEkernel.view63
PRE_WRITEkernel.view33
POST_WRITEkernel.view31
PRE_SERIALIZEkernel.view17
POST_SERIALIZEkernel.view15
PRE_RESPONDkernel.view9
POST_RESPONDkernel.response0

In the following example, we will send a mail each time a new book is created using the API:

<?php
// api/src/EventSubscriber/BookMailSubscriber.php

namespace App\EventSubscriber;

use ApiPlatform\Symfony\EventListener\EventPriorities;
use App\Entity\Book;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mailer\MailerInterface;

final class BookMailSubscriber implements EventSubscriberInterface
{
    private $mailer;

    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }

    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::VIEW => ['sendMail', EventPriorities::POST_WRITE],
        ];
    }

    public function sendMail(ViewEvent $event): void
    {
        $book = $event->getControllerResult();
        $method = $event->getRequest()->getMethod();

        if (!$book instanceof Book || Request::METHOD_POST !== $method) {
            return;
        }

        $message = (new Email())
            ->from('[email protected]')
            ->to('[email protected]')
            ->subject('A new book has been added')
            ->text(sprintf('The book #%d has been added.', $book->getId()));

        $this->mailer->send($message);
    }
}

If you use the official API Platform distribution, creating the previous class is enough. The Symfony DependencyInjection component will automatically register this subscriber as a service and will inject its dependencies thanks to the autowiring feature.

Alternatively, the subscriber must be registered manually.

You can also help us improve the documentation of this page.

Made with love by

Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.

Learn more

Copyright © 2023 Kévin Dunglas

Sponsored by Les-Tilleuls.coop