Install the Package
composer require api-platform/symfony
(Note: This is a read-only split of api-platform/core. Core functionality lives in api-platform/core.)
Enable the Bundle
In config/bundles.php, ensure:
return [
// ...
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
];
First Use Case: Auto-Generated REST API
#[ApiResource]:
use ApiPlatform\Metadata\ApiResource;
#[ApiResource]
class Book {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
public ?int $id = null;
#[ORM\Column]
public string $title;
}
php bin/console debug:router to see auto-generated routes (e.g., /api/books, /api/books/{id}).Key Files to Review
config/packages/api_platform.yaml (default config).src/Entity/ (annotate entities with #[ApiResource]).config/routes/api_platform.yaml (custom route overrides).#[ApiResource] on Doctrine/ODM entities to auto-generate CRUD endpoints.
#[ApiResource(
operations: [
new Get(),
new GetCollection(),
new Post(denormalizationContext: ['groups' => ['book:write']]),
new Put(),
new Delete(),
],
normalizationContext: ['groups' => ['book:read']],
paginationItemsPerPage: 30,
)]
class Book { ... }
#[Groups] for serialization control:
use ApiPlatform\Metadata\ApiProperty;
#[ApiProperty(groups: ['book:read'])]
public string $title;
#[ApiProperty(groups: ['book:write'])]
public ?string $author = null;
#[Patch], #[DeleteCollection]).
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
#[ApiResource]
class Book {
#[Operation(
method: 'POST',
uriTemplate: '/books/{id}/publish',
controller: BookPublisher::class,
input: BookPublishInput::class,
)]
public ?int $id = null;
}
// Controller
class BookPublisher implements ProcessorInterface {
public function __invoke(Book $data, Operation $operation, array $uriVariables): Book {
$data->setPublished(true);
// ...
return $data;
}
}
api-platform/graphql:
composer require api-platform/graphql
config/packages/api_platform.yaml:
api_platform:
formats:
jsonld: ['application/ld+json']
jsonapi: ['application/vnd.api+json']
graphql: ['application/graphql']
/api/graphql):
query {
books {
id
title
}
}
SearchFilter, OrderFilter):
# config/packages/api_platform.yaml
api_platform:
formats:
jsonapi: ['application/vnd.api+json']
patch_formats:
jsonapi: ['application/vnd.api+json']
swagger:
versions: [3]
collection:
pagination:
items_per_page: 20
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
class PublishedFilter extends AbstractFilter {
protected function filterProperty(string $property, $value, Iterable $queryBuilder, string $resourceClass, Operation $operation = null, array $context = []): void {
if ($property === 'published' && $value === true) {
$queryBuilder->andWhere('b.published = :val')->setParameter('val', true);
}
}
}
Register in api_platform.yaml:
api_platform:
filters:
published: App\Filter\PublishedFilter
# config/packages/security.yaml
access_control:
- { path: ^/api/books, roles: ROLE_USER }
#[Security] on operations:
#[ApiResource(
operations: [
new Get(security: "is_granted('ROLE_ADMIN')"),
]
)]
#[ApiProperty] or custom serializers:
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
#[ApiResource]
class Book {
#[ApiProperty(
serializerContext: ['groups' => ['book:read']],
denormalizerContext: ['groups' => ['book:write']]
)]
public string $title;
}
ApiPlatform\Serializer\SerializerContextBuilderInterface.Doctrine ORM/ODM:
ApiPlatform\Metadata\ApiResource traits if needed.api-platform/doctrine-orm for full ORM integration.Mercure (Real-Time Updates):
symfony/mercure-bundle and configure in api_platform.yaml:
api_platform:
mercure:
hubs: ['https://your-mercure-hub']
OpenAPI/Swagger:
/api/doc. Customize with #[OpenApi] annotations:
use ApiPlatform\Metadata\OpenApi;
#[ApiResource(
operations: [
new Get(
openapi: new OpenApi(
summary: 'Get a book',
description: 'Returns a single book by ID',
),
),
],
)]
Testing:
ApiPlatform\Bundle\Test\ApiTestCase for functional tests:
use ApiPlatform\Bundle\Test\ApiTestCase;
class BookTest extends ApiTestCase {
public function testGetBooks(): void {
$response = static::createClient()->request('GET', '/api/books');
$this->assertResponseIsSuccessful();
}
}
Read-Only Repository:
api-platform/core. This package is a Symfony-specific split.PHP Version Requirements:
composer.json). Downgrading may break dependencies.Serialization Conflicts:
#[Groups] may not serialize/deserialize as expected.normalizationContext and denormalizationContext are consistent in #[ApiResource].Circular References:
Book ↔ Author).#[ApiProperty(serialize: false)] or configure max_depth in api_platform.yaml:
api_platform:
formats:
jsonld:
max_depth: 3
Doctrine Lifecycle Callbacks:
#[PrePersist], #[PostLoad] may interfere with API Platform’s state processors.#[ApiResource] state options to control lifecycle:
#[ApiResource(
state: [
new ProcessBookState(), // Custom state processor
],
)]
Pagination Quirks:
itemsPerPage in #[ApiResource] may conflict with global config.api_platform.yaml over entity annotations.GraphQL Schema Conflicts:
#[GraphQLName] to override field names or extend the schema via api_platform.graphql.schema.APP_DEBUG=1 in .env to see detailed errorsHow can I help you explore Laravel packages today?