API Platform Symfony REST: Kapsamli Rehber ve Mulakat Sorulari 2026

API Platform 4 ve Symfony 7 ile uretim kalitesinde REST API gelistirme. State Providers, Processors, filtreler ve API Platform mulakat sorulari.

API Platform Symfony REST Tutorial 2026

API Platform 4.3, Symfony'yi otomatik OpenAPI dokumantasyonu, icerik muzakeresi ve okuma/yazma islemlerini ayiran temiz bir mimari ile guclu bir REST API platformuna donusturur. Bu rehber kurulum, ileri duzey kaliplar ve kidemli adaylari juniorlardan ayiran mulakat sorularini kapsar.

API Platform 4 Mimarisi

API Platform 4, GET islemleri icin State Providers ve POST/PUT/PATCH/DELETE islemleri icin State Processors kullanir. Bu ayrim CQRS prensipleriyle uyumludur ve test yapmayi kolaylastirir.

Symfony 7 Uzerine API Platform Kurulumu

API Platform kurulumu Symfony 7.2 veya ustunu gerektirir. Bundle varsayilan olarak Doctrine ORM ile entegre olur ancak Provider/Processor deseni araciligiyla ozel veri kaynaklarini destekler.

bash
# Install API Platform with Symfony Flex
composer require api

# Verify installation
php bin/console debug:router | grep api

api tarifi, serializer, validator ve property-access bilesenleriyle birlikte api-platform/symfony paketini yukler. Symfony Flex, rotalari otomatik olarak /api altinda yapilandirir.

yaml
# config/packages/api_platform.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', 'Accept-Language']

stateless: true ayari tum API endpoint'leri icin PHP oturumlarini devre disi birakir, bellek kullanimini azaltir ve yatay olceklendirmeyi mumkun kilar.

Ozelliklerle Ilk API Kaynagini Olusturma

API Platform 4, API kaynaklarini tanimlamak icin PHP 8 ozelliklerini kullanir. Her entity, #[ApiResource] ozelligi araciligiyla bir API endpoint'ine donusur.

src/Entity/Product.phpphp
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')")
    ],
    paginationItemsPerPage: 30
)]
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;

    public function __construct()
    {
        $this->createdAt = new \DateTimeImmutable();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): static
    {
        $this->name = $name;
        return $this;
    }

    public function getPrice(): string
    {
        return $this->price;
    }

    public function setPrice(string $price): static
    {
        $this->price = $price;
        return $this;
    }

    public function getCreatedAt(): \DateTimeImmutable
    {
        return $this->createdAt;
    }
}

Her islemdeki security parametresi Symfony'nin ifade dilini kullanir. is_granted() fonksiyonu voter kararlarini kontrol eder ve ozel controller'lar olmadan rol tabanli erisim kontrolu saglar.

Yanit Sekillendirme Icin Serializasyon Gruplari

Serializasyon gruplari API yanitlarinda hangi ozelliklerin gorunecegini kontrol eder. Farkli islemler ayni entity'den farkli alanlari gosterebilir.

src/Entity/User.phpphp
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

#[ORM\Entity]
#[ApiResource(
    operations: [
        new GetCollection(normalizationContext: ['groups' => ['user:list']]),
        new Get(normalizationContext: ['groups' => ['user:read']]),
        new Post(
            normalizationContext: ['groups' => ['user:read']],
            denormalizationContext: ['groups' => ['user:write']]
        )
    ]
)]
class User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    #[Groups(['user:list', 'user:read'])]
    private ?int $id = null;

    #[ORM\Column(length: 180)]
    #[Groups(['user:list', 'user:read', 'user:write'])]
    private string $email;

    #[ORM\Column]
    #[Groups(['user:write'])]
    private string $password;

    #[ORM\Column]
    #[Groups(['user:read'])]
    private \DateTimeImmutable $registeredAt;

    #[ORM\Column(type: 'json')]
    #[Groups(['user:read'])]
    private array $roles = [];
}

normalizationContext cikisi (PHP'den JSON'a) kontrol eder. denormalizationContext girisi (JSON'dan PHP'ye) kontrol eder. password alani yalnizca user:write kullanir ve boylece yanıtlarda hic gorunmez.

Ozel Veri Kaynaklari Icin State Providers

State Providers, GET islemleri icin veri getirir. Varsayilan ItemProvider ve CollectionProvider Doctrine kullanir, ancak ozel provider'lar herhangi bir veri kaynagini etkinlestirir: Elasticsearch, harici API'ler veya hesaplanan degerler.

src/State/ProductStatsProvider.phpphp
namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\ProductRepository;
use App\Dto\ProductStats;

final readonly class ProductStatsProvider implements ProviderInterface
{
    public function __construct(
        private ProductRepository $productRepository
    ) {}

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): ProductStats
    {
        $totalProducts = $this->productRepository->count([]);
        $averagePrice = $this->productRepository->getAveragePrice();
        $lowStockCount = $this->productRepository->countLowStock(threshold: 10);

        return new ProductStats(
            totalProducts: $totalProducts,
            averagePrice: $averagePrice,
            lowStockCount: $lowStockCount
        );
    }
}
src/Dto/ProductStats.phpphp
namespace App\Dto;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use App\State\ProductStatsProvider;

#[ApiResource(
    operations: [
        new Get(
            uriTemplate: '/products/stats',
            provider: ProductStatsProvider::class
        )
    ]
)]
final readonly class ProductStats
{
    public function __construct(
        public int $totalProducts,
        public float $averagePrice,
        public int $lowStockCount
    ) {}
}

ProductStats DTO'su (Veri Transfer Nesnesi) bir Doctrine entity'si degildir. /api/products/stats adresinde erisilen hesaplanmis verileri temsil eder. Provider, her istekte degerleri hesaplar.

Symfony mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Yazma Islemleri Icin State Processors

State Processors, POST, PUT, PATCH ve DELETE islemlerini yonetir. Ozel processor'lar, kayittan once veya sonra is mantigi yurutmeyi saglar.

src/State/UserRegistrationProcessor.phpphp
namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

final readonly class UserRegistrationProcessor implements ProcessorInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private UserPasswordHasherInterface $passwordHasher
    ) {}

    public function process(
        mixed $data,
        Operation $operation,
        array $uriVariables = [],
        array $context = []
    ): User {
        // $data is the deserialized User entity from the request body
        $hashedPassword = $this->passwordHasher->hashPassword(
            $data,
            $data->getPlainPassword()
        );
        $data->setPassword($hashedPassword);
        $data->eraseCredentials();

        $this->entityManager->persist($data);
        $this->entityManager->flush();

        return $data;
    }
}

Processor, User entity'sindeki POST islemine baglanir:

php
#[ApiResource(
    operations: [
        new Post(
            processor: UserRegistrationProcessor::class,
            denormalizationContext: ['groups' => ['user:create']]
        )
    ]
)]

Processor, Doctrine entity'yi kaydetmeden once sifreyi hashler. plainPassword ozelligi yalnizca yazma serializasyon grubu kullanir ve veritabaninda asla saklanmaz.

Sorgu Parametreleri Icin Filtreler

API Platform, koleksiyonlari arama, siralama ve filtreleme icin yerlesik filtreler saglar. Filtreler, GET koleksiyon endpoint'lerine sorgu parametreleri ekler.

src/Entity/Article.phpphp
namespace App\Entity;

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;

#[ORM\Entity]
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
    'title' => 'partial',
    'author.name' => 'exact',
    'category' => 'exact'
])]
#[ApiFilter(DateFilter::class, properties: ['publishedAt'])]
#[ApiFilter(OrderFilter::class, properties: ['publishedAt', 'title'])]
class Article
{
    // Entity properties...
}

Filtre yapilandirmasi su sorgu parametrelerini etkinlestirir:

  • GET /api/articles?title=symfony (baslikta kismi eslestirme)
  • GET /api/articles?author.name=John (iliskili entity'de tam eslestirme)
  • GET /api/articles?publishedAt[after]=2026-01-01 (tarih araligi)
  • GET /api/articles?order[publishedAt]=desc (siralama)

Ozel filtreler, yerlesik filtrelerin ifade edemedigi karmasik sorgu mantigi icin AbstractFilter sinifini genisletir.

Hata Islem ve Dogrulama

API Platform, Symfony Validator ile entegre olur. Dogrulama hatalari RFC 7807 sorun ayrintilariyla 422 Unprocessable Entity dondurur.

src/Entity/Order.phpphp
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity]
#[ApiResource]
class Order
{
    #[ORM\Column]
    #[Assert\NotBlank(message: 'Order quantity is required')]
    #[Assert\Positive(message: 'Quantity must be greater than zero')]
    #[Assert\LessThanOrEqual(value: 100, message: 'Maximum 100 items per order')]
    private int $quantity;
}

Dogrulama hatasi su yaniti dondurur:

json
{
    "@type": "ConstraintViolationList",
    "status": 422,
    "violations": [
        {
            "propertyPath": "quantity",
            "message": "Quantity must be greater than zero"
        }
    ]
}

Sinif duzeyinde kisitlamalari olan ozel dogrulayicilar, siparişin bitis tarihinin baslangic tarihinden sonra gelmesini saglama gibi alanlar arasi dogrulamayi yonetir.

Mulakat Sorulari: API Platform Derin Bilgisi

Teknik mulakatlar temel kullanimın otesinde anlamayi arastirir. Bu sorular Symfony backend pozisyonlari icin sikca ortaya cikar.

S: API Platform, controller'lari manuel yazmakindan nasil farklidir?

API Platform, entity meta verilerinden CRUD islemleri olusturur. Tek bir #[ApiResource] ozelligi, OpenAPI dokumantasyonu, icerik muzakeresi, sayfalandirma ve dogrulama ile GET, POST, PUT, PATCH ve DELETE endpoint'leri uretir. Manuel controller'lar her ozelligin ayri ayri uygulanmasini gerektirir. API Platform, State Providers ve Processors araciligiyla ozel mantik icin genisletilebilir kalirken standart REST islemleri icin boilerplate'i %70-80 azaltir.

S: State Providers ve State Processors arasindaki farki aciklayin.

State Providers veri alimini yonetir (GET islemleri). ProviderInterface::provide() uygular ve entity'ler, DTO'lar veya koleksiyonlar dondurur. State Processors veri mutasyonunu yonetir (POST, PUT, PATCH, DELETE). ProcessorInterface::process() uygular ve istek govdesinden deserialize edilmis nesneyi alir. Bu ayrim CQRS prensiplerine uyar: okumalar ve yazmalar farkli kod yollarina sahiptir.

S: API yanitlarinda hassas alanlarin gosterilmesini nasil onlersiniz?

Serializasyon gruplari alan gorunurlugunu kontrol eder. Hassas alanlari (sifreler, dahili ID'ler, denetim verileri) yalnizca yazma gruplarina atayin veya tamamen dislayin. Yalnizca yoneticilerin gormesi gereken alanlar icin #[Groups(['admin:read'])] kullanin, ardindan o grubu yalnizca yonetici endpoint'leri icin dahil etmek uzere islem duzeyinde normalizationContext yapilandirin.

S: API Platform'da sayfalandirma nasil calisir?

API Platform koleksiyonlari varsayilan olarak sayfa basina 30 oge ile sayfalandirir. page sorgu parametresi offset'i kontrol eder. JSON-LD yanitlarindaki Hydra meta verileri, first/last/next/previous baglantilariyla hydra:view icerir. paginationItemsPerPage, paginationMaximumItemsPerPage ve paginationClientItemsPerPage (istemcilerin farkli sayfa boyutlari istemesine izin verir) araciligiyla yapilandirilir.

S: Entity'yi dogrudan gostermek yerine DTO ne zaman kullanilir?

DTO'lar API sozlesmesini veritabani semasindan ayirir. DTO'lar su durumlarda kullanilir: (1) API gosterimi entity yapisindan onemli olcude farklidir, (2) birden fazla entity tek bir yanita birlestirilir, (3) yanitlarda hesaplanan alanlar gorulur, (4) giris dogrulamasi entity kisitlamarindan farklidir, veya (5) entity serializasyonu zorlastiran Doctrine kalitimi kullanir. DTO'lar ayrica sema degistiginde yeni entity alanlarinin kazara gosterilmesini onler.

Anlamayi pekistirmek icin API Platform mulakat sorulari ile pratik yapin.

API Platform Endpoint'lerini Test Etme

API Platform, Symfony'nin test framework'u ile calisir. ApiTestCase, kimlik dogrulanmis istekler yapmak ve JSON yanitlari dogrulamak icin yontemler saglar.

tests/Api/ProductTest.phpphp
namespace App\Tests\Api;

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Product;

class ProductTest extends ApiTestCase
{
    public function testGetCollection(): void
    {
        $response = static::createClient()->request('GET', '/api/products');

        $this->assertResponseIsSuccessful();
        $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
        $this->assertJsonContains(['@context' => '/api/contexts/Product']);
        $this->assertMatchesResourceCollectionJsonSchema(Product::class);
    }

    public function testCreateProduct(): void
    {
        $response = static::createClient()->request('POST', '/api/products', [
            'json' => [
                'name' => 'Test Product',
                'price' => '29.99'
            ],
            'headers' => [
                'Authorization' => 'Bearer ' . $this->getAdminToken()
            ]
        ]);

        $this->assertResponseStatusCodeSame(201);
        $this->assertJsonContains([
            'name' => 'Test Product',
            'price' => '29.99'
        ]);
    }

    public function testCreateProductValidationFails(): void
    {
        static::createClient()->request('POST', '/api/products', [
            'json' => ['name' => 'AB'],  // Too short
            'headers' => ['Authorization' => 'Bearer ' . $this->getAdminToken()]
        ]);

        $this->assertResponseStatusCodeSame(422);
        $this->assertJsonContains([
            'violations' => [
                ['propertyPath' => 'name']
            ]
        ]);
    }
}

assertMatchesResourceCollectionJsonSchema() yontemi, yanitlari otomatik olarak olusturulan JSON Schema'ya gore dogrular ve entity yapisi degistiginde regresyonlari yakalar.

Eager Loading ile Performans Optimizasyonu

N+1 sorgulari koleksiyon endpoint performansini duşurur. API Platform'un fetchEager secenegi ve Doctrine'in query hints ozelligi bu sorunu cozer.

php
#[ApiResource(
    operations: [
        new GetCollection(
            extraProperties: ['doctrine_orm_fetch_join' => true]
        )
    ]
)]
#[ORM\Entity]
class Order
{
    #[ORM\ManyToOne(fetch: 'EAGER')]
    #[ORM\JoinColumn(nullable: false)]
    private Customer $customer;

    #[ORM\OneToMany(mappedBy: 'order', fetch: 'EAGER')]
    private Collection $items;
}

Karmasik sorgular icin optimize edilmis DQL ile ozel State Providers, otomatik eager loading'den daha iyi performans gosterir. Optimizasyondan once ve sonra Symfony Profiler ile sorgu sayilarini olcun.

Uretime Hazir API Platform Yapilandirmasi

yaml
# config/packages/api_platform.yaml
api_platform:
    title: '%env(API_TITLE)%'
    version: '%env(API_VERSION)%'
    show_webby: false
    
    defaults:
        stateless: true
        cache_headers:
            max_age: 3600
            shared_max_age: 3600
            vary: ['Content-Type', 'Authorization']
        extra_properties:
            standard_put: true
    
    exception_to_status:
        Symfony\Component\Security\Core\Exception\AccessDeniedException: 403
        App\Exception\BusinessException: 400

Uretimde show_webby (maskot) devre disi birakilmalidir. standard_put ozelligi, RFC 7231 semantigini izleyerek PUT'un kaynaklari birlestirmek yerine tamamen degistirmesini saglar.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

2026'da API Platform Hakkinda Hatirlanmasi Gerekenler

  • State Providers, GET istekleri icin veri getirir. State Processors, POST/PUT/PATCH/DELETE'i yonetir. Bu ayrim temiz test ve ozel veri kaynaklarini mumkun kilar.
  • Serializasyon gruplari yanıtlarda hangi alanlarin gorunecegini kontrol eder. Liste gorunumleri ile detay gorunumleri icin farkli gruplar ve sifreler icin yalnizca yazma gruplari kullanin.
  • Filtreler arama, siralama ve tarih araliklari icin sorgu parametreleri ekler. Yerlesik filtreler cogu durumu kapsar ve ozel filtreler karmasik sorgulari yonetir.
  • DTO'lar API sozlesmelerini veritabani semalarindan ayirir. Yanit yapisi entity'den farkli oldugunda veya veriler birden fazla kaynaktan birlestirildiginde kullanin.
  • Symfony Serializer JSON-entity donusumunu yonetir. Normalizer'larini ve baglam seceneklerini anlamak, gelismis API Platform kullanimi icin gereklidir.
  • API Platform 4.3 mevcut kararli surumudur. Surum 5.0 (alfa asamasinda) Symfony 7.4 veya 8.0 gerektirir ve eski Symfony surumleri icin destegi sonlandirir.
  • api-platform.com dokumantasyonu burada ele alinmayan uc durumları kapsar. Doctrine ORM entegrasyonu dokumantasyonu entity iliski yonetimini ayrintili olarak aciklar.
Günün meydan okuması

Symfony kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

25 Ağustos 2026 tarihinde güncellendi

Etiketler

#api-platform
#symfony
#rest-api
#php
#tutorial

Paylaş

İlgili makaleler