API Platform Symfony REST: Tutorial Completo e Perguntas de Entrevista 2026

Domine o API Platform com Symfony para criar APIs REST de alto desempenho. Guia completo com instalação, configuração, boas práticas e perguntas de entrevista técnica.

API Platform Symfony REST: Tutorial Completo e Perguntas de Entrevista 2026

O API Platform se consolidou como o framework de referência para desenvolver APIs REST profissionais com Symfony. Esta solução oferece produtividade excepcional enquanto respeita os padrões web modernos. Este tutorial aborda a instalação, configuração avançada e conceitos essenciais para se destacar em entrevistas técnicas.

O API Platform gera automaticamente documentação OpenAPI, suporte a JSON-LD/Hydra e GraphQL. Essas funcionalidades integradas aceleram consideravelmente o desenvolvimento de APIs em conformidade com os padrões.

Instalação do API Platform com Symfony

A instalação do API Platform em um projeto Symfony existente é realizada via Composer. O bundle se integra perfeitamente com o ecossistema Symfony e Doctrine.

bash
composer require api-platform/core

Para um novo projeto, o API Platform oferece uma distribuição completa incluindo Symfony, Docker e ferramentas de desenvolvimento pré-configuradas:

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

A configuração mínima é feita no arquivo config/packages/api_platform.yaml:

yaml
api_platform:
    title: 'Minha 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

Criação de um Recurso API

A transformação de uma entidade Doctrine em recurso API é realizada com o atributo #[ApiResource]. O API Platform gera automaticamente as operações CRUD padrão.

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 e setters...
}

Grupos de Serialização e DTO

O controle preciso dos dados expostos é realizado através de grupos de serialização. Esta abordagem permite diferenciar os campos visíveis de acordo com o contexto.

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;
}

Para casos complexos, os DTOs (Data Transfer Objects) oferecem uma separação clara entre a representação da API e o modelo de negócio:

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;
}

Filtros e Busca Avançada

O API Platform integra um sistema de filtros poderoso para operações de coleção. Os filtros são aplicados diretamente nas entidades.

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
{
    // Propriedades...
}

As requisições HTTP utilizam esses filtros através de parâmetros de query string:

bash
# Busca parcial por nome
GET /api/products?name=smartphone

# Filtragem por faixa de preços
GET /api/products?price[gte]=100&price[lte]=500

# Ordenação por preço decrescente
GET /api/products?order[price]=desc

# Combinação de filtros
GET /api/products?name=phone&price[gte]=200&order[createdAt]=desc

State Providers e State Processors

Os State Providers permitem personalizar a recuperação de dados, enquanto os State Processors controlam as operações de escrita.

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();
    }
}

O State Processor gerencia a lógica de negócio durante criações e modificações:

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;
    }
}

Segurança e Controle de Acesso

O API Platform se integra com o componente Security do Symfony para gerenciar autenticação e autorizações.

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
{
    // ...
}

Para segurança a nível de campos, os grupos de serialização combinados com voters oferecem controle granular:

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;
    }
}

Pronto para mandar bem nas entrevistas de Symfony?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Perguntas de Entrevista sobre API Platform

As entrevistas técnicas sobre API Platform avaliam a compreensão dos conceitos fundamentais e boas práticas.

Pergunta: Qual a diferença entre State Provider e State Processor?

O State Provider recupera dados (operações GET), enquanto o State Processor os modifica (POST, PUT, PATCH, DELETE). Esta separação respeita o princípio de responsabilidade única.

Pergunta: Como implementar paginação personalizada?

php
#[ApiResource(
    paginationEnabled: true,
    paginationItemsPerPage: 20,
    paginationMaximumItemsPerPage: 100,
    paginationClientEnabled: true,
    paginationClientItemsPerPage: true
)]

Pergunta: Como lidar com relacionamentos no API Platform?

Os relacionamentos utilizam IRIs (Internationalized Resource Identifiers). Para incluir dados relacionados, configuram-se grupos de serialização apropriados ou utiliza-se a opção fetchEager.

Pergunta: Qual estratégia usar para versionamento de API?

O API Platform suporta várias abordagens: versionamento por URL (/api/v1/products), por header (Accept: application/vnd.api+json;version=1) ou por query parameter. A configuração é feita através de operações personalizadas.

Pergunta: Como otimizar o desempenho com API Platform?

As otimizações incluem: eager loading de relacionamentos, uso do cache HTTP integrado, paginação apropriada e grupos de serialização específicos para reduzir o tamanho das respostas.

Testes Funcionais de API

Os testes de API utilizam o cliente HTTP do Symfony combinado com asserções específicas do 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' => 'Novo Produto',
                'description' => 'Descrição do produto',
                'price' => '99.99'
            ],
            'headers' => ['Content-Type' => 'application/ld+json']
        ]);

        $this->assertResponseStatusCodeSame(201);
        $this->assertJsonContains([
            '@type' => 'Product',
            'name' => 'Novo Produto'
        ]);
    }

    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']);
    }
}

Conclusão

O API Platform representa uma solução madura e de alto desempenho para desenvolver APIs REST com Symfony. O domínio dos conceitos de recursos, filtros, state providers/processors e segurança permite construir APIs robustas e de fácil manutenção. As perguntas de entrevista geralmente focam nesses fundamentos junto com as boas práticas de design de APIs RESTful.

Desafio do dia

Você saberia encontrar o bug em Symfony?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 25 de agosto de 2026

Compartilhar

Artigos relacionados