Laravel’de API Versiyonlama Stratejisi: Kırmadan Evrimleştirmek (v1’den v2’ye)
Laravel’de API versiyonlamayı URL/Header ile kurgula; deprecate süreci, transformer’lar ve testlerle kırmadan ilerle.
API’ler “bitmiş ürün” değil; canlı bir sözleşmedir. Yeni alan eklemek kolay, ama alan adı değiştirmek veya davranışı düzeltmek çoğu zaman kırıcı (breaking) etki yapar. Laravel’de API versiyonlamayı doğru kurgulamak; hem hızlı evrim hem de geriye dönük uyumluluk demektir.
1) Versiyonlama ne zaman gerçekten gerekli?
Her değişiklikte v2 çıkarmak yerine şunu ayır:
- Geriye uyumlu: Yeni alan eklemek, opsiyonel parametre eklemek, yeni endpoint eklemek.
- Kırıcı: Alan adı/format değişikliği (string → object), zorunlu alan eklemek, HTTP status/iş kuralı değişimi.
Kırıcı değişiklik varsa versiyon devreye girsin; diğerlerinde tek versiyonda ilerlemek genelde yeterlidir.
2) URL tabanlı versiyonlama (kolay ve görünür)
En pratik yaklaşım: /api/v1/... ve /api/v2/....
routes/api.php
Route::prefix('v1')->group(function () {
Route::get('orders/{order}', [\App\Http\Controllers\Api\V1\OrderController::class, 'show']);
});
Route::prefix('v2')->group(function () {
Route::get('orders/{order}', [\App\Http\Controllers\Api\V2\OrderController::class, 'show']);
});
Artısı: Dokümantasyon ve debug kolay. Eksisi: URL’ler çoğalır; ama yönetilebilir.
3) Header tabanlı versiyonlama (aynı URL, farklı sözleşme)
Tek endpoint URL’si kullanıp versiyonu header’dan okuyabilirsin:
Accept: application/vnd.myapp.v2+json- veya
X-Api-Version: 2
Bu yaklaşımda route tek kalır; içeride farklı “serializer/transformer” seçersin. Ek bir middleware ile versiyonu request’e yazabilirsin.
4) Controller kopyalamak yerine “Transformer” katmanı
Versiyonlar büyüdükçe controller’ları kopyalamak teknik borç üretir. Daha temiz çözüm: aynı domain verisini farklı şekilde sunan transformer’lar.
Örnek: v1’de total düz sayıydı, v2’de para formatı istiyorsun.
app/Http/Resources/V1/OrderResource.php
class OrderResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'total' => $this->total_cents / 100,
];
}
}
app/Http/Resources/V2/OrderResource.php
class OrderResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'total' => [
'amount' => $this->total_cents,
'currency' => 'TRY',
],
];
}
}
Controller sadece doğru resource’u seçer:
public function show(Order $order)
{
return new \App\Http\Resources\V2\OrderResource($order);
}
Bu sayede domain modeli aynı kalır, “sözleşme” katmanı versiyonlanır.
5) Deprecation (kaldırma) planı: Sürpriz yok
Versiyonlamanın yarısı iletişimdir.
- Response header ekle:
Deprecation: trueSunset: Wed, 30 Apr 2026 00:00:00 GMT
- JSON body’ye kırıcı mesaj ekleme (client’ları parse ederken riskli olabilir). Header daha güvenli.
Laravel’de basit bir middleware ile v1 isteklerine bu header’ları ekleyebilirsin.
6) Sözleşme testleri: Versiyonlar “yan yana” yaşamalı
İki versiyonu aynı anda taşırken en kritik nokta: v1’i düzeltirken v2’yi, v2’yi geliştirirken v1’i bozmamak.
Pratik yaklaşım:
- Her versiyon için ayrı feature test klasörü:
tests/Feature/Api/V1veV2. - Aynı seed ile aynı order için farklı response bekle.
Örnek assertion:
$this->getJson('/api/v1/orders/1')
->assertOk()
->assertJsonStructure(['id', 'total']);
$this->getJson('/api/v2/orders/1')
->assertOk()
->assertJsonStructure(['id', 'total' => ['amount', 'currency']]);
Sonuç
Laravel’de API versiyonlama; route prefix’leri, Resource/Transformer katmanı ve net bir deprecation takvimiyle sürdürülebilir hale gelir. Amaç “v2 çıkarıp unutmak” değil; sözleşmeyi kontrollü biçimde evrimleştirmektir.