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 Symfony REST : Tutoriel Complet et Questions d'Entretien 2026

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.

bash
composer require api-platform/core

Pour un nouveau projet, API Platform propose une distribution complète incluant Symfony, Docker et des outils de développement préconfigurés :

bash
composer create-project api-platform/api-platform my-api
cd my-api
docker compose up -d

La configuration minimale s'effectue dans le fichier config/packages/api_platform.yaml :

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: 30

Cré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
<?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
<?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
<?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
<?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 :

bash
# 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]=desc

State 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
<?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
<?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
<?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
<?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 ?

php
#[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
<?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.

Défi du jour

Tu saurais repérer le bug en Symfony ?

Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Anthony Fillion-Maillet

Écrit par

Anthony Fillion-Maillet

Fondateur 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