API Platform Symfony REST: Tutorial Completo y Preguntas de Entrevista 2026
Domina API Platform con Symfony para crear APIs REST de alto rendimiento. Guía completa con instalación, configuración, buenas prácticas y preguntas de entrevista técnica.

API Platform se ha consolidado como el framework de referencia para desarrollar APIs REST profesionales con Symfony. Esta solución ofrece productividad excepcional mientras respeta los estándares web modernos. Este tutorial cubre la instalación, configuración avanzada y conceptos esenciales para destacar en entrevistas técnicas.
API Platform genera automáticamente documentación OpenAPI, soporte JSON-LD/Hydra y GraphQL. Estas funcionalidades integradas aceleran considerablemente el desarrollo de APIs conformes a los estándares.
Instalación de API Platform con Symfony
La instalación de API Platform en un proyecto Symfony existente se realiza mediante Composer. El bundle se integra perfectamente con el ecosistema Symfony y Doctrine.
composer require api-platform/corePara un nuevo proyecto, API Platform ofrece una distribución completa que incluye Symfony, Docker y herramientas de desarrollo preconfiguradas:
composer create-project api-platform/api-platform my-api
cd my-api
docker compose up -dLa configuración mínima se realiza en el archivo config/packages/api_platform.yaml:
api_platform:
title: 'Mi 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: 30Creación de un Recurso API
La transformación de una entidad Doctrine en recurso API se realiza con el atributo #[ApiResource]. API Platform genera automáticamente las operaciones CRUD estándar.
<?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 y setters...
}Grupos de Serialización y DTO
El control preciso de los datos expuestos se realiza mediante grupos de serialización. Este enfoque permite diferenciar los campos visibles según el contexto.
<?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 complejos, los DTO (Data Transfer Objects) ofrecen una separación clara entre la representación API y el modelo de negocio:
<?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 y Búsqueda Avanzada
API Platform integra un sistema de filtros potente para las operaciones de colección. Los filtros se aplican directamente sobre las entidades.
<?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
{
// Propiedades...
}Las solicitudes HTTP utilizan estos filtros mediante parámetros de query string:
# Búsqueda parcial por nombre
GET /api/products?name=smartphone
# Filtrado por rango de precios
GET /api/products?price[gte]=100&price[lte]=500
# Ordenamiento por precio descendente
GET /api/products?order[price]=desc
# Combinación de filtros
GET /api/products?name=phone&price[gte]=200&order[createdAt]=descState Providers y State Processors
Los State Providers permiten personalizar la recuperación de datos, mientras que los State Processors controlan las operaciones de escritura.
<?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();
}
}El State Processor gestiona la lógica de negocio durante las creaciones y modificaciones:
<?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;
}
}Seguridad y Control de Acceso
API Platform se integra con el componente Security de Symfony para gestionar autenticación y autorizaciones.
<?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 seguridad a nivel de campos, los grupos de serialización combinados con voters ofrecen control granular:
<?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;
}
}¿Listo para aprobar tus entrevistas de Symfony?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Preguntas de Entrevista sobre API Platform
Las entrevistas técnicas sobre API Platform evalúan la comprensión de conceptos fundamentales y buenas prácticas.
Pregunta: ¿Cuál es la diferencia entre State Provider y State Processor?
El State Provider recupera datos (operaciones GET), mientras que el State Processor los modifica (POST, PUT, PATCH, DELETE). Esta separación respeta el principio de responsabilidad única.
Pregunta: ¿Cómo implementar paginación personalizada?
#[ApiResource(
paginationEnabled: true,
paginationItemsPerPage: 20,
paginationMaximumItemsPerPage: 100,
paginationClientEnabled: true,
paginationClientItemsPerPage: true
)]Pregunta: ¿Cómo manejar relaciones en API Platform?
Las relaciones utilizan IRI (Internationalized Resource Identifiers). Para incluir datos relacionados, se configuran grupos de serialización apropiados o se utiliza la opción fetchEager.
Pregunta: ¿Qué estrategia usar para versionado de API?
API Platform soporta varios enfoques: versionado por URL (/api/v1/products), por header (Accept: application/vnd.api+json;version=1) o por query parameter. La configuración se realiza mediante operaciones personalizadas.
Pregunta: ¿Cómo optimizar el rendimiento con API Platform?
Las optimizaciones incluyen: eager loading de relaciones, uso del caché HTTP integrado, paginación apropiada y grupos de serialización específicos para reducir el tamaño de las respuestas.
Pruebas Funcionales de API
Las pruebas de API utilizan el cliente HTTP de Symfony combinado con aserciones específicas de 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' => 'Nuevo Producto',
'description' => 'Descripción del producto',
'price' => '99.99'
],
'headers' => ['Content-Type' => 'application/ld+json']
]);
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains([
'@type' => 'Product',
'name' => 'Nuevo Producto'
]);
}
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']);
}
}Conclusión
API Platform representa una solución madura y de alto rendimiento para desarrollar APIs REST con Symfony. El dominio de los conceptos de recursos, filtros, state providers/processors y seguridad permite construir APIs robustas y mantenibles. Las preguntas de entrevista generalmente se centran en estos fundamentos junto con las buenas prácticas de diseño de APIs RESTful.
¿Sabrías detectar el bug en Symfony?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 25 de agosto de 2026
Compartir
Artículos relacionados

Seguridad de API REST en Symfony 2026: OAuth2, Rate Limiting y Preguntas de Entrevista
Guia completa sobre seguridad de API REST en Symfony con OAuth2, rate limiting, JWT y Voters. Incluye preguntas de entrevista tecnica.

API Platform con Symfony en 2026: Arquitectura, State Providers y Preguntas de Entrevista
Dominar API Platform 4.2 con Symfony: State Providers, Processors, Object Mapper, JSON Streamer para optimización de rendimiento, y preguntas de entrevista técnica para desarrolladores senior.

API Platform GraphQL con Symfony: Esquemas, Mutaciones y Preguntas de Entrevista 2026
Domina la integración de GraphQL en Symfony con API Platform. Esta guía cubre esquemas, mutaciones, resolvers personalizados y las preguntas de entrevista técnica más frecuentes en 2026.