API Versioning Stratejileri: URL, Header ve Query Parametreleri
Bir API yayınlandığında, onu tüketen istemcilerle bir sözleşme imzalanmış olur. Ancak API'ler zamanla değişir: yeni alanlar eklenir, davranışlar değişir, bazı endpoint'ler kaldırılır. API versioning (sürümleme), bu değişiklikleri mevcut istemcileri bozmadan yönetmenin yoludur. Doğru strateji, hem geriye dönük uyumluluğu korur hem de API'nin evrilmesine olanak tanır.
1. Neden Sürümleme Gerekli?
Sürümleme olmadan, her değişiklik potansiyel bir kırılma (breaking change) riski taşır. Sürümleme şunları sağlar:
-
Geriye Dönük Uyumluluk: Eski istemciler çalışmaya devam eder.
-
Kademeli Geçiş: İstemciler, kendi hızlarında yeni sürüme geçer.
-
Paralel Sürümler: Eski ve yeni sürümler bir süre birlikte çalışır.
Kritik Ayrım: Her değişiklik yeni sürüm gerektirmez. Yeni opsiyonel alan eklemek genellikle güvenlidir; ancak alan kaldırmak, tip değiştirmek veya davranışı değiştirmek yeni sürüm gerektirir.
2. Üç Temel Sürümleme Stratejisi
A. URL Path Versioning (En Yaygın)
Sürüm, URL'nin bir parçasıdır:
text
GET /api/v1/products GET /api/v2/products
Avantajları:
-
Keşfedilebilirlik: Sürüm, URL'de açıkça görünür; geliştirici hangi sürümü kullandığını bilir.
-
Basitlik: Tarayıcıda, curl'de veya Postman'de test etmek kolaydır.
-
Routing Kolaylığı: API Gateway ve reverse proxy'ler sürüme göre kolayca yönlendirme yapabilir.
-
Önbellekleme: Her sürüm farklı bir URL olduğu için CDN ve tarayıcı önbelleği doğal olarak çalışır.
Dezavantajları:
-
REST safçıları, kaynak kimliğinin URL'de değişmesini eleştirir (aynı kaynak, farklı sürümlerde farklı URL).
-
Sürüm arttıkça URL'ler çoğalır.
ASP.NET Core'da Kullanım:
csharp
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
[MapToApiVersion("1.0")]
public IActionResult GetV1(int id) => Ok(...);
[HttpGet("{id}")]
[MapToApiVersion("2.0")]
public IActionResult GetV2(int id) => Ok(...);
}
B. Header Versioning (URI'yi Temiz Tutar)
Sürüm, özel bir HTTP başlığında taşınır:
text
GET /api/products api-version: 1.0
Avantajları:
-
Temiz URI: URL'ler sürümden bağımsızdır; kaynak kimliği korunur.
-
İçerik Müzakereesi: HTTP'nin
Acceptbaşlığıyla benzer felsefe taşır.
Dezavantajları:
-
Görünürlük: Sürüm, URL'de görünmediği için test ve debug zorlaşır.
-
Önbellekleme Karmaşıklığı: Aynı URL farklı sürümlere hizmet ettiği için
Vary: api-versionbaşlığı gerekir; CDN yapılandırması karmaşıklaşır.
ASP.NET Core'da Kullanım:
csharp
builder.Services.AddApiVersioning(options =>
{
options.ApiVersionReader = new HeaderApiVersionReader("api-version");
});
C. Query Parameter Versioning (En Az Önerilen)
Sürüm, query string'de taşınır:
text
GET /api/products?api-version=1.0
Avantajları:
-
Basit Test: Tarayıcıda kolayca denenebilir.
-
URI Sabit: Kaynak yolu değişmez.
Dezavantajları:
-
Önbellek Karmaşası: Aynı URL farklı yanıtlar döndürebilir; CDN ve tarayıcı önbelleği yanıltıcı olabilir.
-
Görsel Kirlilik: URL'ler uzar ve okunaksız hale gelir.
-
REST Prensiplerine Aykırı: Sürüm, kaynağın bir özelliği değil, temsilin bir özelliğidir.
ASP.NET Core'da Kullanım:
csharp
options.ApiVersionReader = new QueryStringApiVersionReader("api-version");
3. Karşılaştırma Tablosu
| Kriter | URL Path | Header | Query Param |
|---|---|---|---|
| Keşfedilebilirlik | ✅ Yüksek | ❌ Düşük | 🟡 Orta |
| Test Kolaylığı | ✅ Kolay | 🟡 Orta | ✅ Kolay |
| Önbellekleme | ✅ Doğal | ❌ Vary gerekir |
❌ Karmaşık |
| REST Uyumu | 🟡 Tartışmalı | ✅ Yüksek | ❌ Düşük |
| Gateway Yönlendirme | ✅ Kolay | 🟡 Orta | ✅ Kolay |
| Öneri | Varsayılan tercih | İç API'ler | Kaçının |
4. Birden Fazla Stratejiyi Birlikte Destekleme
ASP.NET Core, aynı anda birden fazla sürümleme yöntemini destekleyebilir:
csharp
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("api-version"),
new QueryStringApiVersionReader("api-version")
);
Bu, geçiş dönemlerinde esneklik sağlar; ancak uzun vadede tek bir stratejide karar kılmak, tutarlılık açısından daha iyidir.
5. Sürüm Yaşam Döngüsü: Deprecation
Bir sürümü kullanımdan kaldırırken, istemcileri bilgilendirmek kritiktir:
csharp
[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase { ... }
ReportApiVersions = true ile yanıtlara api-supported-versions ve api-deprecated-versions başlıkları eklenir; istemciler hangi sürümlerin desteklendiğini ve hangilerinin kullanımdan kaldırıldığını görebilir.
Sunset Header: Kullanımdan kaldırma tarihini bildirmek için Sunset başlığı eklenebilir:
text
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
6. En İyi Pratikler
| Pratik | Açıklama |
|---|---|
| Varsayılan sürüm belirleyin | Sürüm belirtilmeyen istekler için makul bir varsayılan (AssumeDefaultVersionWhenUnspecified = true). |
| Tek bir stratejide karar kılın | Uzun vadede URL path en tutarlı ve anlaşılır seçenektir. |
| Sürümü dokümante edin | OpenAPI/Swagger'da her sürüm için ayrı dokümantasyon sağlayın. |
| Deprecation süreci yönetin | Deprecated = true ve Sunset başlığı ile istemcileri zamanında bilgilendirin. |
| Sürüm sayısını sınırlayın | Aynı anda desteklenen sürüm sayısını (genellikle 2-3) kontrol altında tutun. |
Sonuç
API sürümleme, kaçınılmaz bir ihtiyaçtır; ancak doğru strateji seçimi, hem geliştirici deneyimini hem de sistemin sürdürülebilirliğini belirler. URL path versioning, keşfedilebilirlik, önbellekleme ve test kolaylığı açısından en dengeli ve yaygın seçenektir. Header versioning, temiz URI'ler sunar ancak görünürlüğü azaltır. Query parameter versioning ise önbellek ve REST uyumu açısından sorunludur; yalnızca geçici çözüm olarak düşünülmelidir. ASP.NET Core'un Asp.Versioning paketi, üç stratejiyi de esnek biçimde destekler. Unutmayın: Sürümleme bir teknik değil, bir iletişim sözleşmesidir; istemcilerinizi ne zaman ve nasıl etkileyeceğinizi baştan planlamak, sonradan kriz yönetmekten çok daha kolaydır.