- Roy Fielding'in REST'in orijinal makalesi, HTTP metotları ya da CRUD merkezli API kullanımını açıkça zorunlu kılmaz; REST başlangıçta hipermedya tabanlı (HATEOAS) sistem tasarımını vurgular
- Birçok kişinin RESTful API dediği yapılar, önemli REST kısıtlarını —özellikle hipermedya kullanımını— uygulamaz
- Kaynak, yalnızca basit veri yapıları ya da varlıklarla sınırlı değildir; URI ile tanımlanabilen her türlü kavramı kapsar
- Fielding'in 6 kuralına göre hipermedya merkezli gezinme, protokolden bağımsızlık ve medya tipine verilen önem temel unsurlardır
- Pratikte ise çoğu API, OpenAPI gibi belgelendirme araçlarının sağladığı kolaylık nedeniyle gerçek anlamda RESTful olmaktan çok RPC tarzına yakın tasarlanma eğilimindedir
Çoğu RESTful API neden gerçek anlamda RESTful değil
- Roy Fielding'in temel makalesi (2000), REST'i (Representational State Transfer) ağ tabanlı yazılım tasarımı için ideal bir stil olarak sunar ve bunu web'in başarısını destekleyen bir ilke olarak açıklar
- Makale, HTTP metotlarının kullanımı ya da CRUD merkezli tasarımı zorunlu kılmaz; REST yaklaşımında hipermedya (HATEOAS) tabanlı durum geçişleri, evrensel arayüz ve kaynak merkezli etkileşimlerin temel olduğunu vurgular
Hipermedya (HATEOAS) ve REST hakkındaki yanlış anlamalar
- Fielding, hipermedya olmadan bir API'nin RESTful olmadığını açıkça belirtir
- "Motor hipermetin tarafından yönlendirilmiyorsa RESTful değildir"
- HATEOAS, istemcinin sunucu yanıtına gömülü bağlantıları takip ederek davranışları dinamik biçimde keşfetmesi anlamına gelir
- Yalnızca HTTP/CRUD arayüzü kullanmak, REST'in özüyle aynı şey değildir
REST hakkında yaygın yanlış kanılar
- REST'in CRUD olduğu düşüncesi (oysa gerçekte çok daha geniş bir kavramdır)
- Kaynağın bir varlık, yani sunucudaki veri yapısı olduğu yanılgısı
- RESTful API'lerde fiil (verb) kullanılmaması gerektiği iddiası
- Bunlar yalnızca tasarım kararlarıdır; REST'in özüyle doğrudan ilgili değildir
Hipermedya güdümlü olmanın (HATEOAS) pratikte anlamı
- HATEOAS'ın amacı, istemci-sunucu bağlılığını en aza indirmektir
- Sunucunun URI yapısı değiştiğinde, istemcinin yeniden dağıtım maliyetini azaltarak ölçeklenebilirliği ve evrilebilirliği artırır
- İstemci, belge ya da ön bilgi olmadan yanıt içindeki hiperbağlantıları izleyerek davranışları keşfeder
{
"orderId": 123,
"_links": {
"self": { "href": "/orders/123" },
"cancel": { "href": "/orders/123/cancel", "method": "POST" }
}
}
- Yukarıdaki gibi, yanıtın içinde eylemler (bağlantılar) yer almalı ki yapı gerçek anlamda RESTful'a yaklaşsın
REST'te kaynak nedir?
- Kaynak, URI ile tanımlanabilen her şeydir
- Buna belge, görsel, fiziksel nesne, hizmet, soyut kavram vb. dahildir
- Sunucuda fiilen bir "kaynak" bulunmasından ziyade, URI üzerinden erişilebilen soyut bir eşleme yapısı vardır
- RFC 3986 da kaynağın kapsamını sınırlamaz
- Elektronik belgeler, görseller, bilgi kaynakları, hizmetler, hatta kişiler, tüzel kurumlar ve kitaplar bile buna dahil olabilir
Fielding'in tanımladığı RESTful API için 6 kural
- 1. Tek bir protokole bağımlı olmayın
- URI tabanlı tanımlama, tüm protokollerde kullanılabilir olmalıdır
- 2. Protokol standartlarını (ör. HTTP) keyfi biçimde değiştirmeyin
- Standardın eksik yönleri telafi edilebilir, ancak keyfi kural ekleme/değiştirme yapılmamalıdır
- 3. URI yapısı yerine medya tipi (format) tanımına odaklanın
- Veri biçimi ve bağlantı tanımına odaklanın; yol belirtimi/belgelendirmesi için gereksiz enerji harcamayın
- 4. Sabit URI adlandırmasını/hiyerarşisini hard-code etmeyin
- İstemci, sunucunun ad alanı yapısını tahmin edip sabitlememeli; bağlantı tabanlı keşif teşvik edilmelidir
- 5. Kaynak tipini (iç sınıflandırmayı) açığa çıkarmayın
- İç nesne tipi istemci için anlamsızdır; yalnızca standart medya tipleri ve bağlantılar görünür olmalıdır
- 6. Sadece yer imi niteliğinde bir başlangıç URI'si gerekir; geri kalanı yanıttaki bağlantılarla keşfedilmelidir
- İstemci yalnızca standart medya tiplerini bilmeli, durum geçişleri de her zaman sunucu yanıtındaki hiperbağlantılara dayanmalıdır
Kuralların yorumu ve pratik uygulama
- Protokol, URI ve tipe olan bağımlılık en aza indirilmeli ki yapı gerçek anlamda RESTful'a yaklaşsın
- API, veri ve bağlantıların biçimine (medya tipi) odaklanmalıdır; URI yapısı ya da adlandırma kurallarının istemci tarafından önceden bilinmesi gerekmez
- Örneğin
/users/123/activate gibi bir yolu belirtmek yerine, eylem yanıttaki "activate" bağlantısıyla yönlendirilmelidir
Gerçek RESTful API'lerin nadir olmasının nedeni
- OpenAPI, Swagger gibi araçların sağladığı kolaylık, geliştirme sahasında öncelik kazanır
- Otomatik belgelendirme, kod üretimi, doğrulama gibi somut avantajlar sunarlar
- İstemci ve sunucunun aynı ekip tarafından geliştirildiği SPA ortamlarında URI bağlılığı sorunu çok büyük görülmediğinden, HATEOAS'ın avantajları öne çıkmaz
- REST ilkelerini öğrenmenin başlangıç maliyeti ve dinamik bağlantı ayrıştırmanın karmaşıklığı gibi pratik engeller yüksektir
Sonuç
- Fielding'in kurallarına göre gerçek bir RESTful API için hipermedya (HATEOAS) tabanlı dinamik keşif zorunludur
- REST, yalnızca HTTP üzerinde CRUD uygulamak değildir; web'in ilkelerinde olduğu gibi gevşek bağlılık, evrilebilirlik ve dinamik durum geçişi odaklı bir mimaridir
- Gerçek dünyada ise çoğu zaman pratiklik ve ekibin koşullarına uygun tasarım daha önemli olabilir
- Dış geliştiricilere açık bir public API için HATEOAS önerilebilir; yalnızca iç kullanım içinse basit bir RPC tarzı da pratiktir
- API tasarımında asıl önemli olan, öğrenmesi kolay ve yanlış kullanması zor bir yapı kurmaktır; bunun mutlaka RESTful olması gerekmez
1 yorum
Hacker News görüşleri
Buradaki aşırı ilkeselliğe empati duyuyorum ve Fielding’in makalesini de ilgiyle okudum, ama bunun artık bitmiş bir savaş olduğunu düşündüm. “REST API” dendiği anda şunlardan neredeyse emin olabilirsiniz:
gibi bir fonksiyon yazarsınız ve bu fonksiyon
çağrısını yapar. Backend’de de
gibi bir fonksiyon vardır ve annotation ile URL eşlemesi açıklanır. Sonuçta bu RPC’dir. Sadece karmaşık ve sezgisel olmayan sistemler yerine daha manuel ve geliştiricinin kontrol edebildiği bir biçimde kalmıştır. Çoğu sorunun %80’inin sebebi insanların ISO 8601 tarih formatını kullanmamasıdır
Birinin bunu neden bir “savaş” olarak görüp önemsediğini pek anlayamıyorum. REST kavramı faydalı ama HATEOAS kısmı neredeyse hiç pratik değil ve sadece sorun çıkarıyor. Richardson maturity model’e bakarsanız REST’in zirvesinde HATEOAS benzeri unsurlar var. HATEOAS olmadan REST’e REST denemeyeceği mantığını da anlamıyorum; çok büyük bir ayırıcı özellik değil. HATEOAS pratikte neredeyse anlamsızsa bu ilkesel sınıflandırmaya takılmanın da anlamı yok gibi. Richardson maturity model
“Ekibin durum kodları hakkında aşırı tartışması ve HTTP spec’ten farklı kullanması çok yaygındır” kısmında, 401 Unauthorized’ın kimlik doğrulaması yapılmamış durumlarda, 403 Forbidden’ın ise yetki olmayan durumlarda kullanılması gerektiğini vurguluyorum
İnsanlara genelde makalede tanımlandığı anlamıyla REST’i gerçekten kastedip kastetmediklerini sorarım. Genelde anlamsız terimlerin hoyratça kullanılmasına pek tahammül etmeyen taraftayım. Sonunda “tamam, yani sadece bir web API diyorsun” deyip geçerim. Önemli olan fark, her böyle API’de tuhaf olan yönleri ayrıca anlamak zorunda olmanızdır
Benim için gerçekten önemli nüans şu: “hipermedya linkleri” çeşitli link type’larla (HTTP header’larında ya da dönen yanıtta) genel amaçlıymış gibi görünse de, pratikte bugünün REST’i de aynı şekilde çalışıyor. Örneğin kötü tasarım yüzünden “activate” yerine “enable” kullanılması gerekse, sonunda /api/v1/account/ID/activate’den /api/v2/account/ID/enable’a geçmeniz gerekir. Yani API’deki tüm aksiyonların anlamını bir yerlerde hardcode etmeniz gerekir (ve ikonlar, aksiyon açıklılarının çevirileri gibi ek meta veriler de eksiktir). Bu yaklaşımın “genelliği” aslında bir yanılsama
13 yıl önce ilk kez bir HTTP API geliştirmem gerektiğinde, gerçek REST’in ne olduğunu anlamak için Fielding’in makalesini baştan sona okudum, bir de RESTful Web Services Cookbook’u. Sonra Django’nun yerleşik kalıplarından kaçınarak bir REST API yaptım. Bu bir tür “cargo cult” yaklaşımıydı; gerçek REST’in hizmetimize ne avantaj sağlayacağını tam bilmiyordum. Sonraki birkaç yılda daha çeşitli HTTP API’leri geliştirince, pratikte REST’in saf teorisinin bana bir fayda sağlamadığını fark ettim. “Kendini keşfedebilen” ve “genel amaçlı istemcilerle uyumlu” vizyonu neredeyse gerçekleştirilemez ve somut olarak Fielding’in makalesi de bu konularda eksiksiz rehberlik sunmuyor. Gerçekten kendini keşfedebilen bir API yapmak için “endpoint discovery protocol”, “operation description”, “help message” gibi somut kurallar gerekir. Sonunda da bu kuralları anlayan özel bir istemci yazmanız gerekir ve o zaman genelleştirilmiş istemcinin avantajı ortadan kalkar. Yani gerçek dünyada hizmete özel API/JS kodu/CLI vb. için sonuçta sunucuya özel kod yazmak zorunda kaldık. Ayrıca iyi UX, REST’in idealleriyle çatışıyor; gerçekten iyi UX için frontend’de uygulamaya özel kod yazmanız gerekiyor. UI öğelerini standartlaştırmak teoride mümkün ama pratikte JavaScript gibi dillerle esnek UI kurmak çok daha kullanışlı
Kendini keşfedebilen API kavramının sınırlarına katılıyorum. Gerçek bir REST istemcisi pratikte gerçekleştirilemez. Tüm URL’lerin davranışını bilmek zorundadır ve yeni bir davranış eklenirse (ör. /cansofspam/123/frobnicate) istemci bunu net şekilde işleyemez. Sonunda istemci güncellemesi gerekir ya da bunu yok sayar ya da en fazla çok basit bir düğme eklenebilir (ör. Frobnicate). Bu yüzden gerçekte tam anlamıyla REST sunucuları ya da istemcileri yoktur. Pratikte çalışan şey, istemcinin keşif olmadan beklediği API’yi desteklemektir
API’lerin anlatılması zor olan pek çok yönü var. API kullanıcılarının ortalama yanıt gecikmesini, yeniden denenebilir hata kodlarını, aksiyonların atomik/idempotent olup olmadığını da bilmesi gerekir. HATEOAS bunları tek başına anlatamaz. Kusursuz REST uygulamak zorunda değilsiniz; temelde REST’in getirdiği fayda sadece isimleri/fiilleri HTTP metodları ve URL’lerle eşleyen ortak bir dil sunmasıdır. Buna rağmen ayrıntılı tasarım ve düşünülmesi gereken şeyler çok fazla. Örneğin HTTP spec’te izin verilen bir şey gerçek LB’de kullanılamayabiliyor ya da 500 hatalarının hangi ölçüte göre retry edileceği ve backoff mantığı gibi ayrıntılarla uğraşmanız gerekiyor
Tarayıcı zaten “genel amaçlı kod”un kendisi ve her gün kullandığımız en iyi UX’i sağlıyor. REST kavramı, sunucunun istemciye kod aktarabilmesini de içerir (güvenlik sorunları olsa da tarayıcılar ve standartlar bu alanın çoğunu çözmüş durumda). Fielding makalesindeki ilgili bölüm bağlantısı
Aslında REST tanımının yumuşatılmış sürümünde de pek bir fayda yok. “Bir kaynağı silmek için mutlaka DELETE kullanılmalı” o kadar da önemli değil. Sadece POST kullanılsa kime ne zorluk çıkarır?
“Kendini keşfedebilir” olmanın bir hedef olduğunu hiç düşünmedim, bunun ulaşılabilir bir şey olduğunu da sanmıyorum. Basit istemci tasarımında bu baştan aşırı bir beklenti. Özellikle TFA’da da “discoverable” kelimesi hiç geçmiyor
Bu API tasarım biçiminin gerçekten işe yaradığı yer, kullanıcı ile onun adına gezinme yapan bir agent’ın (ör. tarayıcı) API’de gezinip her yanıttaki media type ve link bilgisine göre etkileşime girebildiği durumlardır. Çoğu web API böyle bir use-case için değil, belirli bir UI/UX’i hedefleyen web uygulamaları için tasarlanır. Bu bilinçli bir tercihtir. Uygulama geliştiricisi uygulamanın hedefine ulaşmak için verinin sunumunu, UI akışını vb. tamamen kontrol etmek ister. REST API tasarımı, kullanıcının API kaynaklarını nasıl kullanacağını daha fazla yönlendirmesi gerektiği durumlarda gereklidir. Örnek olarak
Aslında HTML belgeleri tam olarak bu örnek. Belgenin içinde başka belgelere giden linkler var ve kullanıcı link üstündeki metne göre istediği yere gidebiliyor. Bu kullanıcı içinse buna UI diyoruz, uygulamalar içinse API diyoruz. HATEOAS’ın garip gelmesinin sebebi, API’yi doğrudan kullanıcıya dönük yapmaya çalışıyormuş gibi görünmesi. Oysa biz bunu zaten UI biçiminde yaşıyoruz
Saf REST kavramı çok akademik. Open/big data projelerinde gerçek performans ya da mimariyi kurmak için REST’e tam ulaşılıp ulaşılmadığından çok daha pratik yaklaşımlar önemli. Hatta akademisyenler bile sonunda bir çıktı üretmek zorunda olduğu için sadece kusursuz REST’e takılı kalmıyor
Bu tür API tasarımı sadece web sayfaları için değil başka istemciler kurarken de faydalı. GET ile kaynağı alıp alan/yol üzerinden değer çekmek, yeni URI oluşturup işlem yapmak gibi benzer kalıplarla çeşitli uygulamalar/CLI/UI’lar kurulabilir. non-SPA ise bunu doğrudan HTML ile yapmak da mümkün; sonuçta kullanıcı (veya user-agent) dönen temsildeki bilgileri dereference ediyor
API’leri tüketen yapay zekanın çağı gelirse bu use-case’in daha önemli hale gelip gelmeyeceğini merak ediyorum. API discoverability’si web uygulaması geliştiricilerinden çok yapay zekaya fayda sağlar. MCP’ye bakınca tool discoverability’nin ne kadar güçlü olduğu görülüyor. HATEOAS, bu tür çıplak API tüketiminde büyük potansiyel fayda sağlayabilir
ABD Ulusal Hava Durumu Servisi’nin RESTful API dokümantasyonu gibi kamusal bilgi API’leri iyi tasarlandığında gerçekten kullanımı çok rahat oluyor
“Gerçek hipermedya tabanlı istemciler kurmanın ilk bilişsel yükü çok büyük geldi ve sadece URI şablonlarını (/users/{id}/orders gibi) hardcode etmek daha rahat hissettirdi” fikriyle bağlantılı olarak, bunun gerçekten daha kolay olduğunu deneyimsel olarak hissettim. Saf REST ilkeleri çoğu durumda maliyet/fayda açısından zayıf kalıyor. Bir bakıma mikrodalgada tek bir düğmeyle menüyü/çalışma modunu/süreyi yönetmeye çalışmak gibi; alışıldık standart düğmeleri kullanmaktan çok daha zahmetli. Benim gerçekten kullandığım 2 düğmeli motor kod okuyucu da saçma derecede zor kullanılıyor. Hâlâ Fielding’in makalesinin mutlaka okunması gerektiğini söyleyen kültür biraz ciddi bir tartışma konusu. İyi bir fikir, çeşitli yollarla ve popüler bakış açısıyla kolay anlatılabilmeli. Örneğin fiziği anlamak için Newton’un Principia’sını mutlaka okuyun diyen kimse yok
RESTful/HATEOAS kalıplarını benimsemenin gerçekten değerli olması için bunları anlayan istemciler gerekir. htmx: hypermedia clients intercoolerjs: hatoeas-is-for-humans
UI tasarımcıları ekranın ayrıntılı görünümünü kontrol etmek ister. Kaynakta mümkün olan bazı aksiyonlar büyük düğme olabilir, bazıları menüye gizlenebilir ya da hiç gösterilmeyebilir. Eğer aksiyonlar her duruma göre API yanıtından dinamik olarak render edilirse tüm aksiyonlar aynı görünmeye başlar. Bu yüzden RESTful API’lerin yaygın web frontend UI’ları için uygun olmadığını düşünüyorum
Bu iddiada birçok hata var
Benim deneyimimde “RESTful API” geliştirme işi çoğu zaman UI ile doğrudan ilişkili değildi. Sadece gerçek bir UI gerekiyorsa API’nin kendisi zaten gereksiz olabilir; doğrudan sunucu güdümlü bir yaklaşım (eski DWR gibi) da yeterli olur
HATEOAS pratikte neredeyse hiç kullanılmıyor gibi görünüyor; neden hâlâ bu kadar tartışıldığını pek anlamıyorum. Gerçekten bunu kullanan yerler var mı ve sunucuyu önceden bilmeden çalışabilen nasıl bir “otomatik keşif yapan istemci” var merak ediyorum
ACME’nin (Let’s Encrypt’in protokolü) HATEOAS tabanlı olduğunu hatırlatmak isterim. Bu, fiilen HTTPS hizmetlerinin çoğunda kullanılıyor. HTTP’nin kendisi de aslında doğru kullanılırsa bir HATEOAS protokolü. “auto-discovery”, link type’lar veya ‘next’ gibi yapılarla kaynaklar arasında gezinilebilmesi demek. Tabii istemcinin “next”in ne anlama geldiğini önceden biliyor olması gerekir. LLM’ler de bu tür otomatik keşifte güçlü
Kurumsal ölçekli video gözetim sistemlerinde HATEOAS kullandım. Sürüm/yetki sorunlarını API seviyesinde çok iyi çözdü. Birkaç RFC’den de yararlanıldı. Ama bir numaralı sorun, insanların modelin bütünlüğünü bozacak şekilde “kolaylık” peşinde koşup daha fazla karmaşıklık üretmesiydi. Ayrıca JSON özünde bir hypertext formatı değil; application/json içine HATEOAS zorla yerleştirmeye çalışınca eğreti duruyordu
HATEOAS kullanarak yorum yazıyorsunuz ve ben de şu anda yanıt veriyorum. Bunu işleyen “sihirli otomatik keşif yapan istemci” tam olarak “web tarayıcısı”
htmx en gerçekçi deneme olabilir
OData gibi standartlar bile neredeyse hiç kullanılmıyor, üstelik çok da popüler değiller. HATEOAS’ın hem popülerlik hem standart tarafı zayıf olduğu için daha da yaygınlaşmıyor gibi
Bu tartışmada her zaman gözden kaçan şey backend API tüketicisinin türü. REST ve HATEOAS genelde backend’e doğrudan sahip olmayan üçüncü taraflar tükettiğinde daha anlamlı hale geliyor. Örneğin geleneksel HTML sayfalarının nihai tüketicisi tarayıcı kullanıcılarıdır. Son dönemdeki MCP de çeşitli JSON RPC API’lerinde “keşif ve yorumlama” ihtiyacı olduğu için ortaya çıktı. Buna karşılık frontend ile backend 1:1 bağlıysa REST’in faydası maliyetine göre büyük değil. Daha generic dokümantasyon/spec yazmanız gerekiyor ve pratikte separation of concerns’ü göz ardı edip üretkenliği artıran araçlar (trPC gibi) çoğu zaman daha kullanışlı olabiliyor. Prototip aşamasında end-to-end entegrasyon hızlı oluyor
Buna fazlasıyla katılıyorum; HATOEAS is for humans ve htmx: hypermedia clients yazılarını öneririm
*HATEOAS
HATEOAS ve schema reference’lar (XSD, JSON Schema vb.) ile dinamik keşif yapan istemcilerin mümkün olduğu iddiasına karşı, pratikte JSON Schema’daki "additionalProperties" gibi özellikler sorunun kökenini tekrar üretir. Dokümanı "out of band" yolla vermenin daha sağlam olduğunu düşünüyorum. O halde "_links" içinde sadece Swagger dokümanına giden tek bir link verip, istemcinin o self path bilgisini işlemesini sağlasak nasıl olurdu? Eğer durum buysa "_links" neden var olsun? Böyle karmaşık JSON belgelerini işleyebilen bir istemci varsa Swagger şablonları vb. ile çok daha yüksek bilgi yoğunluğu ve dinamiklik elde edilebilir. Sadece CRUD linkleriyle tüm API’yi doğru düzgün tanımlamak mümkün değil; JSON Schema ile her şeyi kapsamak da imkânsız
Buna sadece HTTP API derseniz herkes mutlu olur. REST zaten en başta API için tasarlanmış değildi. Başlangıçta REST, programlar için değil insanların tükettiği bilgi sistemleri içindi