Bölüm 3 / 11
REST nedir
Kaynak, temsil, durumsuzluk, Richardson olgunluk modeli.
11 dakikalık okuma
"REST API yazdım" cümlesi çoğu zaman "JSON döndüren bir HTTP servisi yazdım" anlamında kullanılıyor. İkisi aynı şey değil. REST bir protokol ya da kütüphane değil, bir mimari stildir: belirli kısıtları kabul edersen belirli kazanımlar elde edersin. Bu ders o kısıtları ve her birinin neyi mümkün kıldığını kuruyor — ve kısıtları çiğnediğinde tam olarak neyi kaybettiğini gösteriyor. Her iki ilanın da adını doğrudan andığı konu budur.
01Ne öğreneceksin
- REST'in ne olduğu ve ne olmadığı
- Altı mimari kısıt ve her birinin somut kazancı
- Kaynak, temsil ve URI ayrımı; kaynak tablo değildir
- Tek tip arayüzün dört alt kuralı
- Richardson olgunluk modeli: 0'dan 3'e
- HATEOAS: fikri, faydası ve pratikte neden az kullanıldığı
- Yaygın dört yanlış ve doğruları
- REST'in uygun olmadığı durumlar: RPC, GraphQL, gRPC
02REST ne değildir
REST (Representational State Transfer), Roy Fielding'in 2000 tarihli doktora tezinde tanımladığı bir mimari stildir. Bir standart, bir protokol ya da bir kütüphane değildir; web'in neden bu kadar iyi ölçeklendiğini açıklayan kısıtlar kümesidir. Fielding HTTP/1.1 spesifikasyonunun yazarlarındandır — yani REST, HTTP'nin üzerine sonradan giydirilmiş bir moda değil, HTTP'nin tasarım gerekçesidir.
Üç yaygın yanlış anlama:
- "JSON dönüyorsa REST'tir." Biçimin REST ile ilgisi yoktur; XML, HTML ya da düz metin döndüren bir servis de REST olabilir. İçerik pazarlığı (1. ders) REST'in parçasıdır, JSON zorunluluğu değil.
- "HTTP kullanıyorsa REST'tir." Tek bir
/apiucuna her şeyi POST eden bir servis HTTP kullanır ama REST değildir — kısıtların hiçbirini karşılamaz. - "REST bir performans tekniğidir." Doğrudan hız kazandırmaz; önbelleklenebilir ve durumsuz olmayı zorunlu kılarak ölçeklenebilirlik kazandırır.
REST'in vaadi şudur: kısıtlara uyarsan, servisin tanımadığı istemcilerle, tanımadığı ara katmanlarla (vekil, önbellek, ağ geçidi) ve yıllar boyunca çalışabilir. Kısıtları çiğnersen servis çalışmaya devam eder ama bu üç kazanımdan vazgeçersin.
03Altı kısıt
| Kısıt | Ne der | Ne kazandırır |
|---|---|---|
| İstemci–sunucu | Arayüz ile veri ve iş mantığı ayrılır | İki taraf bağımsız gelişir; aynı servisi web, mobil ve başka servisler kullanır |
| Durumsuzluk | Sunucu istekler arasında istemci durumu tutmaz | Yatay ölçekleme; istek hangi kopyaya düşerse düşsün aynı sonuç |
| Önbelleklenebilirlik | Yanıt, önbelleklenebilir olup olmadığını kendisi bildirir | Sunucuya hiç ulaşmayan istekler; gecikme ve yük düşer |
| Tek tip arayüz | Tüm kaynaklar aynı kurallarla adreslenir ve işlenir | Genel amaçlı istemciler ve araçlar çalışabilir; öğrenme maliyeti düşer |
| Katmanlı sistem | İstemci, doğrudan sunucuya mı yoksa aradaki bir katmana mı bağlandığını bilemez | Vekil, yük dengeleyici, ağ geçidi ve önbellek şeffafça araya girebilir |
| Talep üzerine kod (isteğe bağlı) | Sunucu istemciye çalıştırılabilir kod gönderebilir | Tarayıcıya JavaScript göndermek bunun örneğidir; API'lerde nadiren kullanılır |
Durumsuzluk en çok yanlış anlaşılan kısıttır. "Sunucu hiç veri tutmaz" demek
değildir — veritabanı elbette vardır. Demek istediği, istemciye ait oturum durumunun
sunucunun belleğinde tutulmamasıdır. Kimlik bilgisi her istekte
Authorization başlığıyla gelir; "kullanıcı hangi adımdaydı" bilgisi ya
istemcide ya da paylaşılan bir depoda durur. 5. hattın 13. dersinde ölçeklemenin ön şartı
olarak koyduğumuz kural budur; bedeli, her isteğin biraz daha büyük olmasıdır.
Katmanlı sistem ile önbelleklenebilirlik birlikte çalışır: istemci
cevabın nereden geldiğini bilmediği için, arada duran bir önbellek ya da ağ geçidi
servisi değiştirmeden devreye girebilir. Bu iki kısıt, 1. derste öğrendiğin
Cache-Control ve ETag başlıklarının neden protokol düzeyinde
tanımlandığını açıklar.
04Kaynak ve temsil
REST'in merkezindeki kavram kaynaktır. Kaynak, adlandırmaya değer herhangi bir şeydir: bir sipariş, bir kullanıcı, bir raporun kendisi, hatta "bugünün döviz kuru" gibi zamanla değişen bir kavram. Üç kavramı ayırmak gerekir:
- Kaynak — soyut varlık: "42 numaralı sipariş".
- Tanımlayıcı (URI) — o kaynağın adresi:
/api/siparisler/42. - Temsil — kaynağın belirli bir andaki, belirli bir biçimdeki hâli: aynı siparişin JSON temsili, XML temsili, özet temsili.
Bir kaynağın birden çok temsili olabilir ve istemci hangisini istediğini içerik pazarlığıyla söyler. Sunucu ile istemci arasında gidip gelen şey kaynağın kendisi değil, temsilidir — stilin adı buradan gelir.
Kaynak, veritabanı tablosu değildir. Bu, en sık yapılan modelleme hatasıdır. Tabloları birebir uca çevirmek, istemciyi senin şemana bağlar ve iş kavramlarını görünmez kılar:
// Tablo yansıması — istemci sekiz çağrı yapıp kendi birleştirmesini yapar
GET /siparis_basliklari/42
GET /siparis_satirlari?siparis_id=42
GET /musteriler/17
// Kaynak modellemesi — iş kavramı tek adreste
GET /api/siparisler/42 → başlık + satırlar + müşteri özeti
GET /api/siparisler/42/satirlar → satırlar ayrıca da adreslenebilir
Ölçüt şudur: kaynak, istemcinin konuştuğu dildeki kavramdır. "Sepet", "iade talebi", "aylık rapor" birer kaynaktır; bunların veritabanında kaç tabloya dağıldığı istemciyi ilgilendirmez. 4. hattaki toplam kök (aggregate root) fikri burada doğrudan işe yarar: bir toplam kök genelde iyi bir kaynak sınırıdır.
Bir kaynak tekil (/api/siparisler/42) ya da koleksiyon
(/api/siparisler) olabilir; koleksiyon da kendi başına bir kaynaktır ve
sorgu dizesiyle filtrelenmiş hâlleri o koleksiyonun farklı temsilleridir. Adlandırma
kuralları 4. dersin konusudur.
05Tek tip arayüz ve olgunluk modeli
Tek tip arayüz, REST'i diğer stillerden ayıran kısıttır ve dört alt kuralı vardır:
(1) kaynaklar URI ile tanımlanır, (2) kaynaklar temsilleri üzerinden
işlenir, (3) mesajlar kendini açıklar (metot, durum kodu, Content-Type
ne yapıldığını söyler), (4) uygulama durumu bağlantılarla yürütülür (HATEOAS).
Bir servisin bu kurallara ne kadar uyduğu Richardson olgunluk modeli ile anlatılır. Model bir puanlama değil, bir konuşma dilidir; mülakatta "seviye 2" demek, uzun bir açıklamayı tek kelimeye indirir.
| Seviye | Adı | Ne yapar | Örnek |
|---|---|---|---|
| 0 | Tek uç (RPC tüneli) | Her şey tek adrese POST edilir; ne yapılacağı gövdede yazar | POST /api {"islem":"siparisGetir","no":42} |
| 1 | Kaynaklar | Farklı kaynaklar farklı adreslerde, ama metot hâlâ tek | POST /siparisler/42 {"islem":"getir"} |
| 2 | HTTP fiilleri ve kodları | Metotlar semantiğine göre, durum kodları doğru kullanılır | GET /siparisler/42 → 200 · DELETE → 204 |
| 3 | HATEOAS | Yanıt, bir sonraki adımların bağlantılarını taşır | Yanıtta "iptal": "/siparisler/42/iptal" |
Gerçek dünya seviye 2'dedir ve bu bilinçli bir tercihtir. Seviye 2, kazanımların
neredeyse tamamını verir: önbellek çalışır, ara katmanlar anlar, araçlar (tarayıcı,
curl, Postman, ağ geçitleri) servisi tanır.
HATEOAS (Hypermedia as the Engine of Application State) fikri şudur: istemci adresleri kendi içine gömmez; yanıtta gelen bağlantıları izler. Böylece sunucu adres yapısını değiştirebilir ve istemci kırılmaz; ayrıca hangi işlemin o an mümkün olduğu yanıtta görünür.
{
"no": 42,
"durum": "Onaylandi",
"_links": {
"self": { "href": "/api/siparisler/42" },
"iptal": { "href": "/api/siparisler/42/iptal", "method": "POST" },
"fatura": { "href": "/api/siparisler/42/fatura" }
}
}
Bu örnekte sipariş kargolanmışsa iptal bağlantısı hiç gönderilmez — istemci
"iptal edilebilir mi" kuralını yeniden yazmak zorunda kalmaz. Fikir güçlüdür ama pratikte
az kullanılır; gerekçeleri dürüstçe söylenmelidir: istemciler genellikle adresleri yine de
gömer, yanıt boyutu büyür, istemci kütüphaneleri ve kod üreticileri (2. hattaki OpenAPI
araçları) bağlantı izlemeye göre değil sabit sözleşmeye göre çalışır, ve ek karmaşıklığın
karşılığı çoğu iç API'de görülmez. Kamu ve kurumsal projelerde HATEOAS'ın en çok işe
yaradığı yer, durum makinesi olan iş akışlarıdır: hangi geçişin mümkün olduğunu
yanıtta bildirmek, istemci ile sunucu arasındaki kural tekrarını gerçekten
kaldırır.
06Yaygın yanlışlar ve REST'in sınırı
Dört yanlış, kod incelemelerinde en sık düzeltilenlerdir:
- URL'de fiil kullanmak.
POST /api/siparisOlustur,GET /api/getSiparis?no=42— bunlar RPC'dir. Fiil zaten HTTP metodudur; URL ismi taşır. Doğrusu:POST /api/siparisler,GET /api/siparisler/42. - Her şeyi POST etmek. Okuma isteğini POST'la yapmak önbelleği devre dışı bırakır, isteği idempotent olmaktan çıkarır ve ara katmanların hiçbir şey anlamamasına yol açar (seviye 0'a düşüş). Gövdesi zorunlu olan karmaşık aramalar bunun meşru istisnasıdır ve 4. derste ele alınır.
- 200 içinde hata döndürmek. 1. derste söylediğimiz kuralın REST tarafı: durum
kodu mesajın kendini açıklamasının parçasıdır.
200 OK+ gövdede"hata": true, önbelleklerin hatalı yanıtı saklamasına ve izleme panolarının hata oranını sıfır göstermesine yol açar. - Sunucuda oturum durumu tutmak. Bellekte tutulan "sihirbazın hangi adımı" bilgisi, servisi tek kopyaya mahkûm eder — 5. hattın 13. dersindeki tabloyu hatırla.
Bir de meşru esneklikler vardır ve bunları savunabilmek kıdem işaretidir:
saf REST'e sığmayan eylemler (POST /siparisler/42/iptal) yaygın ve kabul
gören bir kalıptır; alternatifi durum alanını PATCH ile değiştirmektir ama
"iptal" bir iş olayıdır ve yan etkileri vardır. Ölçüt: eylem, kaynağın bir alt
kaynağı gibi adlandırılabiliyorsa tutarlılık korunur.
REST ne zaman uygun değildir? Üç durum:
| İhtiyaç | Daha uygun | Neden |
|---|---|---|
| İstemci her ekranda farklı alan kümesi istiyor; az sayıda çağrıda çok veri | GraphQL | Aşırı/eksik veri çekme sorununu istemci tarafında çözer (8. ders) |
| Servisler arası yüksek hacimli, düşük gecikmeli iç iletişim | gRPC | İkili biçim, HTTP/2 akışları, üretilen sözleşme (19. hat) |
| Sunucudan istemciye sürekli olay akışı | WebSocket / SSE | İstek–yanıt modeli buna uygun değildir |
Ayrıca kamu ve kurumsal entegrasyonlarda hâlâ karşına çıkacak SOAP vardır: XML tabanlı, WSDL ile sözleşmesi tanımlanan, taşıma protokolünden bağımsız bir standarttır. REST'e göre daha ağır ve daha törenlidir; karşılığında güçlü şema doğrulaması ve standartlaşmış güvenlik/işlem uzantıları sunar. Karşı taraf SOAP konuşuyorsa tartışma yoktur, uyulur (2. ders).
07Mini alıştırma — Servisini seviyelendir
Yaklaşık 20 dakika · kâğıt + 5. hatta yazdığın servis
- Kaynakları çıkar: aracının iş kavramlarını (sipariş, satır, müşteri, rapor) listele ve her biri için tekil/koleksiyon URI'lerini yaz. Hiçbirinde fiil kullanma.
- Tablo mu kaynak mı: yazdığın URI'lerden hangileri veritabanı tablosunun yansımasıydı? En az birini iş kavramına göre yeniden adlandır.
- Seviye biç: 5. hatta yazdığın uçları Richardson modeline göre puanla. Seviye 2'ye çıkmak için eksik olan ne?
- Seviye 0 deneyi:
POST /api {"islem":"getir","no":42}biçiminde tek uç yaz ve normalGETucuyla karşılaştır: hangisi önbelleklenebilir, hangisini tarayıcı adres satırından çağırabiliyorsun? - Durumsuzluk sınavı: servisinde bellekte tutulan bir şey var mı? Varsa uygulamayı iki portta çalıştırıp istekleri dönüşümlü gönder ve sonucu gözle.
- Eylem modelle: "siparişi iptal et" işlemini üç biçimde tasarla:
POST /siparisler/42/iptal,PATCH /siparisler/42vePOST /siparisIptal. Her birinin artı ve eksisini birer cümleyle yaz. - HATEOAS denemesi: sipariş yanıtına
_linksekle ve iptal bağlantısını yalnız durum uygunsa gönder. İstemci bu sayede hangi kuralı yazmak zorunda kalmıyor? - Kendini açıklama: hatalı bir isteğe 200 +
{"hata":true}döndüren bir uç yaz, sonra doğru durum koduna çevir. Tarayıcının ağ sekmesinde ikisi nasıl görünüyor?
08Kendini sına
REST nedir? "JSON dönen HTTP servisi" ile aynı şey mi?
REST, Roy Fielding'in tanımladığı bir mimari stildir — bir protokol, standart ya da kütüphane değil. Belirli kısıtları kabul edersen belirli kazanımlar elde edersin; web'in neden ölçeklendiğini açıklar ve HTTP'nin tasarım gerekçesidir. "JSON dönüyorsa REST'tir" yanlıştır: biçimin stille ilgisi yoktur, XML ya da HTML döndüren bir servis de REST olabilir. "HTTP kullanıyorsa REST'tir" de yanlıştır: tek bir uca her şeyi POST eden servis HTTP kullanır ama hiçbir kısıtı karşılamaz. REST doğrudan hız da kazandırmaz; durumsuz ve önbelleklenebilir olmayı zorunlu kılarak ölçeklenebilirlik, ara katmanlarla uyum ve uzun ömürlü sözleşme kazandırır. Kısıtları çiğneyen servis çalışmaya devam eder, yalnız bu kazanımlardan vazgeçer.
REST'in kısıtlarını sayın. Durumsuzluk tam olarak ne demek?
Altı kısıt: istemci–sunucu (arayüz ile veri/iş mantığı ayrılır, iki taraf bağımsız gelişir), durumsuzluk, önbelleklenebilirlik (yanıt önbelleklenip önbelleklenemeyeceğini kendisi bildirir), tek tip arayüz, katmanlı sistem (istemci doğrudan sunucuya mı ara katmana mı bağlandığını bilemez, böylece vekil ve önbellek şeffafça girer) ve isteğe bağlı talep üzerine kod. Durumsuzluk "sunucu hiç veri tutmaz" demek değildir — veritabanı elbette vardır. Demek istediği, istemciye ait oturum durumunun sunucunun belleğinde tutulmamasıdır: her istek kendini anlamlı kılan her şeyi yanında taşır, kimlik her istekte gelir, ara durum istemcide ya da paylaşılan bir depoda durur. Kazancı yatay ölçeklemedir; istek hangi kopyaya düşerse düşsün sonuç aynıdır. Bedeli, her isteğin biraz daha büyük olmasıdır.
Kaynak nedir, temsilden farkı ne? Kaynakları nasıl modellersin?
Kaynak adlandırmaya değer soyut varlıktır ("42 numaralı sipariş"); URI onun adresidir; temsil o kaynağın belirli bir andaki, belirli bir biçimdeki hâlidir. Bir kaynağın birden çok temsili olabilir ve istemci hangisini istediğini içerik pazarlığıyla söyler; taraflar arasında giden şey kaynağın kendisi değil temsilidir. En sık yapılan modelleme hatası kaynağı tablo sanmaktır: tabloları birebir uca çevirmek istemciyi şemaya bağlar, iş kavramlarını görünmez kılar ve istemciyi çok sayıda çağrı yapıp birleştirme yapmaya zorlar. Ölçüt, kaynağın istemcinin konuştuğu dildeki kavram olmasıdır: sepet, iade talebi, aylık rapor birer kaynaktır ve kaç tabloya dağıldıkları istemciyi ilgilendirmez. Tasarım desenleri hattındaki toplam kök fikri iyi bir kaynak sınırı verir. Koleksiyonlar da kendi başına kaynaktır; filtrelenmiş hâlleri o koleksiyonun farklı temsilleridir.
Richardson olgunluk modelini anlatın. HATEOAS neden yaygın değil?
Model, bir servisin tek tip arayüz kısıtına ne kadar uyduğunu anlatan ortak bir dildir. Seviye 0: her şey tek adrese POST edilir, ne yapılacağı gövdede yazar — RPC tüneli. Seviye 1: farklı kaynaklar farklı adreslerde, ama metot hâlâ tek. Seviye 2: HTTP metotları semantiğine göre ve durum kodları doğru kullanılır. Seviye 3: HATEOAS — yanıt, bir sonraki adımların bağlantılarını taşır. Gerçek dünya bilinçli olarak seviye 2'dedir, çünkü kazanımların neredeyse tamamını verir: önbellek çalışır, ara katmanlar ve araçlar servisi tanır. HATEOAS'ın fikri istemcinin adresleri gömmek yerine yanıttaki bağlantıları izlemesi ve hangi işlemin o an mümkün olduğunun yanıtta görünmesidir — sipariş kargolanmışsa iptal bağlantısı hiç gönderilmez ve istemci bu kuralı tekrar yazmaz. Yaygın olmamasının gerçek sebepleri: istemciler yine de adres gömer, yanıt büyür, kod üreticileri ve istemci kütüphaneleri sabit sözleşmeye göre çalışır ve ek karmaşıklığın karşılığı çoğu iç API'de görülmez. En işe yaradığı yer durum makinesi olan iş akışlarıdır.
09Sınav köşesi
Yazılıda nasıl sorulur
(1) REST nedir; mimari stil ile protokol farkı. (2) REST kısıtlarını sayınız (en az dördü beklenir). (3) Durumsuzluk nedir, ne kazandırır. (4) Kaynak, URI ve temsil kavramlarını tanımlayınız. (5) Richardson olgunluk modelinin seviyeleri. (6) HATEOAS nedir, neden az kullanılır. (7) Verilen URL listesinden REST'e uygun olmayanları bulunuz ve düzeltiniz (klasik soru). (8) REST ile SOAP karşılaştırması. (9) REST ile GraphQL karşılaştırması. (10) Kaynak tasarımı: verilen bir iş senaryosu için URI ve metot tablosu yazınız.
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Tanım — "REST bir protokol değil, mimari stildir; Fielding'in tanımladığı kısıtlara uyunca servis ölçeklenebilir, ara katmanlarla uyumlu ve uzun ömürlü olur." (2) Kısıtlar — "istemci–sunucu ayrımı, durumsuzluk, önbelleklenebilirlik, tek tip arayüz, katmanlı sistem; durumsuzluk sunucunun oturum durumu tutmaması demektir ve yatay ölçeklemenin ön şartıdır." (3) Model — "merkezde kaynak vardır; URI kaynağı adresler, temsil onun bir andaki biçimidir. Kaynakları tablolardan değil iş kavramlarından çıkarırım." (4) Olgunluk — "pratikte seviye 2 hedeflerim: doğru metot, doğru durum kodu. HATEOAS'ı yalnız durum makinesi olan akışlarda değerli buluyorum." (5) Kıdem vuruşu — "URL'de fiil kullanmam, okumayı POST'la yapmam ve hatayı 200 içinde göndermem — bunların üçü de mesajın kendini açıklamasını bozar ve önbellekle izlemeyi yanıltır. Ama saf REST'e takılıp kalmam: 'iptal' gibi iş olaylarını alt kaynak olarak modellerim, ve istemci her ekranda farklı alan kümesi istiyorsa GraphQL'i, servisler arası yoğun trafikte gRPC'yi tartışırım."
