API Platform + Symfony : creer une API REST/GraphQL
API Platform est le framework API le plus puissant de l'ecosysteme PHP. Couple a Symfony 7.2, il permet de creer une API REST complete avec documentation OpenAPI, pagination, filtres et validation en quelques minutes - puis d'ajouter GraphQL avec un seul paquet. Voici comment l'utiliser concretement, au-dela du tutorial "Hello World".
Installation et configuration
API Platform s'installe via Composer et s'integre automatiquement dans Symfony grace a Flex :
composer require api
# Pour le support GraphQL (optionnel)
composer require api-platform/graphql
La recette Flex configure tout automatiquement : le routing, la serialisation, la documentation. Votre API est immediatement accessible sur /api avec une interface Swagger UI.
Definir une ressource API
La force d'API Platform reside dans la declaration des ressources directement sur les entites Doctrine via des attributs PHP :
// src/Entity/Article.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ApiResource(
operations: [
new GetCollection(normalizationContext: ['groups' => ['article:list']]),
new Get(normalizationContext: ['groups' => ['article:read']]),
new Post(security: "is_granted('ROLE_EDITOR')"),
new Put(security: "is_granted('ROLE_ADMIN') or object.author == user"),
new Delete(security: "is_granted('ROLE_ADMIN')"),
],
paginationItemsPerPage: 20,
order: ['publishedAt' => 'DESC'],
)]
#[ApiFilter(SearchFilter::class, properties: [
'title' => 'partial',
'author.name' => 'exact',
'category' => 'exact',
])]
#[ApiFilter(OrderFilter::class, properties: ['publishedAt', 'title'])]
#[ApiFilter(DateFilter::class, properties: ['publishedAt'])]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['article:list', 'article:read'])]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Assert\Length(min: 5, max: 255)]
#[Groups(['article:list', 'article:read'])]
private string $title = '';
#[ORM\Column(type: 'text')]
#[Assert\NotBlank]
#[Groups(['article:read'])]
private string $content = '';
#[ORM\Column(type: 'datetime_immutable')]
#[Groups(['article:list', 'article:read'])]
private \DateTimeImmutable $publishedAt;
#[ORM\ManyToOne(targetEntity: User::class)]
#[Groups(['article:read'])]
public ?User $author = null;
// Getters et setters...
}
Avec cette seule entite, API Platform genere automatiquement 5 endpoints REST (GET /api/articles, GET /api/articles/{id}, POST, PUT, DELETE), les filtres de recherche, le tri, la pagination et la documentation OpenAPI complete.
Groupes de serialisation
Les groupes de serialisation controlent quelles proprietes sont exposees selon l'operation. Dans l'exemple ci-dessus, le listing (article:list) retourne l'id, le titre et la date, mais pas le contenu complet. La vue detaillee (article:read) inclut tout. C'est crucial pour les performances - ne retournez jamais toutes les donnees sur un listing.
Pour aller plus loin avec la serialisation Symfony, consultez le guide complet du Serializer qui couvre les normaliseurs personnalises et les contextes avances.
Filtres et pagination
API Platform integre une dizaine de filtres Doctrine prets a l'emploi :
# Exemples de requetes avec filtres
GET /api/articles?title=symfony # Recherche partielle
GET /api/articles?author.name=Pierre # Filtre sur une relation
GET /api/articles?order[publishedAt]=desc # Tri
GET /api/articles?publishedAt[after]=2026-01-01 # Filtre par date
GET /api/articles?page=2 # Pagination
La pagination est automatique. Par defaut, elle utilise le format Hydra (JSON-LD) qui inclut les liens hydra:first, hydra:next, hydra:last pour la navigation. Vous pouvez aussi utiliser le format cursor-based pour de meilleures performances sur les grandes collections.
Operations personnalisees
Pour les operations qui ne rentrent pas dans le CRUD standard, creez des operations personnalisees avec un controller ou un State Processor :
// src/State/PublishArticleProcessor.php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Article;
class PublishArticleProcessor implements ProcessorInterface
{
public function __construct(
private ProcessorInterface $persistProcessor,
) {}
/**
* @param Article $data
*/
public function process(
mixed $data,
Operation $operation,
array $uriVariables = [],
array $context = [],
): Article {
$data->setPublishedAt(new \DateTimeImmutable());
$data->setStatus('published');
return $this->persistProcessor->process(
$data, $operation, $uriVariables, $context
);
}
}
Puis declarez l'operation sur l'entite :
#[ApiResource(
operations: [
// ... operations standard
new Post(
uriTemplate: '/articles/{id}/publish',
processor: PublishArticleProcessor::class,
security: "is_granted('ROLE_EDITOR')",
),
],
)]
Support GraphQL
Si vous avez installe api-platform/graphql, toutes vos ressources API sont automatiquement accessibles via GraphQL sur /graphql :
# Query GraphQL
{
articles(first: 10, order: { publishedAt: "DESC" }) {
edges {
node {
id
title
publishedAt
author {
name
}
}
}
totalCount
}
}
# Mutation GraphQL
mutation {
createArticle(input: {
title: "Mon article",
content: "Le contenu..."
}) {
article {
id
title
}
}
}
Les filtres, la pagination et les regles de securite definies sur vos ressources s'appliquent identiquement en REST et en GraphQL. Vous n'avez rien a reconfigurer.
Authentification JWT
Pour securiser votre API, le standard est JWT via le bundle LexikJWT :
# Installation
composer require lexik/jwt-authentication-bundle
# Generer les cles RSA
php bin/console lexik:jwt:generate-keypair
Configuration du firewall dans config/packages/security.yaml :
# config/packages/security.yaml
security:
firewalls:
api:
pattern: ^/api
stateless: true
jwt: ~
access_control:
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Les attributs security sur chaque operation affinent le controle d'acces au niveau de la ressource. Pour une couverture complete de la securite Symfony, consultez l'article sur l'authentification et l'autorisation.
Documentation OpenAPI
API Platform genere une documentation OpenAPI 3.1 complete automatiquement. Elle inclut les schemas des entites, les parametres de filtres, les codes de reponse et les exemples. Vous pouvez la personnaliser via des attributs :
#[ApiResource(
description: 'Gestion des articles du blog',
extraProperties: [
'standard_put' => true,
],
)]
La documentation est servie par Swagger UI sur /api et exportable en JSON sur /api/docs.json. C'est un outil precieux pour les developpeurs front-end qui consomment votre API.
State Providers : sources de donnees alternatives
API Platform n'est pas lie a Doctrine. Vous pouvez connecter n'importe quelle source de donnees en implementant un State Provider :
// src/State/ExternalArticleProvider.php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class ExternalArticleProvider implements ProviderInterface
{
public function __construct(
private HttpClientInterface $httpClient,
) {}
public function provide(
Operation $operation,
array $uriVariables = [],
array $context = [],
): iterable|object|null {
$response = $this->httpClient->request('GET', 'https://api.externe.com/articles');
return $response->toArray();
}
}
Pour les appels a des APIs externes, le guide du HTTP Client Symfony detaille les bonnes pratiques de consommation d'APIs tierces.
Bonnes pratiques
- Groupes de serialisation - definissez toujours des groupes, ne retournez jamais toutes les proprietes
- Pagination - limitez le nombre d'items par page (20-50 maximum)
- Validation - utilisez les contraintes Symfony, API Platform les execute automatiquement
- Versionning - preferez le versionning par header (
Accept: application/vnd.api.v2+json) au versionning par URL - Cache HTTP - activez les en-tetes
Cache-ControletETagpour les operations GET - Securite - appliquez le principe du moindre privilege sur chaque operation
Aller plus loin
API Platform est un ecosysteme complet qui merite d'etre explore en profondeur. Cet article couvre les bases solides pour demarrer un projet d'API serieuse. Pour les sujets connexes, consultez le guide du Serializer, le HTTP Client pour consommer des APIs, et la securite Symfony.
Vous avez un projet d'API a construire avec Symfony et API Platform ? Consultez mes services de developpement, mes tarifs et contactez-moi. En tant que developpeur freelance en France et Belgique, je concois des APIs robustes et bien documentees.
