Api Platform conference
Register now
API Platform Conference
September 19-20, 2024 | Lille & online

The international conference on the API Platform Framework

API Platform Conference 2024: meet the best PHP, JavaScript and API experts

Learn more about the event, register for the conference, and get ready for two days of inspiration, ideas, and knowledge-sharing with our incredible lineup of renowned specialists and advocates.

Register now

Validate incoming data

When processing the incoming request, when creating or updating content, API-Platform will validate the incoming content. It will use the Symfony’s validator. API Platform takes care of validating the data sent to the API by the client (usually user data entered through forms). By default, the framework relies on the powerful Symfony Validator Component for this task, but you can replace it with your preferred validation library such as the PHP filter extension if you want to. Validation is called when handling a POST, PATCH, PUT request as follows : graph LR Request –> Deserialization Deserialization –> Validation Validation –> Persister Persister –> Serialization Serialization –> Response In this guide we’re going to use Symfony’s built-in constraints and a custom constraint. Let’s start by shaping our to-be-validated resource:
// src/App/Entity.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
A custom constraint.
use App\Validator\Constraints\MinimalProperties;
use Doctrine\ORM\Mapping as ORM;
Symfony’s built-in constraints
use Symfony\Component\Validator\Constraints as Assert;
 * A product.
class Product
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;
    public string $name;
     * @var string[] Describe the product
    #[ORM\Column(type: 'json')]
    public $properties;
    public function getId(): ?int
        return $this->id;
The MinimalProperties constraint will check that the properties data holds at least two values: description and price. We start by creating the constraint:
// src/App/Validator/Constraints.php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class MinimalProperties extends Constraint
    public $message = 'The product must have the minimal properties required ("description", "price")';
Then the validator following Symfony’s naming conventions
// src/App/Validator/Constraints.php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
final class MinimalPropertiesValidator extends ConstraintValidator
    public function validate($value, Constraint $constraint): void
        if (!\array_key_exists('description', $value) || !\array_key_exists('price', $value)) {

// src/DoctrineMigrations.php
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Migration extends AbstractMigration
    public function up(Schema $schema): void
        $this->addSql('CREATE TABLE product (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, properties CLOB NOT NULL)');

// src/App/Playground.php
namespace App\Playground;
use Symfony\Component\HttpFoundation\Request;
function request(): Request
    return Request::create(
        uri: '/products',
        method: 'POST',
        server: [
            'CONTENT_TYPE' => 'application/ld+json',
            'HTTP_ACCEPT' => 'application/ld+json',
        content: '{"name": "test", "properties": {"description": "Test product"}}'

// src/App/Tests.php
namespace App\Tests;
use ApiPlatform\Playground\Test\TestGuideTrait;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
final class BookTest extends ApiTestCase
    use TestGuideTrait;
    public function testValidation(): void
        $response = static::createClient()->request(method: 'POST', url: '/products', options: [
            'json' => ['name' => 'test', 'properties' => ['description' => 'foo']],
            'headers' => ['content-type' => 'application/ld+json'],

If the data submitted by the client is invalid, the HTTP status code will be set to 422 Unprocessable Entity and the response’s body will contain the list of violations serialized in a format compliant with the requested one. For instance, a validation error will look like the following if the requested format is JSON-LD (the default):

  "@context": "/contexts/ConstraintViolationList",
  "@type": "ConstraintViolationList",
  "hydra:title": "An error occurred",
  "hydra:description": "properties: The product must have the minimal properties required (\"description\", \"price\")",
  "violations": [
      "propertyPath": "properties",
      "message": "The product must have the minimal properties required (\"description\", \"price\")"
            'hydra:description' => 'properties: The product must have the minimal properties required ("description", "price")',
            'title' => 'An error occurred',
            'violations' => [
                ['propertyPath' => 'properties', 'message' => 'The product must have the minimal properties required ("description", "price")'],

You can also help us improve this guide.

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