API Platform con Symfony nel 2026: Architettura e Domande per Colloqui Tecnici
Guida completa ad API Platform con Symfony nel 2026. Architettura REST API, State Providers, Processors e domande frequenti nei colloqui per sviluppatori Symfony.

API Platform rappresenta il framework di riferimento per lo sviluppo di API REST e GraphQL con Symfony. Nel 2026, la versione attuale offre funzionalità avanzate come State Providers, State Processors e documentazione OpenAPI estesa. Questo articolo esplora l'architettura moderna di API Platform e prepara gli sviluppatori ai colloqui tecnici.
API Platform 4.x richiede Symfony 7.x e PHP 8.3+. Gli esempi presentati utilizzano le ultime funzionalità e le best practice per API pronte per la produzione.
Installazione e Configurazione del Progetto
La creazione di un nuovo progetto API Platform avviene tramite Composer. Il framework si integra perfettamente con applicazioni Symfony esistenti.
composer create-project api-platform/api-platform my-api
cd my-api
composer require api-platform/coreLa configurazione base in config/packages/api_platform.yaml definisce i parametri essenziali:
api_platform:
title: "My API"
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:
stateless: true
cache_headers:
vary: ["Content-Type", "Authorization", "Origin"]
extra_properties:
standard_put: true
rfc_7807_compliant_errors: trueDefinizione delle Entità come Risorse API
API Platform utilizza attributi PHP per configurare le risorse API. Un'entità tipica combina il mapping Doctrine con le definizioni API.
<?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 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')")
],
paginationItemsPerPage: 20,
order: ["createdAt" => "DESC"]
)]
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: "decimal", precision: 10, scale: 2)]
#[Assert\Positive]
private string $price;
#[ORM\Column]
private \DateTimeImmutable $createdAt;
#[ORM\ManyToOne(targetEntity: User::class)]
private ?User $owner = null;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
// Getters and setters...
}State Providers per Logica Dati Personalizzata
Gli State Providers sostituiscono i precedenti Data Providers e offrono maggiore flessibilità nel recupero dei dati. Permettono l'integrazione di sorgenti dati esterne o logica di business complessa.
<?php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Entity\Product;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;
class ProductStateProvider implements ProviderInterface
{
public function __construct(
private ProductRepository $repository,
private RequestStack $requestStack
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
$request = $this->requestStack->getCurrentRequest();
if ($operation instanceof GetCollection) {
$category = $request?->query->get('category');
if ($category) {
return $this->repository->findByCategory($category);
}
return $this->repository->findAllActive();
}
return $this->repository->find($uriVariables['id']);
}
}La registrazione avviene tramite l'attributo provider:
#[ApiResource(
provider: ProductStateProvider::class
)]
class Product
{
// ...
}State Processors per Operazioni di Scrittura
Gli State Processors gestiscono le richieste POST, PUT, PATCH e DELETE. Incapsulano la logica di business per le modifiche dei dati.
<?php
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
class ProductStateProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $entityManager,
private MailerInterface $mailer
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Product
{
if ($data instanceof Product && $operation instanceof Post) {
$data->setCreatedAt(new \DateTimeImmutable());
$this->entityManager->persist($data);
$this->entityManager->flush();
$this->sendNotification($data);
return $data;
}
$this->entityManager->flush();
return $data;
}
private function sendNotification(Product $product): void
{
$email = (new Email())
->to('admin@example.com')
->subject('New Product Created')
->text(sprintf('Product %s was created.', $product->getName()));
$this->mailer->send($email);
}
}DTO e Trasformazioni Input/Output
I Data Transfer Objects permettono la separazione tra rappresentazione API ed entità interne. Questa architettura aumenta flessibilità e sicurezza.
<?php
namespace App\Dto;
use Symfony\Component\Validator\Constraints as Assert;
class CreateProductInput
{
#[Assert\NotBlank]
#[Assert\Length(min: 3, max: 255)]
public string $name;
#[Assert\NotBlank]
#[Assert\Positive]
public float $price;
#[Assert\NotBlank]
public string $category;
public ?string $description = null;
}<?php
namespace App\Dto;
class ProductOutput
{
public int $id;
public string $name;
public float $price;
public string $formattedPrice;
public string $category;
public string $createdAt;
}La configurazione della trasformazione avviene nella definizione della risorsa:
#[ApiResource(
operations: [
new Post(
input: CreateProductInput::class,
output: ProductOutput::class,
processor: CreateProductProcessor::class
)
]
)]
class Product
{
// ...
}Filtri e Ordinamento
API Platform offre filtri dichiarativi per pattern di query comuni. La configurazione avviene direttamente sull'entità.
<?php
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\RangeFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Metadata\ApiFilter;
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
'name' => 'partial',
'category.name' => 'exact'
])]
#[ApiFilter(RangeFilter::class, properties: ['price'])]
#[ApiFilter(DateFilter::class, properties: ['createdAt'])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'price', 'createdAt'])]
class Product
{
// ...
}Un esempio di richiesta API con filtri:
GET /api/products?name=laptop&price[gte]=500&order[price]=ascAutenticazione e Autorizzazione
La configurazione della sicurezza utilizza il componente Security di Symfony. API Platform integra Voter ed expressions in modo trasparente.
<?php
namespace App\Security\Voter;
use App\Entity\Product;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class ProductVoter extends Voter
{
public const EDIT = 'PRODUCT_EDIT';
public const DELETE = 'PRODUCT_DELETE';
protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, [self::EDIT, self::DELETE])
&& $subject instanceof Product;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
/** @var Product $product */
$product = $subject;
return match($attribute) {
self::EDIT => $this->canEdit($product, $user),
self::DELETE => $this->canDelete($product, $user),
default => false,
};
}
private function canEdit(Product $product, UserInterface $user): bool
{
return $product->getOwner() === $user || in_array('ROLE_ADMIN', $user->getRoles());
}
private function canDelete(Product $product, UserInterface $user): bool
{
return in_array('ROLE_ADMIN', $user->getRoles());
}
}Versionamento delle API
API Platform supporta diverse strategie di versionamento. Il versionamento basato su URL risulta essere il più diffuso.
#[ApiResource(
routePrefix: '/v1',
operations: [
new GetCollection(),
new Get()
]
)]
class Product
{
// ...
}
#[ApiResource(
routePrefix: '/v2',
operations: [
new GetCollection(),
new Get()
],
normalizationContext: ['groups' => ['product:read:v2']]
)]
class ProductV2
{
// ...
}Domande Frequenti nei Colloqui
Domanda: Qual è la differenza tra State Provider e State Processor?
Gli State Providers gestiscono le operazioni di lettura (richieste GET) e recuperano dati da qualsiasi sorgente. Gli State Processors elaborano le operazioni di scrittura (POST, PUT, PATCH, DELETE) e implementano la logica di persistenza ed eventuali effetti collaterali come le notifiche.
Domanda: Come si implementa la paginazione in API Platform?
API Platform offre paginazione automatica. La configurazione può essere globale o per singola risorsa:
#[ApiResource(
paginationEnabled: true,
paginationItemsPerPage: 30,
paginationMaximumItemsPerPage: 100,
paginationClientEnabled: true
)]Domanda: Quali gruppi di serializzazione sono consigliati?
Le best practice raccomandano gruppi separati per lettura e scrittura:
#[ApiResource(
normalizationContext: ['groups' => ['product:read']],
denormalizationContext: ['groups' => ['product:write']]
)]Domanda: Come si testano gli endpoint di API Platform?
<?php
namespace App\Tests\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Product;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;
class ProductTest extends ApiTestCase
{
use RefreshDatabaseTrait;
public function testGetCollection(): void
{
$response = static::createClient()->request('GET', '/api/products');
$this->assertResponseIsSuccessful();
$this->assertJsonContains(['@type' => 'hydra:Collection']);
}
public function testCreateProduct(): void
{
$response = static::createClient()->request('POST', '/api/products', [
'json' => [
'name' => 'Test Product',
'price' => '29.99'
],
'headers' => [
'Authorization' => 'Bearer ' . $this->getToken()
]
]);
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains(['name' => 'Test Product']);
}
}Pronto a superare i tuoi colloqui su Symfony?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Conclusione
API Platform nel 2026 offre un'architettura matura per lo sviluppo di API REST con Symfony. State Providers e Processors permettono pattern di accesso ai dati flessibili, mentre i DTO garantiscono una separazione pulita tra API e logica di dominio. L'integrazione di filtri, sicurezza e documentazione automatica rende API Platform la scelta principale per lo sviluppo professionale di API. I concetti e le domande trattate preparano al meglio gli sviluppatori per i colloqui tecnici e trasmettono le best practice per implementazioni pronte per la produzione.
Sapresti trovare il bug in Symfony?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 8 settembre 2026
Condividi
Articoli correlati

Sicurezza REST API Symfony nel 2026: OAuth2, Rate Limiting e Domande per Colloqui
Guida completa alla protezione delle REST API Symfony con OAuth2 Token Introspection, componente RateLimiter, Voters e best practice di sicurezza.

API Platform GraphQL con Symfony: Schema, Mutation e Domande da Colloquio 2026
Guida completa ad API Platform GraphQL con Symfony: generazione schema, query, mutation, resolver personalizzati, sicurezza e domande tecniche per colloqui 2026.

Sicurezza REST API Symfony: Autenticazione JWT e Best Practice 2026
Guida completa alla protezione delle REST API Symfony con autenticazione JWT, LexikJWTAuthenticationBundle e pratiche di sicurezza per applicazioni production-ready.