API Platform mit Symfony 2026: Architektur und Interview-Fragen für Entwickler

Umfassender Leitfaden zu API Platform mit Symfony 2026. Lernen Sie REST-API-Architektur, State Providers, Processors und häufige Interview-Fragen für Symfony-Entwickler.

API Platform mit Symfony 2026: Architektur und Interview-Fragen für Entwickler

API Platform hat sich als führendes Framework für die Entwicklung von REST- und GraphQL-APIs mit Symfony etabliert. Im Jahr 2026 bietet die aktuelle Version leistungsstarke Features wie State Providers, State Processors und erweiterte OpenAPI-Dokumentation. Dieser Artikel behandelt die moderne Architektur von API Platform und bereitet Entwickler auf technische Interviews vor.

API Platform 4.x setzt Symfony 7.x und PHP 8.3+ voraus. Die hier gezeigten Beispiele nutzen die neuesten Features und Best Practices für produktionsreife APIs.

Installation und Projektkonfiguration

Die Einrichtung eines neuen API Platform-Projekts erfolgt über Composer. Das Framework integriert sich nahtlos in bestehende Symfony-Anwendungen.

bash
composer create-project api-platform/api-platform my-api
cd my-api
composer require api-platform/core

Die Grundkonfiguration in config/packages/api_platform.yaml definiert wichtige Parameter:

yaml
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: true

Entitäten als API-Ressourcen definieren

API Platform verwendet PHP-Attribute zur Konfiguration von API-Ressourcen. Eine typische Entität kombiniert Doctrine-Mapping mit API-Definitionen.

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 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 für individuelle Datenlogik

State Providers ersetzen die früheren Data Providers und bieten mehr Flexibilität bei der Datenbeschaffung. Sie ermöglichen die Integration externer Datenquellen oder komplexer Geschäftslogik.

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

Die Registrierung erfolgt über das Provider-Attribut:

php
#[ApiResource(
    provider: ProductStateProvider::class
)]
class Product
{
    // ...
}

State Processors für Schreiboperationen

State Processors verarbeiten POST-, PUT-, PATCH- und DELETE-Anfragen. Sie kapseln die Geschäftslogik für Datenänderungen.

php
<?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);
    }
}

DTOs und Input/Output-Transformationen

Data Transfer Objects ermöglichen die Trennung von API-Repräsentation und internen Entitäten. Diese Architektur erhöht die Flexibilität und Sicherheit.

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

namespace App\Dto;

class ProductOutput
{
    public int $id;
    public string $name;
    public float $price;
    public string $formattedPrice;
    public string $category;
    public string $createdAt;
}

Die Konfiguration der Transformation erfolgt in der Ressourcendefinition:

php
#[ApiResource(
    operations: [
        new Post(
            input: CreateProductInput::class,
            output: ProductOutput::class,
            processor: CreateProductProcessor::class
        )
    ]
)]
class Product
{
    // ...
}

Filterung und Sortierung

API Platform bietet deklarative Filter für häufige Abfragemuster. Die Konfiguration erfolgt direkt an der Entität.

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

Eine beispielhafte API-Anfrage mit Filtern:

bash
GET /api/products?name=laptop&price[gte]=500&order[price]=asc

Authentifizierung und Autorisierung

Die Sicherheitskonfiguration nutzt Symfonys Security-Komponente. API Platform integriert Voter und Expressions nahtlos.

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

Versionierung von APIs

API Platform unterstützt verschiedene Versionierungsstrategien. Die URL-basierte Versionierung ist am weitesten verbreitet.

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

Häufige Interview-Fragen

Frage: Was ist der Unterschied zwischen State Provider und State Processor?

State Providers sind für Leseoperationen zuständig (GET-Anfragen) und liefern Daten aus beliebigen Quellen. State Processors verarbeiten Schreiboperationen (POST, PUT, PATCH, DELETE) und implementieren die Persistenzlogik sowie Nebeneffekte wie Benachrichtigungen.

Frage: Wie implementiert man Pagination in API Platform?

API Platform bietet automatische Pagination. Die Konfiguration erfolgt global oder pro Ressource:

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

Frage: Welche Serialisierungsgruppen werden empfohlen?

Best Practices empfehlen separate Gruppen für Lesen und Schreiben:

php
#[ApiResource(
    normalizationContext: ['groups' => ['product:read']],
    denormalizationContext: ['groups' => ['product:write']]
)]

Frage: Wie testet man API Platform-Endpunkte?

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

Bereit für deine Symfony-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

Fazit

API Platform 2026 bietet eine ausgereifte Architektur für die Entwicklung von REST-APIs mit Symfony. State Providers und Processors ermöglichen flexible Datenzugriffsmuster, während DTOs eine saubere Trennung von API und Domänenlogik gewährleisten. Die Integration von Filtern, Sicherheit und automatischer Dokumentation macht API Platform zur ersten Wahl für professionelle API-Entwicklung. Die behandelten Konzepte und Interview-Fragen bereiten Entwickler optimal auf technische Gespräche vor und vermitteln Best Practices für produktionsreife Implementierungen.

Tägliche Challenge

Findest du den Bug in Symfony?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 8. September 2026

Teilen

Verwandte Artikel