---
title: 'API Biçemlerinin Karşılaştırması'
source: 'https://academia.sh/tr/kurslar/api-tasarimi/api-bicemlerinin-karsilastirmasi'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:44+00:00'
license: 'CC BY-SA 4.0'
---

# API Biçemlerinin Karşılaştırması

Aynı işi karşılayan üç biçem: kaynak temelli, uzak yordam çağrısı biçimli ve sorgu temelli yaklaşımlar aynı senaryoda yazılıp istek sayısı, tel üzerindeki bayt ve taşınan alanın kullanılan oranı ölçülerek karşılaştırılır; gereksinim değiştiğinde hangi tarafın değişmek zorunda kaldığı gösterilir.

Önceki ders bir arayüzü sözleşme yapan şeyi kurdu ama sözleşmenin biçimini açık bıraktı.
Kütüphane servisinden istenen şu iş üç ayrı yaklaşımla karşılanabilir: **bir üyenin
ödünçteki kitaplarını, kitap adları ve son teslim tarihleriyle birlikte listele.**

Üç yaklaşım şudur. **Kaynak temelli** biçemde her kavramın kendi adresi vardır ve istemci
gerekli parçaları tek tek toplar. **Uzak yordam çağrısı** biçeminde tek bir adres vardır,
istemci gövdede bir yordam adı gönderir ve sunucu o iş için hazırlanmış yanıtı döndürür.
**Sorgu temelli** biçemde yine tek adres vardır, ama istemci gövdede hangi alanları istediğini
yazar. Aradaki fark bir üslup tercihi değildir: yanıtın biçimini **kimin belirlediği**
değişir ve bunun ölçülebilir sonuçları vardır.

## Ortak Veri ve Üçüncü Biçemin Çözümleyicisi

Üç biçem de aynı veri üzerinde çalışır.

```js
// veri.mjs — uc bicemin de uzerinde calistigi ortak kutuphane verisi
export const UYELER = [{ id: 1, ad: "Ayse Kaya", eposta: "ayse@ornek.test", telefon: "0000000000",
  adres: "Merkez Sube, Kat 2", kayit: "2024-01-05", ceza: 0, uyelikTuru: "tam" }];
export const KITAPLAR = {
  "978-0262033848": { isbn: "978-0262033848", ad: "Algoritmalara Giris", yazar: "Cormen",
    sayfa: 1312, yayinevi: "MIT", basim: 2009, raf: "R-12", ozet: "Algoritma tasarimi ve cozumlemesi." },
  "978-0201896831": { isbn: "978-0201896831", ad: "Bilgisayar Programlama Sanati", yazar: "Knuth",
    sayfa: 650, yayinevi: "Addison", basim: 1997, raf: "R-03", ozet: "Temel algoritmalar cildi." },
  "978-0131103627": { isbn: "978-0131103627", ad: "C Programlama Dili", yazar: "Kernighan",
    sayfa: 272, yayinevi: "Prentice", basim: 1988, raf: "R-07", ozet: "Dilin tanimi ve ornekleri." },
};
export const ODUNCLER = [
  { id: 11, uyeId: 1, isbn: "978-0262033848", alinma: "2024-05-01", sonTarih: "2024-05-15" },
  { id: 12, uyeId: 1, isbn: "978-0201896831", alinma: "2024-05-03", sonTarih: "2024-05-17" },
  { id: 13, uyeId: 1, isbn: "978-0131103627", alinma: "2024-05-06", sonTarih: "2024-05-20" },
];
```

Sorgu temelli biçem bir çözümleyici gerektirir: istemcinin yazdığı seçim metni bir ağaca
dönüştürülür, sonra bu ağaç veri üzerinde yürütülür. Aşağıdaki otuz satır bu işi yapar.

```js
// sorgu.mjs — kucuk secim cozumleyicisi. Girdi: "uye(1) { ad oduncler { sonTarih kitap { ad } } }"
export const cozumle = (metin) => {
  const simgeler = metin.match(/[A-Za-z_][A-Za-z0-9_]*|\d+|[{}()]/g) ?? [];
  let i = 0;
  const secimKumesi = () => {                       // { alan alan(arg) { ... } ... }
    const alanlar = [];
    i++;                                            // acilis suslu ayraci
    while (simgeler[i] !== "}") {
      const ad = simgeler[i++];
      let arg = null;
      if (simgeler[i] === "(") { arg = simgeler[i + 1]; i += 3; }
      const alt = simgeler[i] === "{" ? secimKumesi() : null;
      alanlar.push({ ad, arg, alt });
    }
    i++;                                            // kapanis suslu ayraci
    return alanlar;
  };
  simgeler.unshift("{"); simgeler.push("}");
  return secimKumesi();
};

// Secim agacini veri uzerinde yurutur: istemci hangi alanlari istediyse yalniz onlar doner.
export const yurut = (alanlar, kok, cozucu) => {
  const cikti = {};
  for (const alan of alanlar) {
    const deger = cozucu(kok, alan.ad, alan.arg);
    if (!alan.alt) { cikti[alan.ad] = deger; continue; }
    cikti[alan.ad] = Array.isArray(deger)
      ? deger.map((d) => yurut(alan.alt, d, cozucu))
      : yurut(alan.alt, deger, cozucu);
  }
  return cikti;
};
```

Üç biçem tek bir sunucuda yan yana durur; böylece karşılaştırma aynı veri, aynı süreç ve
aynı bağlantı üzerinde yapılır.

```js
// sunucu.mjs — ayni isi uc bicemle karsilayan sunucu: kaynak temelli, uzak yordam, sorgu temelli
import { createServer } from "node:http";
import { UYELER, KITAPLAR, ODUNCLER } from "./veri.mjs";
import { cozumle, yurut } from "./sorgu.mjs";

const govdeOku = (istek) => new Promise((coz) => {
  let m = ""; istek.on("data", (p) => (m += p)); istek.on("end", () => coz(m));
});
const json = (yanit, kod, govde) => {
  const metin = JSON.stringify(govde);
  yanit.setHeader("Content-Type", "application/json; charset=utf-8");
  yanit.setHeader("Content-Length", Buffer.byteLength(metin));
  yanit.writeHead(kod).end(metin);
};

// Sorgu temelli bicem icin cozucu: her alan adi bir veri erisimine karsilik gelir.
const cozucu = (kok, ad, arg) => {
  if (ad === "uye") return UYELER.find((u) => u.id === Number(arg));
  if (ad === "oduncler") return ODUNCLER.filter((o) => o.uyeId === kok.id);
  if (ad === "kitap") return KITAPLAR[kok.isbn];
  return kok[ad];
};

createServer(async (istek, yanit) => {
  yanit.sendDate = false;
  const yol = new URL(istek.url, "http://yerel").pathname;

  // 1) Kaynak temelli: her kavram ayri bir adres, her adres kaydin tamamini dondurur.
  let e;
  if ((e = /^\/uyeler\/(\d+)$/.exec(yol))) return json(yanit, 200, UYELER.find((u) => u.id === +e[1]));
  if ((e = /^\/uyeler\/(\d+)\/oduncler$/.exec(yol)))
    return json(yanit, 200, ODUNCLER.filter((o) => o.uyeId === +e[1]));
  if ((e = /^\/kitaplar\/(.+)$/.exec(yol))) return json(yanit, 200, KITAPLAR[e[1]]);

  // 2) Uzak yordam cagrisi: tek adres, govdede yordam adi; yanit bicimini sunucu belirler.
  if (yol === "/rpc") {
    const { yordam, parametre } = JSON.parse(await govdeOku(istek));
    if (yordam !== "uyeOduncOzeti") return json(yanit, 200, { hata: "bilinmeyen yordam" });
    const uye = UYELER.find((u) => u.id === parametre.uyeId);
    return json(yanit, 200, { sonuc: { ad: uye.ad, oduncler: ODUNCLER
      .filter((o) => o.uyeId === uye.id)
      .map((o) => ({ sonTarih: o.sonTarih, kitapAdi: KITAPLAR[o.isbn].ad })) } });
  }

  // 3) Sorgu temelli: tek adres, govdede secim kumesi; yanit bicimini istemci belirler.
  if (yol === "/sorgu") {
    const { sorgu } = JSON.parse(await govdeOku(istek));
    return json(yanit, 200, { veri: yurut(cozumle(sorgu), null, cozucu) });
  }
  return json(yanit, 404, { hata: "bulunamadi" });
}).listen(8451, "127.0.0.1", () => console.log("uc bicem 127.0.0.1:8451"));
```

## Üç Ölçü

Karşılaştırma üç sayıya dayanır. **İstek sayısı** ağ turlarını verir. **Tel üzerindeki bayt**
başlıklar dahil gerçekte taşınan veriyi verir; bunu ölçmek için istemci, bağlantıyı saran
bir katman kullanır. **Taşınan alan sayısı** ise yanıtlardaki skaler değerlerin toplamıdır;
bunun kaçının kullanıldığı **aşırı veri çekmeyi** (over-fetching) sayısallaştırır.

```js
// olc.mjs — uc bicemi ayni senaryoda olcer: istek sayisi, tel uzerindeki bayt, kullanilan alan orani
import { Agent, request } from "node:http";
import { connect } from "node:net";

let gonderilen = 0, alinan = 0, istekSayisi = 0;
const ajan = new Agent({ keepAlive: true, maxSockets: 1 });
ajan.createConnection = (secenek) => {                 // tel uzerindeki baytlari sayan baglanti
  const s = connect(secenek);
  s.on("data", (p) => { alinan += p.length; });
  const yaz = s.write.bind(s);
  s.write = (parca, ...k) => { gonderilen += Buffer.byteLength(parca); return yaz(parca, ...k); };
  return s;
};
const iste = (yol, govde) => new Promise((coz, hata) => {
  istekSayisi++;
  const r = request({ agent: ajan, host: "127.0.0.1", port: 8451, path: yol,
    method: govde ? "POST" : "GET", headers: govde ? { "Content-Type": "application/json" } : {} },
    (y) => { let m = ""; y.on("data", (p) => (m += p)); y.on("end", () => coz(JSON.parse(m))); });
  r.on("error", hata);
  r.end(govde ? JSON.stringify(govde) : undefined);
});

// Yanitlarda tasinan skaler alanlarin toplami
const yaprakSay = (d) => (typeof d !== "object" || d === null
  ? 1 : Object.values(d).reduce((t, v) => t + yaprakSay(v), 0));
const sifirla = () => { gonderilen = alinan = istekSayisi = 0; };
const rapor = (ad, tasinan, kullanilan, not) => console.log(
  `${ad.padEnd(15)} istek=${String(istekSayisi).padStart(2)}  ` +
  `gonderilen=${String(gonderilen).padStart(4)} B  alinan=${String(alinan).padStart(4)} B  ` +
  `tasinan alan=${String(tasinan).padStart(2)}  kullanim=` +
  (tasinan ? `%${Math.round((100 * kullanilan) / tasinan)}`.padStart(5) : "    -") +
  (not ? `\n${" ".repeat(16)}${not}` : ""));

const kaynakTemelli = async () => {
  sifirla();
  let tasinan = 0;
  const uye = await iste("/uyeler/1");               tasinan += yaprakSay(uye);
  const oduncler = await iste("/uyeler/1/oduncler"); tasinan += yaprakSay(oduncler);
  for (const o of oduncler) tasinan += yaprakSay(await iste(`/kitaplar/${o.isbn}`));
  return tasinan;
};

// --- Senaryo 1: uye adi + her odunc icin kitap adi ve son tarih (7 alan) ---
console.log("-- senaryo 1: uye adi, kitap adi, son tarih --");
rapor("kaynak temelli", await kaynakTemelli(), 7);
sifirla();
const rpc = await iste("/rpc", { yordam: "uyeOduncOzeti", parametre: { uyeId: 1 } });
rapor("uzak yordam", yaprakSay(rpc), 7, JSON.stringify(rpc.sonuc).slice(0, 78) + " ...");
sifirla();
const sonuc = await iste("/sorgu", { sorgu: "uye(1) { ad oduncler { sonTarih kitap { ad } } }" });
rapor("sorgu temelli", yaprakSay(sonuc), 7, JSON.stringify(sonuc.veri).slice(0, 78) + " ...");

// --- Senaryo 2: istemcinin gereksinimi degisti, yazar adi da isteniyor (10 alan) ---
console.log("\n-- senaryo 2: yanita yazar adi eklendi --");
rapor("kaynak temelli", await kaynakTemelli(), 10, "sunucu degismedi: yazar zaten tasiniyordu");
sifirla();
const rpc2 = await iste("/rpc", { yordam: "uyeOduncOzetiYazarli", parametre: { uyeId: 1 } });
rapor("uzak yordam", 0, 10, `sunucu yaniti: ${JSON.stringify(rpc2)} -> yeni yordam gerekiyor`);
sifirla();
const sonuc2 = await iste("/sorgu",
  { sorgu: "uye(1) { ad oduncler { sonTarih kitap { ad yazar } } }" });
rapor("sorgu temelli", yaprakSay(sonuc2), 10, "sunucu degismedi: sorguya yazar alani eklendi");
ajan.destroy();
```

```bash
node sunucu.mjs & p=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null http://127.0.0.1:8451/uyeler/1
node olc.mjs
kill $p
```

```
uc bicem 127.0.0.1:8451
-- senaryo 1: uye adi, kitap adi, son tarih --
kaynak temelli  istek= 5  gonderilen= 414 B  alinan=1602 B  tasinan alan=47  kullanim=  %15
uzak yordam     istek= 1  gonderilen= 170 B  alinan= 361 B  tasinan alan= 7  kullanim= %100
                {"ad":"Ayse Kaya","oduncler":[{"sonTarih":"2024-05-15","kitapAdi":"Algoritmala ...
sorgu temelli   istek= 1  gonderilen= 182 B  alinan= 380 B  tasinan alan= 7  kullanim= %100
                {"uye":{"ad":"Ayse Kaya","oduncler":[{"sonTarih":"2024-05-15","kitap":{"ad":"A ...

-- senaryo 2: yanita yazar adi eklendi --
kaynak temelli  istek= 5  gonderilen= 414 B  alinan=1602 B  tasinan alan=47  kullanim=  %21
                sunucu degismedi: yazar zaten tasiniyordu
uzak yordam     istek= 1  gonderilen= 177 B  alinan= 161 B  tasinan alan= 0  kullanim=    -
                sunucu yaniti: {"hata":"bilinmeyen yordam"} -> yeni yordam gerekiyor
sorgu temelli   istek= 1  gonderilen= 188 B  alinan= 433 B  tasinan alan=10  kullanim= %100
                sunucu degismedi: sorguya yazar alani eklendi
```

Bayt sayıları başlıkları da içerdiği için çalışma zamanının varsayılan başlık kümesine
bağlıdır; alan sayıları ve istek sayıları ise veriye bağlıdır, makineden bağımsızdır.

## Ölçümün Söyledikleri

**Eksik veri çekme** (under-fetching) kaynak temelli biçemde istek sayısı olarak görünür.
Üye kaydı bir istek, ödünç listesi bir istek, sonra her ödünç için bir kitap isteği: toplam
beş. Bu sayı sabit değildir, ödünç sayısına bağlıdır. Bir kayıt listesi getirip listedeki
her satır için ayrı istek yapma örüntüsü **N+1 sorunu** (N+1 problem) olarak anılır ve veri
büyüdükçe ağ turu sayısı da büyür.

**Fazla veri çekme** aynı biçemde alan sayısı olarak görünür. Kırk yedi alan taşınmış, yedisi
kullanılmıştır. Üyenin adresi, telefonu, ceza tutarı; kitabın özeti, yayınevi, raf numarası —
hiçbiri istenmemişti. Kaynak temelli biçemde kaydın tamamının dönmesi bir kusur değil, o
biçemin tanımıdır: adres bir **kayda** karşılık gelir, bir **görünüme** değil.

Diğer iki biçem her iki sorunu da tek istekte çözer: yedi alan taşınır, yedisi kullanılır.
Aradaki fark ikinci senaryoda ortaya çıkar.

## Yanıtın Biçimini Kim Belirler

İkinci senaryoda gereksinim değişti: yazar adı da isteniyor. Üç biçem üç ayrı yerden
değişmek zorunda kaldı.

**Kaynak temelli biçemde hiçbir şey değişmedi.** Yazar adı zaten taşınıyordu; kullanım oranı
%15'ten %21'e çıktı, o kadar. Fazla veri çekmenin beklenmedik bir yararı budur: istemcinin
sonradan gereksinim duyduğu alan çoğu zaman elinde zaten vardır.

**Uzak yordam biçeminde sunucunun değişmesi gerekti.** İstemcinin çağırdığı yordam yoktur ve
sunucu bunu bildirir. Yeni alanı almak için ya var olan yordamın yanıtı genişletilir ya da
yeni bir yordam eklenir; iki durumda da sunucu yeniden yayımlanmadan istemci ilerleyemez.
Bu biçemde yanıtın biçimi sunucunun kararıdır, dolayısıyla biçim değişikliği de sunucunun
işidir.

**Sorgu temelli biçemde yalnız istemci değişti.** Seçim kümesine `yazar` eklendi, sunucu
aynı kaldı, taşınan alan yediden ona çıktı ve kullanım oranı %100'de kaldı. Yanıtın biçimini
istemci belirlediği için biçim değişikliği de istemcinin işidir.

Bu, biçemler arasındaki asıl ödünleşimdir ve maliyeti sunucu tarafına yansır. Sorgu temelli
bir arayüzde sunucu, istemcinin isteyebileceği her alan bileşimini karşılayabilmek zorundadır;
gelen sorgunun ne kadar veri erişimi doğuracağı önceden bilinmez. Uzak yordam biçeminde ise
sunucu her yanıtın ne getireceğini kesin bilir, çünkü biçimi kendisi sabitlemiştir.

## Biçem Seçimi Neye Bağlıdır

Ölçüm bir biçemin diğerinden iyi olduğunu göstermez; her ölçünün bir sahibi olduğunu
gösterir.

Tüketici sayısı azsa ve gereksinimleri birbirine benziyorsa, sunucunun biçimi sabitlemesi
en az sürprizi üretir. Tüketiciler çoksa ve her biri farklı alan bileşimi istiyorsa,
biçimi istemciye bırakmak sunucuyu her yeni ekran için değişmekten kurtarır. Tüketici
tarafı önbelleklemeye dayanıyorsa, her kaydın kendi adresinin olması bir avantaja dönüşür:
aynı kitap ikinci kez istendiğinde ağa hiç çıkılmaz. Bu son nokta bir sonraki dersin
konusudur.

Bir de ölçüme girmeyen bir kalem vardır: **anlaşılabilirlik**. Kaynak temelli bir arayüzde
adresler kavram adlarıdır; sistemi tanımayan biri adres listesine bakarak alanı öğrenebilir.
Tek adresli biçemlerde bu bilgi gövdenin içindedir ve ancak sözleşme okunarak görülür.

## Özet

- Üç biçem aynı işi karşılar ama yanıtın biçimini farklı taraf belirler: kaynak temelli
  biçemde kaydın kendisi, uzak yordam biçeminde sunucu, sorgu temelli biçemde istemci.
- Kaynak temelli biçemde ölçüm beş istek ve kırk yedi taşınan alan verdi; yedi alan
  kullanıldı. Eksik veri çekme istek sayısında, aşırı veri çekme kullanım oranında görünür.
- Ödünç sayısına bağlı olarak artan istek sayısı N+1 örüntüsüdür; liste getirip her satır
  için ayrı istek yapan her tasarımda ortaya çıkar.
- Gereksinim değiştiğinde uzak yordam biçeminde sunucu, sorgu temelli biçemde istemci
  değişmek zorunda kaldı; kaynak temelli biçemde ise fazla taşınan veri değişikliği gereksiz
  kıldı.
- Sorgu temelli biçemin bedeli sunucu tarafındadır: gelen sorgunun doğuracağı veri erişimi
  önceden bilinmez, oysa sabit biçimli yanıtın maliyeti bellidir.

## Sonraki Adım

Kaynak temelli biçem ölçümde en çok bayt taşıyan ve en çok istek yapan seçenek oldu, ama en
yaygın kullanılan biçem odur. Bu çelişki, ölçüme girmeyen özelliklerden gelir: her kaydın
kendi adresinin olması aynı kaydın ikinci kez istendiğinde ağa hiç çıkılmamasını mümkün
kılar; her isteğin kendi kendine yeter olması isteğin hangi sunucu kopyasına düştüğünü
önemsiz kılar. Sonraki ders bu özellikleri bir ilkeler kümesi olarak adlandırır ve üçünü de
çalıştırarak sınar: aynı isteğin iki ayrı süreçte aynı yanıtı vermesi, tek biçim arayüzün
somut karşılığı ve önbelleklenebilirliğin taşınan bayt üzerindeki etkisi.
