API Platform com Symfony em 2026: Arquitetura, State Providers e Perguntas de Entrevista
Dominar API Platform 4.2 com Symfony: State Providers, Processors, Object Mapper, JSON Streamer para otimização de performance, e perguntas de entrevista técnica para desenvolvedores seniores.

API Platform 4.2 transforma a maneira como aplicações Symfony expõem APIs REST e GraphQL. Esta versão introduz o Symfony Object Mapper para separação limpa de recursos, o JSON Streamer para ganhos significativos de performance, e um sistema de filtros redesenhado. Para desenvolvedores que se preparam para entrevistas técnicas, compreender esses padrões arquiteturais distingue os perfis seniores dos juniores.
API Platform 4.2 requer Symfony 7.4 ou 8.0. O suporte para Symfony 6.4 e 7.0-7.3 foi descontinuado. O JSON Streamer oferece até 32% mais requisições por segundo em endpoints de coleção.
Configurando API Platform 4.2 com Symfony
API Platform se instala através do Symfony Flex com configuração automática. A configuração padrão lida com a maioria dos casos de uso enquanto permanece totalmente personalizável para requisitos de domínio complexos.
# Install API Platform
composer require api-platform/symfony
# The API documentation is available at /api/
# Open http://localhost:8000/api/ after starting the server
symfony serveA receita Flex configura grupos de serialização, integração com Doctrine e geração de documentação OpenAPI. Os recursos API expõem operações CRUD adicionando um único atributo às classes de entidade.
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')"),
new Delete(security: "is_granted('ROLE_ADMIN')")
],
normalizationContext: ['groups' => ['book:read']],
denormalizationContext: ['groups' => ['book:write']]
)]
class Book
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Groups(['book:read', 'book:write'])]
private string $title;
#[ORM\Column(type: 'text')]
#[Groups(['book:read', 'book:write'])]
private string $description;
#[ORM\Column]
#[Groups(['book:read'])]
private \DateTimeImmutable $createdAt;
// Getters and setters...
}Essa configuração gera cinco endpoints com validação automática, serialização e documentação OpenAPI.
State Providers: Obtendo Dados de Qualquer Fonte
Os State Providers controlam como o API Platform recupera dados para operações GET. O provider Doctrine padrão lida com a recuperação de entidades, mas providers personalizados permitem integração com APIs externas, Elasticsearch ou dados em cache.
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\BookRepository;
use Psr\Cache\CacheItemPoolInterface;
final class BookStateProvider implements ProviderInterface
{
public function __construct(
private BookRepository $repository,
private CacheItemPoolInterface $cache
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
// Single item retrieval
if (isset($uriVariables['id'])) {
$cacheKey = sprintf('book_%d', $uriVariables['id']);
$item = $this->cache->getItem($cacheKey);
if ($item->isHit()) {
return $item->get();
}
$book = $this->repository->find($uriVariables['id']);
$item->set($book)->expiresAfter(3600);
$this->cache->save($item);
return $book;
}
// Collection retrieval with custom filtering
return $this->repository->findActiveBooks();
}
}O registro do provider em operações específicas é feito assim:
#[ApiResource(
operations: [
new GetCollection(provider: BookStateProvider::class),
new Get(provider: BookStateProvider::class),
// Other operations use default Doctrine provider
new Post(),
new Put(),
]
)]
class Book { /* ... */ }State Processors: Lidando com Mutações e Lógica de Negócio
Os State Processors lidam com operações POST, PUT, PATCH e DELETE. Eles recebem dados deserializados e aplicam lógica de negócio antes da persistência.
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Book;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
final class BookStateProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private MailerInterface $mailer,
private ProcessorInterface $persistProcessor // Decorated Doctrine processor
) {}
public function process(
mixed $data,
Operation $operation,
array $uriVariables = [],
array $context = []
): mixed {
// Pre-persist business logic
if ($data instanceof Book && $operation instanceof Post) {
$data->setCreatedAt(new \DateTimeImmutable());
$data->setSlug($this->generateSlug($data->getTitle()));
}
// Delegate to Doctrine processor
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
// Post-persist notification
if ($operation instanceof Post) {
$this->notifyNewBook($data);
}
return $result;
}
private function generateSlug(string $title): string
{
return strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $title));
}
private function notifyNewBook(Book $book): void
{
$email = (new Email())
->to('catalog@example.com')
->subject('New book added: ' . $book->getTitle())
->text('A new book has been added to the catalog.');
$this->mailer->send($email);
}
}Decorar o processor Doctrine padrão com #[AsDecorator] preserva o comportamento de persistência enquanto adiciona lógica personalizada. Esse padrão evita duplicar operações ORM.
Object Mapper: Separando Recursos API de Entidades
API Platform 4.2 integra o componente Symfony Object Mapper para desacoplar as representações API das entidades de domínio. Essa separação permite diferentes modelos de leitura/escrita e protege as estruturas internas das entidades.
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use App\Entity\Book;
use Symfony\Component\ObjectMapper\Attribute\Map;
#[ApiResource(
shortName: 'Book',
operations: [
new GetCollection(),
new Get()
]
)]
#[Map(target: Book::class)]
class BookResource
{
public ?int $id = null;
public string $title;
public string $description;
// Computed field not in entity
public int $wordCount;
// Formatted date for API consumers
public string $publishedDate;
}O provider mapper transforma entidades em recursos automaticamente:
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookResource;
use App\Repository\BookRepository;
use Symfony\Component\ObjectMapper\ObjectMapperInterface;
final class BookResourceProvider implements ProviderInterface
{
public function __construct(
private BookRepository $repository,
private ObjectMapperInterface $mapper
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if (isset($uriVariables['id'])) {
$book = $this->repository->find($uriVariables['id']);
return $book ? $this->toResource($book) : null;
}
return array_map(
fn(Book $book) => $this->toResource($book),
$this->repository->findAll()
);
}
private function toResource(Book $book): BookResource
{
$resource = $this->mapper->map($book, BookResource::class);
$resource->wordCount = str_word_count($book->getDescription());
$resource->publishedDate = $book->getCreatedAt()->format('F j, Y');
return $resource;
}
}Pronto para mandar bem nas entrevistas de Symfony?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
JSON Streamer: Melhoria de Performance de 32%
O componente JSON Streamer serializa grandes coleções sem carregar conjuntos de dados inteiros em memória. Os benchmarks na API do Sylius mostraram um aumento de 32,4% em requisições por segundo.
Habilitação do streaming a nível de recurso ou operação:
#[ApiResource(
operations: [
new GetCollection(
jsonStream: true, // Enable JSON streaming
paginationItemsPerPage: 100
),
new Get()
]
)]
class Book { /* ... */ }O streaming beneficia particularmente:
- Endpoints de coleção com mais de 50 itens
- Recursos com relacionamentos aninhados
- APIs servindo clientes móveis com largura de banda limitada
A especificação OpenAPI também foi otimizada. A mutualização de JSON Schema reduz o tamanho do arquivo em 30%, melhorando os tempos de carregamento da documentação.
Perguntas de Entrevista: Arquitetura API Platform
Entrevistas técnicas para posições Symfony frequentemente cobrem padrões do API Platform. Essas perguntas avaliam a compreensão da arquitetura do framework além das operações CRUD básicas.
"Explique a diferença entre State Providers e Processors"
Resposta esperada: State Providers obtêm dados para operações de leitura (GET). Eles retornam entidades, DTOs ou arrays. State Processors lidam com operações de escrita (POST, PUT, PATCH, DELETE). Eles recebem entrada deserializada e executam lógica de negócio antes da persistência. A separação segue princípios CQRS: consultas através de Providers, comandos através de Processors.
"Quando utilizar um recurso API personalizado em vez de expor uma entidade diretamente?"
Resposta esperada: Recursos personalizados se aplicam quando:
- A representação API difere do esquema do banco de dados
- Campos calculados requerem agregação de múltiplas entidades
- Os modelos de escrita e leitura precisam de estruturas diferentes
- Campos internos das entidades devem permanecer ocultos dos consumidores da API
- A compatibilidade de versão requer contratos estáveis enquanto as entidades evoluem
"Como o API Platform lida com validação?"
Resposta esperada: API Platform usa constraints do Symfony Validator nas propriedades das entidades. A validação é executada automaticamente durante a deserialização antes do State Processor executar. Grupos de validação controlam quais constraints se aplicam por operação. Validadores personalizados se integram através de mecanismos padrão do Symfony.
#[ApiResource(
operations: [
new Post(validationContext: ['groups' => ['create']]),
new Put(validationContext: ['groups' => ['update']])
]
)]
class Book
{
#[Assert\NotBlank(groups: ['create', 'update'])]
private string $title;
#[Assert\Isbn(groups: ['create'])]
private string $isbn; // Required only on creation
}Candidatos frequentemente descrevem a validação como "automática" sem mencionar grupos de validação ou constraints personalizadas. Entrevistadores procuram compreensão de como personalizar a validação por operação.
"Quais mecanismos de segurança o API Platform fornece?"
Resposta esperada: API Platform se integra com Symfony Security através de:
- Atributo
securityem operações para controle de acesso baseado em roles securityPostDenormalizepara verificações a nível de objeto após o binding de dados- Voters para lógica de autorização complexa
- Limitação de taxa através de integração com Symfony Rate Limiter
#[ApiResource(
operations: [
new Get(
security: "is_granted('ROLE_USER')"
),
new Put(
security: "is_granted('ROLE_ADMIN') or object.getOwner() == user",
securityPostDenormalize: "is_granted('BOOK_EDIT', object)"
)
]
)]Filtros e Paginação: Padrões de Consulta Avançados
Os filtros do API Platform permitem que clientes consultem coleções com parâmetros URL. O sistema de filtros na versão 4.2 foi redesenhado para melhor extensibilidade.
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
'title' => 'partial', // LIKE %value%
'author.name' => 'exact' // Nested property
])]
#[ApiFilter(DateFilter::class, properties: ['createdAt'])]
#[ApiFilter(OrderFilter::class, properties: ['title', 'createdAt'])]
class Book { /* ... */ }Endpoints gerados:
GET /api/books?title=symfony # Search by title
GET /api/books?createdAt[after]=2026-01-01 # Date range
GET /api/books?order[createdAt]=desc # SortingTestes de Recursos API Platform
API Platform fornece um cliente de testes que simplifica os testes funcionais. A classe ApiTestCase oferece asserções específicas para respostas de API.
namespace App\Tests\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Book;
class BookTest extends ApiTestCase
{
public function testGetCollection(): void
{
$response = static::createClient()->request('GET', '/api/books');
$this->assertResponseIsSuccessful();
$this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
$this->assertJsonContains([
'@context' => '/api/contexts/Book',
'@type' => 'Collection'
]);
}
public function testCreateBook(): void
{
$response = static::createClient()->request('POST', '/api/books', [
'json' => [
'title' => 'Symfony Best Practices',
'description' => 'A guide to modern Symfony development'
],
'headers' => ['Authorization' => 'Bearer ' . $this->getToken()]
]);
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains(['title' => 'Symfony Best Practices']);
}
public function testCreateBookValidationFails(): void
{
$response = static::createClient()->request('POST', '/api/books', [
'json' => ['description' => 'Missing title'],
'headers' => ['Authorization' => 'Bearer ' . $this->getToken()]
]);
$this->assertResponseStatusCodeSame(422);
$this->assertJsonContains([
'violations' => [
['propertyPath' => 'title', 'message' => 'This value should not be blank.']
]
]);
}
}Pontos-Chave para API Platform com Symfony
- State Providers lidam com operações GET, State Processors lidam com mutações. Essa separação permite arquitetura limpa com fontes de dados personalizadas e lógica de negócio
- O componente Object Mapper desacopla recursos API de entidades Doctrine, permitindo diferentes modelos de leitura/escrita e protegendo estruturas internas
- O JSON Streamer oferece melhoria de performance de 32% em endpoints de coleção ao serializar sem alocação completa de memória
- A segurança se integra através de mecanismos padrão do Symfony: expressões
security, Voters e componente Rate Limiter - Os grupos de validação personalizam a aplicação de constraints por operação
- Filtros expõem parâmetros de consulta automaticamente, com filtros de busca, data e ordenação cobrindo a maioria dos casos de uso
- Perguntas de entrevista focam em decisões arquiteturais: quando usar providers personalizados, como separar modelos de leitura/escrita, e padrões de implementação de segurança
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Você saberia encontrar o bug em Symfony?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 8 de setembro de 2026
Tags
Compartilhar
Artigos relacionados

Perguntas de entrevista Symfony: Top 25 em 2026
As 25 perguntas de entrevista Symfony mais frequentes. Arquitetura, Doctrine ORM, serviços, segurança, formulários e testes com respostas detalhadas e exemplos de código.

Segurança no Symfony em 2026: Voters, Firewalls e Perguntas de Entrevista Técnica
Análise aprofundada da segurança no Symfony cobrindo voters, firewalls, access tokens e autenticação, com as perguntas de entrevista técnica mais frequentes para desenvolvedores Symfony.

Doctrine ORM: Dominando relacionamentos no Symfony
Guia completo dos relacionamentos Doctrine ORM no Symfony. OneToMany, ManyToMany, estratégias de carregamento e otimização de performance com exemplos práticos.