---
title: 'REST İlkeleri'
source: 'https://academia.sh/tr/kurslar/api-tasarimi/rest-ilkeleri'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:45+00:00'
license: 'CC BY-SA 4.0'
---

# REST İlkeleri

Kaynak temelli biçemin arkasındaki kısıt kümesi: durumsuzluğun aynı isteği iki ayrı süreçte sınayarak gösterilmesi, tek biçim arayüzün kaynak adını bilmeyen bir istemciyle ölçülmesi, önbelleklenebilirliğin varlık etiketiyle taşınan bayta etkisi ve katmanlı sistemin bu kısıtlardan doğması.

Önceki ders kaynak temelli biçemi ölçtü ve en pahalı seçenek olarak buldu: beş istek, kırk
yedi taşınan alan, %15 kullanım. Buna karşın ödünç servislerinin çoğu bu biçemle yazılır.
Çelişki, ölçüme girmeyen özelliklerden gelir.

O özellikler bir kısıt kümesinin sonuçlarıdır. **REST** (representational state transfer),
bir protokol ya da bir kitaplık değil, bir arayüzün uyabileceği kısıtların adıdır. Bu ders
kısıtlardan üçünü — durumsuzluk, tek biçim arayüz ve önbelleklenebilirlik — çalıştırarak
sınar.

## Kısıtlar ve Kaynak Yönelimi

REST'in temel birimi **kaynaktır** (resource): bir adla anılabilen her kavram. Kütüphane
servisinde kitap, üye, ödünç ve raf birer kaynaktır. İstemcinin gördüğü şey kaynağın kendisi
değil, bir **gösterimidir** (representation): o kaynağın belirli bir biçimdeki kopyası.
Aynı kaynağın JSON gösterimi de HTML gösterimi de olabilir; ikisi de aynı şeyi anlatır.

Kısıtlar bu birimin üstüne kurulur:

- **İstemci–sunucu ayrımı:** iki taraf ayrı gelişir, aralarındaki tek bağ arayüzdür.
- **Durumsuzluk:** her istek kendi kendine yeter; sunucu istekler arasında istemciye ait
  bir bağlam tutmaz.
- **Önbelleklenebilirlik:** yanıt, saklanabilir olup olmadığını kendisi bildirir.
- **Tek biçim arayüz:** kaynaklar aynı yolla adlandırılır ve aynı yöntemlerle işlenir.
- **Katmanlı sistem:** istemci, karşısındaki tarafın son sunucu mu ara bir katman mı
  olduğunu bilmek zorunda değildir.

Aşağıdaki sunucu bu kısıtları hem uygulayan hem de ihlal eden iki tasarımı yan yana taşır;
bağlantı noktasını dışarıdan aldığı için iki kopyası birlikte çalıştırılabilir.

```js
// sunucu.mjs — ayni kodun iki kopyasi calistirilabilsin diye baglanti noktasi disaridan verilir:
// node sunucu.mjs 8461
import { createServer } from "node:http";
import { createHash, randomUUID } from "node:crypto";

const KAPI = Number(process.argv[2] ?? 8461);
const KAYNAKLAR = {
  kitaplar: [{ id: "978-0262033848", ad: "Algoritmalara Giris" }, { id: "978-0201896831", ad: "Programlama Sanati" },
             { id: "978-0131103627", ad: "C Programlama Dili" }, { id: "978-0596007126", ad: "Head First" }],
  uyeler:   [{ id: "u1", ad: "Ayse Kaya" }, { id: "u2", ad: "Baris Demir" }],
  oduncler: [{ id: "o11", uye: "u1", kitap: "978-0262033848" }],
  raflar:   [{ id: "R-12", kat: 2 }, { id: "R-03", kat: 1 }],   // sonradan eklenen kaynak
};
const imlecler = new Map();          // sunucu tarafinda tutulan durum: yalniz bu kopyada var

const json = (yanit, kod, govde, etiket) => {
  const metin = JSON.stringify(govde);
  yanit.setHeader("Content-Type", "application/json; charset=utf-8");
  yanit.setHeader("X-Surec", process.pid);
  if (etiket) yanit.setHeader("ETag", etiket);
  yanit.setHeader("Content-Length", Buffer.byteLength(metin));
  yanit.writeHead(kod).end(metin);
};
const etiketle = (govde) =>
  `"${createHash("sha256").update(JSON.stringify(govde)).digest("hex").slice(0, 16)}"`;

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const adres = new URL(istek.url, "http://yerel");
  const [, ilk, ikinci] = adres.pathname.split("/");

  if (ilk === "") return json(yanit, 200, { kaynaklar: Object.keys(KAYNAKLAR) });

  // Durumlu sayfalama: imlec sunucuda tutulur, istek yalniz imlec kimligini tasir.
  if (ilk === "imlec" && istek.method === "POST") {
    const kimlik = randomUUID().slice(0, 8);
    imlecler.set(kimlik, 0);
    return json(yanit, 201, { imlec: kimlik });
  }
  if (ilk === "imlec" && istek.method === "GET") {
    if (!imlecler.has(ikinci)) return json(yanit, 404, { hata: "imlec_bilinmiyor" });
    const ofset = imlecler.get(ikinci);
    imlecler.set(ikinci, ofset + 2);
    return json(yanit, 200, { ofset, sayfa: KAYNAKLAR.kitaplar.slice(ofset, ofset + 2) });
  }

  // Tek bicim arayuz: her kaynak ayni yolla adlandirilir, ayni yontemlerle okunur.
  const kume = KAYNAKLAR[ilk];
  if (!kume) return json(yanit, 404, { hata: "kaynak_yok" });
  if (ikinci) {
    const oge = kume.find((k) => k.id === ikinci);
    if (!oge) return json(yanit, 404, { hata: "bulunamadi" });
    const etiket = etiketle(oge);
    if (istek.headers["if-none-match"] === etiket) {         // kosullu istek: govde gonderilmez
      yanit.setHeader("ETag", etiket);
      yanit.setHeader("X-Surec", process.pid);
      return yanit.writeHead(304).end();
    }
    return json(yanit, 200, oge, etiket);
  }
  // Durumsuz sayfalama: sayfa bilgisi istegin icindedir.
  const ofset = Number(adres.searchParams.get("ofset") ?? 0);
  const adet = Number(adres.searchParams.get("adet") ?? kume.length);
  return json(yanit, 200, { ofset, adet, oge: kume.slice(ofset, ofset + adet) });
}).listen(KAPI, "127.0.0.1", () => console.log(`kopya :${KAPI} pid=${process.pid}`));
```

Ölçüm istemcisi üç bölümden oluşur ve istekleri iki kopya arasında dönüşümlü dağıtır.

```js
// olc.mjs — uc ilkeyi sinar: durumsuzluk, tek bicim arayuz, onbelleklenebilirlik
import { Agent, request } from "node:http";
import { connect } from "node:net";

let alinan = 0;
const ajan = new Agent({ keepAlive: true });
ajan.createConnection = (s) => { const b = connect(s); b.on("data", (p) => (alinan += p.length)); return b; };
const iste = (kapi, yol, secenek = {}) => new Promise((coz, hata) => {
  const r = request({ agent: ajan, host: "127.0.0.1", port: kapi, path: yol,
    method: secenek.yontem ?? "GET", headers: secenek.basliklar ?? {} }, (y) => {
    let m = ""; y.on("data", (p) => (m += p));
    y.on("end", () => coz({ kod: y.statusCode, etiket: y.headers.etag, surec: y.headers["x-surec"],
                            govde: m ? JSON.parse(m) : null }));
  });
  r.on("error", hata); r.end();
});

// 1) DURUMSUZLUK — ayni istek iki ayri surecte ayni yaniti veriyor mu?
console.log("-- 1) durumsuzluk: istekler iki kopya arasinda donusumlu dagitiliyor --");
const { govde: kur } = await iste(8461, "/imlec", { yontem: "POST" });
for (const kapi of [8461, 8462, 8461]) {
  const y = await iste(kapi, `/imlec/${kur.imlec}`);
  console.log(`  durumlu   :${kapi} pid=${y.surec} -> ${y.kod} ${JSON.stringify(y.govde).slice(0, 62)}`);
}
for (const kapi of [8461, 8462, 8461]) {
  const y = await iste(kapi, "/kitaplar?ofset=2&adet=2");
  console.log(`  durumsuz  :${kapi} pid=${y.surec} -> ${y.kod} ${JSON.stringify(y.govde).slice(0, 62)}`);
}

// 2) TEK BICIM ARAYUZ — ayni istemci kodu, kaynak adini bilmeden her kaynagi okuyor mu?
console.log("\n-- 2) tek bicim arayuz: kaynak adlari sunucudan ogreniliyor --");
const { govde: kok } = await iste(8461, "/");
for (const ad of kok.kaynaklar) {
  const liste = await iste(8461, `/${ad}`);
  const ilk = liste.govde.oge[0];
  const tek = await iste(8461, `/${ad}/${ilk.id}`);
  console.log(`  ${ad.padEnd(9)} liste=${liste.kod} adet=${liste.govde.oge.length}` +
              `  tek=${tek.kod} etiket=${tek.etiket}`);
}
const yok = await iste(8461, "/subeler");
console.log(`  subeler   liste=${yok.kod} ${JSON.stringify(yok.govde)}`);

// 3) ONBELLEKLENEBILIRLIK — kosullu istek tasinan bayti ne kadar dusuruyor?
console.log("\n-- 3) onbelleklenebilirlik: ayni kayit on kez isteniyor --");
const YOL = "/kitaplar/978-0262033848";
alinan = 0;
for (let i = 0; i < 10; i++) await iste(8461, YOL);
const kosulsuz = alinan;
alinan = 0;
const ilk = await iste(8461, YOL);
for (let i = 0; i < 9; i++) await iste(8461, YOL, { basliklar: { "If-None-Match": ilk.etiket } });
const kosullu = alinan;
const son = await iste(8461, YOL, { basliklar: { "If-None-Match": ilk.etiket } });
console.log(`  kosulsuz  10 istek -> ${kosulsuz} B`);
console.log(`  kosullu   10 istek -> ${kosullu} B  (son yanit ${son.kod}, kazanc %${
  Math.round((100 * (kosulsuz - kosullu)) / kosulsuz)})`);
ajan.destroy();
```

```bash
node sunucu.mjs 8461 & a=$!
node sunucu.mjs 8462 & b=$!
for k in 8461 8462; do
  curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null "http://127.0.0.1:$k/"
done
node olc.mjs
kill $a $b
```

```
kopya :8461 pid=8582
kopya :8462 pid=8583
-- 1) durumsuzluk: istekler iki kopya arasinda donusumlu dagitiliyor --
  durumlu   :8461 pid=8582 -> 200 {"ofset":0,"sayfa":[{"id":"978-0262033848","ad":"Algoritmalara
  durumlu   :8462 pid=8583 -> 404 {"hata":"imlec_bilinmiyor"}
  durumlu   :8461 pid=8582 -> 200 {"ofset":2,"sayfa":[{"id":"978-0131103627","ad":"C Programlama
  durumsuz  :8461 pid=8582 -> 200 {"ofset":2,"adet":2,"oge":[{"id":"978-0131103627","ad":"C Prog
  durumsuz  :8462 pid=8583 -> 200 {"ofset":2,"adet":2,"oge":[{"id":"978-0131103627","ad":"C Prog
  durumsuz  :8461 pid=8582 -> 200 {"ofset":2,"adet":2,"oge":[{"id":"978-0131103627","ad":"C Prog

-- 2) tek bicim arayuz: kaynak adlari sunucudan ogreniliyor --
  kitaplar  liste=200 adet=4  tek=200 etiket="9ad1f08038f3c1fa"
  uyeler    liste=200 adet=2  tek=200 etiket="5d5092c2e89cc21d"
  oduncler  liste=200 adet=1  tek=200 etiket="de830fee13237e05"
  raflar    liste=200 adet=2  tek=200 etiket="8703c0e7db6c6311"
  subeler   liste=404 {"hata":"kaynak_yok"}

-- 3) onbelleklenebilirlik: ayni kayit on kez isteniyor --
  kosulsuz  10 istek -> 2240 B
  kosullu   10 istek -> 1277 B  (son yanit 304, kazanc %43)
```

Süreç kimlikleri her çalıştırmada değişir. Kimlik `X-Surec` başlığında taşındığı için
basamak sayısı toplam baytı da birkaç bayt kaydırır; oran ise değişmez. Bayt sayıları
ayrıca çalışma zamanının yazdığı başlık kümesine bağlıdır.

## Durumsuzluk: İsteğin Kendi Kendine Yetmesi

İlk bölüm iki sayfalama tasarımını aynı koşulda sınar: istekler iki sunucu kopyası arasında
dönüşümlü dağıtılır. Bu, önceki kursta kurulan vekil arkasındaki çoklu kopya kurulumunun
küçük bir örneğidir.

**Durumlu tasarımda** imleç sunucunun belleğinde durur. İlk istek 8461'e düşer ve çalışır.
İkinci istek 8462'ye düştüğünde aynı imleç orada bulunmadığı için `404` alınır. Üçüncü istek
yine 8461'e düştüğünde ikinci sayfa gelir — yani ikinci kopyanın verdiği hata, imlecin
durumunu da bozmuştur: istemci bir sayfayı hiç görmeden atlamıştır.

**Durumsuz tasarımda** sayfa bilgisi isteğin içindedir. Aynı istek üç kez, iki ayrı sürece
gitmiş ve üçünde de aynı gövdeyi vermiştir. Sunucu kimliğinin yanıt üzerinde hiçbir etkisi
yoktur.

Durumsuzluğun bedeli her istekte bağlamın yeniden gönderilmesidir: kimlik bilgisi,
sayfalama konumu, süzme ölçütleri her seferinde taşınır. Karşılığında elde edilen şey,
sunucu kopyalarının birbirinin yerine geçebilmesidir. Bir kopya düştüğünde istek başka bir
kopyaya yönlendirilebilir, kopya sayısı yük altında artırılabilir ve hiçbiri istemcinin
oturumunu taşımak zorunda kalmaz.

## Tek Biçim Arayüz: Aynı İstemci Kodu, Dört Kaynak

İkinci bölümdeki istemci hiçbir kaynak adını bilmez. Kök adresten kaynak listesini okur,
sonra her kaynak için aynı iki isteği yapar: koleksiyonu al, ilk ögenin kimliğiyle tek
kaydı al. Dördü de aynı kodla okunmuştur; sonradan eklenen `raflar` kaynağı için istemcide
tek satır değişmemiştir.

Tek biçim arayüzün somut karşılığı budur: **kaynaklar adreslerle adlandırılır**, **aynı
yöntemler her kaynakta aynı anlama gelir** ve **yanıtlar kendini betimler** — gövdenin nasıl
okunacağı içerik türü başlığından, kaydın sürümü varlık etiketinden anlaşılır. Var olmayan
bir kaynak da aynı biçimde, `404` ile karşılanır.

Bunun bedeli esneklik kaybıdır. Bir işlem tek bir kaynağa oturmuyorsa — "şu üç kitabı aynı
anda uzat ve gecikme cezasını yeniden hesapla" gibi — tek biçim arayüz o işlemi doğal
karşılamaz. Bu tür işlemler ya kendileri birer kaynağa dönüştürülür ya da arayüz bu noktada
uzak yordam çağrısına yaklaşır.

## Önbelleklenebilirlik: Aynı Kaydı On Kez İstemek

Üçüncü bölüm aynı kitabı on kez ister. Bu çalıştırmada koşulsuz istekler 2240 bayt taşır. İkinci turda
istemci ilk yanıttaki varlık etiketini saklar ve sonraki dokuz isteğe `If-None-Match`
başlığıyla ekler; sunucu kaydın değişmediğini görüp gövdesiz `304` döndürür. Taşınan bayt
1277'ye iner — %43 azalma.

Varlık etiketi Statik Dosya Sunumu dersinde tanıtılmıştı; buradaki fark, aynı düzeneğin
hesaplanmış bir kayda uygulanmasıdır. Kazancın kaynağı gövdenin gönderilmemesidir; başlıklar
yine taşınır, bu yüzden azalma %100 olamaz. Kayıt büyüdükçe oran yükselir: bu ölçümdeki
kayıt yalnız iki alan taşıyor.

Önbelleklenebilirliğin durumsuzlukla bağı doğrudandır. Bir yanıtın saklanabilmesi, o yanıtın
yalnız isteğin içeriğine bağlı olmasını gerektirir. Sunucuda tutulan bir imlece bağlı yanıt
saklanamaz, çünkü aynı adres iki farklı sonuç verebilir.

## Katmanlı Sistem

Üç ölçümün hepsi bir dördüncü kısıtı da mümkün kılar. Yanıt yalnız isteğe bağlıysa ve
saklanabilir olduğunu kendisi bildiriyorsa, istemci ile sunucu arasına bir katman
konulabilir: bir ters vekil, bir önbellek ya da bir yönlendirici. İstemci karşısındakinin
son sunucu mu ara katman mı olduğunu bilmez ve bilmesi gerekmez.

Önceki kursun Web Sunucusu Kavramı dersinde kurulan ters vekil tam olarak budur. O ders
vekilin bilgi kaybı da getirdiğini göstermişti; buradaki ek gözlem şudur: vekilin araya
girebilmesi, arayüzün durumsuz ve önbelleklenebilir olmasına dayanır. Sunucuda oturum
tutan bir tasarımda vekil, isteği herhangi bir kopyaya değil, oturumun bulunduğu kopyaya
göndermek zorunda kalır.

## Özet

- REST bir protokol değil, kaynak yönelimli bir arayüzün uyduğu kısıtlar kümesidir; temel
  birim kaynak, taşınan şey ise kaynağın gösterimidir.
- Durumsuzluk ölçümde doğrudan görüldü: sunucuda tutulan imleç ikinci kopyada bulunamadı ve
  bir sayfa atlandı; istek içinde taşınan sayfa bilgisi iki kopyada da aynı yanıtı verdi.
- Tek biçim arayüz, kaynak adını bilmeyen tek bir istemci kodunun dört kaynağı da okuyabilmesi
  demektir; sonradan eklenen kaynak istemcide değişiklik gerektirmedi.
- Koşullu istek aynı kaydın on kez alınmasında taşınan baytı yaklaşık %43 düşürdü; kazanç
  gövdenin gönderilmemesinden gelir, başlıklar taşınmaya devam ettiği için tam olmaz.
- Katmanlı sistem bu iki kısıta dayanır: yanıt yalnız isteğe bağlı ve saklanabilir olduğunda
  araya vekil ya da önbellek konulabilir.

## Sonraki Adım

REST'in kısıtları arayüzü ağın kurallarına yaslar: adresler, yöntemler, durum kodları,
başlıklar. Bunun karşılığında sözleşme metin biçiminde ve gevşek kalır — bir alanın tipi ya
da bir yöntemin imzası, ancak belgeye bakılarak bilinir. Bunun tersini yapan bir yaklaşım
vardır: sözleşmeyi önce ayrı bir şema dosyasında yazmak, istemci ve sunucu tarafını o
şemadan üretmek ve teli metin yerine ikili biçimde kullanmak. Sonraki ders bu yaklaşımı
kurar: bir şema dosyası yazar, ondan bir doğrulayıcı üretir, kendi ikili çerçevelemesini
yazar ve aynı kaydın ikili ile metin biçimindeki bayt sayısını karşılaştırır.
