API Platform Symfony REST : Tutoriel Complet et Questions d'Entretien 2026
Maîtrisez API Platform avec Symfony pour créer des APIs REST performantes. Guide complet avec installation, configuration, bonnes pratiques et questions d'entretien technique.

API Platform s'est imposé comme le framework de référence pour développer des APIs REST professionnelles avec Symfony. Cette solution offre une productivité exceptionnelle tout en respectant les standards du web moderne. Ce tutoriel couvre l'installation, la configuration avancée et les concepts essentiels pour réussir en entretien technique.
API Platform génère automatiquement la documentation OpenAPI, le support JSON-LD/Hydra et GraphQL. Ces fonctionnalités intégrées accélèrent considérablement le développement d'APIs conformes aux standards.
Installation d'API Platform avec Symfony
L'installation d'API Platform dans un projet Symfony existant se réalise via Composer. Le bundle s'intègre parfaitement avec l'écosystème Symfony et Doctrine.
composer require api-platform/corePour un nouveau projet, API Platform propose une distribution complète incluant Symfony, Docker et des outils de développement préconfigurés :
composer create-project api-platform/api-platform my-api
cd my-api
docker compose up -dLa configuration minimale s'effectue dans le fichier config/packages/api_platform.yaml :
api_platform:
title: 'Mon API REST'
version: '1.0.0'
formats:
jsonld: ['application/ld+json']
json: ['application/json']
docs_formats:
jsonld: ['application/ld+json']
jsonopenapi: ['application/vnd.openapi+json']
html: ['text/html']
defaults:
pagination_enabled: true
pagination_items_per_page: 30Création d'une Ressource API
La transformation d'une entité Doctrine en ressource API s'effectue avec l'attribut #[ApiResource]. API Platform génère automatiquement les opérations CRUD standard.
<?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 Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ApiResource(
operations: [
new GetCollection(),
new Post(),
new Get(),
new Put(),
new Delete()
],
paginationItemsPerPage: 20
)]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Assert\Length(min: 3, max: 255)]
private string $name;
#[ORM\Column(type: 'text')]
private string $description;
#[ORM\Column(type: 'decimal', precision: 10, scale: 2)]
#[Assert\Positive]
private string $price;
#[ORM\Column]
private \DateTimeImmutable $createdAt;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
// Getters et setters...
}Groupes de Sérialisation et DTO
Le contrôle précis des données exposées s'effectue via les groupes de sérialisation. Cette approche permet de différencier les champs visibles selon le contexte.
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Symfony\Component\Serializer\Annotation\Groups;
#[ApiResource(
operations: [
new GetCollection(normalizationContext: ['groups' => ['product:list']]),
new Get(normalizationContext: ['groups' => ['product:read']]),
new Post(denormalizationContext: ['groups' => ['product:write']])
]
)]
class Product
{
#[Groups(['product:list', 'product:read'])]
private ?int $id = null;
#[Groups(['product:list', 'product:read', 'product:write'])]
private string $name;
#[Groups(['product:read', 'product:write'])]
private string $description;
#[Groups(['product:list', 'product:read', 'product:write'])]
private string $price;
#[Groups(['product:read'])]
private \DateTimeImmutable $createdAt;
}Pour les cas complexes, les DTO (Data Transfer Objects) offrent une séparation nette entre la représentation API et le modèle métier :
<?php
namespace App\Dto;
use Symfony\Component\Validator\Constraints as Assert;
final class CreateProductInput
{
#[Assert\NotBlank]
#[Assert\Length(min: 3, max: 255)]
public string $name;
#[Assert\NotBlank]
public string $description;
#[Assert\Positive]
public float $price;
#[Assert\NotBlank]
public string $categoryId;
}Filtres et Recherche Avancée
API Platform intègre un système de filtres puissant pour les opérations de collection. Les filtres s'appliquent directement sur les entités.
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\RangeFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
'name' => 'partial',
'category.name' => 'exact'
])]
#[ApiFilter(RangeFilter::class, properties: ['price'])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'price', 'createdAt'])]
#[ApiFilter(DateFilter::class, properties: ['createdAt'])]
class Product
{
// Propriétés...
}Les requêtes HTTP utilisent ces filtres via les paramètres de query string :
# Recherche partielle sur le nom
GET /api/products?name=smartphone
# Filtrage par plage de prix
GET /api/products?price[gte]=100&price[lte]=500
# Tri par prix décroissant
GET /api/products?order[price]=desc
# Combinaison de filtres
GET /api/products?name=phone&price[gte]=200&order[createdAt]=descState Providers et State Processors
Les State Providers permettent de personnaliser la récupération des données, tandis que les State Processors contrôlent les opérations d'écriture.
<?php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\ProductRepository;
final class ProductStateProvider implements ProviderInterface
{
public function __construct(
private ProductRepository $repository
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if (isset($uriVariables['id'])) {
return $this->repository->findActiveById($uriVariables['id']);
}
return $this->repository->findAllActive();
}
}Le State Processor gère la logique métier lors des créations et modifications :
<?php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Product;
use App\Service\ProductService;
use App\Service\NotificationService;
final class ProductStateProcessor implements ProcessorInterface
{
public function __construct(
private ProcessorInterface $persistProcessor,
private ProductService $productService,
private NotificationService $notificationService
) {}
public function process(
mixed $data,
Operation $operation,
array $uriVariables = [],
array $context = []
): Product {
$this->productService->calculateFinalPrice($data);
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
if ($operation instanceof Post) {
$this->notificationService->notifyNewProduct($result);
}
return $result;
}
}Sécurité et Contrôle d'Accès
API Platform s'intègre avec le composant Security de Symfony pour gérer l'authentification et les autorisations.
<?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;
#[ApiResource(
operations: [
new GetCollection(),
new Get(),
new Post(security: "is_granted('ROLE_ADMIN')"),
new Put(security: "is_granted('ROLE_ADMIN') or object.owner == user"),
new Delete(security: "is_granted('ROLE_ADMIN')")
]
)]
class Product
{
// ...
}Pour une sécurité au niveau des champs, les groupes de sérialisation combinés aux voters offrent un contrôle granulaire :
<?php
namespace App\Serializer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class ProductNormalizer implements NormalizerInterface
{
public function __construct(
private NormalizerInterface $normalizer,
private AuthorizationCheckerInterface $authChecker
) {}
public function normalize($object, ?string $format = null, array $context = []): array
{
$data = $this->normalizer->normalize($object, $format, $context);
if (!$this->authChecker->isGranted('ROLE_ADMIN')) {
unset($data['costPrice'], $data['margin']);
}
return $data;
}
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
{
return $data instanceof Product;
}
}Prêt à réussir tes entretiens Symfony ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Questions d'Entretien API Platform
Les entretiens techniques sur API Platform évaluent la compréhension des concepts fondamentaux et des bonnes pratiques.
Question : Quelle différence entre State Provider et State Processor ?
Le State Provider récupère les données (opérations GET), tandis que le State Processor les modifie (POST, PUT, PATCH, DELETE). Cette séparation respecte le principe de responsabilité unique.
Question : Comment implémenter la pagination personnalisée ?
#[ApiResource(
paginationEnabled: true,
paginationItemsPerPage: 20,
paginationMaximumItemsPerPage: 100,
paginationClientEnabled: true,
paginationClientItemsPerPage: true
)]Question : Comment gérer les relations dans API Platform ?
Les relations utilisent les IRI (Internationalized Resource Identifiers). Pour embarquer les données liées, il faut configurer les groupes de sérialisation appropriés ou utiliser l'option fetchEager.
Question : Quelle stratégie pour le versioning d'API ?
API Platform supporte plusieurs approches : versioning par URL (/api/v1/products), par header (Accept: application/vnd.api+json;version=1) ou par query parameter. La configuration s'effectue via les opérations personnalisées.
Question : Comment optimiser les performances avec API Platform ?
Les optimisations incluent : eager loading des relations, utilisation du cache HTTP intégré, pagination appropriée, et serialization groups ciblés pour réduire la taille des réponses.
Tests Fonctionnels API
Les tests API utilisent le client HTTP de Symfony combiné aux assertions spécifiques d'API Platform.
<?php
namespace App\Tests\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Product;
use App\Factory\ProductFactory;
final class ProductApiTest extends ApiTestCase
{
public function testGetCollection(): void
{
ProductFactory::createMany(30);
$response = static::createClient()->request('GET', '/api/products');
$this->assertResponseIsSuccessful();
$this->assertJsonContains([
'@context' => '/api/contexts/Product',
'@type' => 'Collection',
'totalItems' => 30
]);
$this->assertCount(20, $response->toArray()['member']);
}
public function testCreateProduct(): void
{
$response = static::createClient()->request('POST', '/api/products', [
'json' => [
'name' => 'Nouveau Produit',
'description' => 'Description du produit',
'price' => '99.99'
],
'headers' => ['Content-Type' => 'application/ld+json']
]);
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains([
'@type' => 'Product',
'name' => 'Nouveau Produit'
]);
}
public function testFilterByPrice(): void
{
ProductFactory::createOne(['price' => '50.00']);
ProductFactory::createOne(['price' => '150.00']);
ProductFactory::createOne(['price' => '250.00']);
$response = static::createClient()->request(
'GET',
'/api/products?price[gte]=100&price[lte]=200'
);
$this->assertResponseIsSuccessful();
$this->assertCount(1, $response->toArray()['member']);
}
}Conclusion
API Platform représente une solution mature et performante pour développer des APIs REST avec Symfony. La maîtrise des concepts de ressources, filtres, state providers/processors et sécurité permet de construire des APIs robustes et maintenables. Les questions d'entretien se concentrent généralement sur ces fondamentaux ainsi que sur les bonnes pratiques de conception d'APIs RESTful.
Tu saurais repérer le bug en Symfony ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 25 août 2026
Partager
Articles similaires

Sécurité API REST Symfony en 2026 : OAuth2, Rate Limiting et Questions d'Entretien
Guide complet sur la securisation des API REST Symfony avec OAuth2, rate limiting, JWT et Voters. Inclut des questions d'entretien technique.

API Platform avec Symfony en 2026 : Architecture, State Providers et Questions d'Entretien
Maîtriser API Platform 4.2 avec Symfony : State Providers, Processors, Object Mapper, JSON Streamer pour des performances optimales, et questions d'entretien technique pour développeurs confirmés.

API Platform GraphQL avec Symfony : Schémas, Mutations et Questions d'Entretien 2026
Maîtrisez l'intégration de GraphQL dans Symfony avec API Platform. Ce guide couvre les schémas, mutations, résolveurs personnalisés et les questions d'entretien technique les plus posées en 2026.