Understanding the ApiResource Attribute
Understanding the ApiResource Attribute
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Now the ACTUAL work begins: our FIRST real Doctrine entity, Project, becomes a complete API through ONE attribute.
Creating the Project entity
docker compose exec php bin/console make:entity ProjectEXACTLY the same make:entity command from the Symfony course (chapter 19) – fields: name (string, 255), description (text, nullable), createdAt (datetime_immutable).
Adding #[ApiResource]
<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\ProjectRepository;
use Doctrine\ORM\Mapping as ORM;
#[ApiResource]
#[ORM\Entity(repositoryClass: ProjectRepository::class)]
class Project
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $name = '';
#[ORM\Column(type: 'text', nullable: true)]
private ?string $description = null;
#[ORM\Column]
private \DateTimeImmutable $createdAt;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): static
{
$this->description = $description;
return $this;
}
public function getCreatedAt(): \DateTimeImmutable
{
return $this->createdAt;
}
}EXACTLY ONE difference from a normal Symfony entity: the line #[ApiResource]. EVERYTHING ELSE (attributes, getters/setters) is IDENTICAL to the Symfony course.
Creating and applying a migration
docker compose exec php bin/console make:migration
docker compose exec php bin/console doctrine:migrations:migrate --no-interactionEXACTLY the same five-step workflow from chapter 20 of the Symfony course – migrations work UNCHANGED in API Platform, since it's built on COMPLETELY NORMAL Doctrine/Symfony underneath.
What already works now
curl -k https://localhost/api/projects{
"@context": "/api/contexts/Project",
"@id": "/api/projects",
"@type": "hydra:Collection",
"hydra:member": [],
"hydra:totalItems": 0
}An EMPTY list (no projects in the database yet), but a FULLY functioning endpoint – hydra:member is where the actual projects will appear once some exist (chapter 10).
How API Platform derives the endpoint name
/api/projects instead of /api/project: API Platform AUTOMATICALLY pluralizes the class name (Project → projects) for collection endpoints – EXACTLY the same convention as MOST modern REST APIs, including the Magento REST API from our separate tutorials.
Tipp: php bin/console debug:router (EXACTLY as in the Symfony course, chapter 7) ALSO shows the routes AUTOMATICALLY generated by API Platform – useful for checking WHICH endpoints actually exist.