Skip to main content

Adding metadata to an API Platform Input DTO

· 2 min read
Stephan Hochdörfer
Head of IT Business Operations

For API-based projects, we heavily rely on API Platform as the foundation. How can I attach some metadata to my input DTO? That was a task I wanted to solve recently.

The Input DTO in question was a read-only class with two properties set via constructor injection:

<?php

declare(strict_types=1);

namespace App\ApiResource;

use ApiPlatform\Metadata\ApiProperty;

final readonly class MyInput
{
public function __construct(
public string $firstName,
public string $lastName,
) {
}

Adding another property to the constructor would have exposed the property to the API client, which is something I wanted to avoid. The basic idea was that API Platform sets an additional property (e.g., a transaction reference) in the input class so that I can pass the instance to several other methods.

I could have introduced the property, including getter and setter methods, but that felt "odd" given that the existing properties were exposed as read-only properties.

I then experimented with overwriting some API Platform core classes and tweaking the Symfony Serializer component. Both approaches resulted in too much code for such a simple task, so I decided against it.

In the end, I settled on the most obvious solution: setting the property in the class constructor. Since I don't need some complicated logic to generate a transaction reference number, this felt like the best approach to solving the problem.

The resulting DTO class looks like this:

<?php

declare(strict_types=1);

namespace App\ApiResource;

use ApiPlatform\Metadata\ApiProperty;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Uid\UuidV4;

final readonly class MyInput
{
#[ApiProperty(readable: false)]
public UuidV4 $transactionReference;

public function __construct(
public string $firstName,
public string $lastName,
) {
$this->transactionReference = Uuid::v4();
}
}

Thanks to the #[ApiProperty(readable: false)] attribute, the $transactionReference property is not exposed in the API docs. And thanks to Symfony's UID component, generating the $transactionReference was a one-liner:

$this->transactionReference = Uuid::v4();

Once again, this is a perfect example to show that simple problems can be solved with simple solutions.