---
title: 'REST İstemcisi'
source: 'https://academia.sh/tr/kurslar/frontend-mimarisi/rest-istemcisi'
course: 'Uygulama Mimarisi: Yönlendirme, Durum ve Veri'
language: tr
updated: '2026-08-17T18:09:21+00:00'
license: 'CC BY-SA 4.0'
---

# REST İstemcisi

Uygulamanın sunucuyla konuşan tek kapısı olarak istek katmanı; hata sözleşmesinin dört sınıfı, durum kodundan hataya eşleme, iptal ve zaman aşımı ile kaynak adreslerinin tasarımı.

Durum yönetimi konusu, uygulamanın hangi bilgiyi nerede tuttuğunu ve bu bilginin sayfa
kapandıktan sonra nasıl hayatta kaldığını kurdu. Kap hazır: yerel durum bileşenin içinde,
paylaşılan durum ortak bir depoda, adreste taşınan durum rotada, kalıcı olması gerekenler
depolamada duruyor. Kabın en büyük bölümü ise henüz boş — sunucu durumu.

Kuzey Yamaç Ölçüm İstasyonu arayüzünde istasyon listesi, her istasyonun ölçüm geçmişi ve
yeni ölçüm kaydı sunucuda tutulur. Bu ders o veriyi almanın **hangi sözleşmeyle**
yapıldığını ele alır: isteği kim kurar, yanıt nasıl çözülür, işler yolunda gitmediğinde
görünüme ne bildirilir. Sorunun cevabı bir işlev değil, bir kattır.

## İstek Katmanı Neden Ayrı Bir Kat

Veriyi çeken kod doğrudan bileşenin içine yazıldığında dört şey her çağrı noktasında
tekrarlanır: taban adresin birleştirilmesi, ortak başlıkların eklenmesi, yanıtın
çözümlenmesi ve hatanın yorumlanması. Tekrar yalnız yazım maliyeti değildir; her tekrar
ayrı bir davranış üretir. Bir yerde 404 boş liste sayılır, başka yerde hata sayılır; bir
yerde zaman aşımı vardır, başka yerde yoktur.

**İstek katmanı**, bu dört sorumluluğu tek yere toplayan ince bir aradır. Görünüme iki şey
söz verir: her çağrı ya veri döndürür ya da **hata sözleşmesine** uyan bir hata nesnesi
döndürür; üçüncü bir olasılık yoktur. Katmanın altında HTTP vardır, üstünde uygulama
kavramları. İstasyon listesini isteyen kod durum kodu bilmez, yalnız "bulunamadı" ya da
"yetki yok" görür.

Katmanın sınırı da bellidir. İstek katmanı **önbellek tutmaz**, yeniden denemez, yükleme
göstergesi yönetmez. Bunlar sırasıyla sunucu durumu yönetiminin, dayanıklılık düzeninin ve
görünüm durum makinesinin işidir; sonraki dersler bunları ayrı ayrı kurar. Katmanın tek
işi, ağın belirsizliğini kapalı ve sayılabilir bir sonuç kümesine çevirmektir.

## Hata Sözleşmesi

Bir istek dört ayrı yerde başarısız olabilir ve bu dördü aynı şey değildir.

- **Ağ hatası.** İstek karşı tarafa hiç ulaşmadı: ad çözümlenemedi, bağlantı kurulamadı,
  bağlantı koptu. Sunucu bu isteği görmemiş olabilir — ama görmüş de olabilir; yanıtın
  yolda kaybolması da aynı hataya benzer. Bu belirsizlik, yeniden deneme kararında
  belirleyici olacak.
- **HTTP hatası.** İstek ulaştı, sunucu yanıt verdi, yanıt başarısızlık bildiriyor.
  Sunucunun ne düşündüğü durum kodunda yazılıdır ve bu bilgi güvenilirdir.
- **Gövde hatası.** Yanıt geldi, durum kodu başarı diyor, ama gövde beklenen biçimde
  değil: bozuk JSON, eksik alan, farklı içerik türü. Araya giren bir vekil sunucunun HTML
  hata sayfası döndürmesi bu sınıfa girer.
- **İptal.** İstek, sonucu artık kimse beklemediği için durduruldu. Kullanıcı sayfadan
  ayrıldı, arama kutusuna yeni bir harf yazdı ya da zaman aşımı doldu. Bu bir başarısızlık
  değil, bir karardır; kullanıcıya hata gösterilmez.

Dördünün ortak bir gösterimi olmalı, yoksa her çağrı noktası kendi ayrımını yeniden
uydurur. Sözleşme şu alanlardan oluşur: `tur` (dört sınıftan biri), `kod` (uygulamanın
tanıdığı sabit ad), `durum` (varsa HTTP durum kodu), `yenidenDenenebilir` (mantıksal) ve
`ayrinti` (sunucunun gönderdiği yapılandırılmış açıklama). İleti metni sözleşmede tutulmaz
— hangi dilde ve hangi tonda yazılacağı görünümün kararıdır; bu ayrımı Uluslararasılaştırma
dersi kapatır.

## Durum Kodundan Hataya Eşleme

Eşleme bir tablo işidir ve tablo tek yerde durur. İnternet Nasıl Çalışır kursundaki HTTP
İstek ve Yanıtı dersinde tanıtılan durum kodu sınıfları burada uygulama kararına çevrilir:
`4xx` isteğin kendisinde bir sorun olduğunu söyler, aynı isteği tekrarlamak sonucu
değiştirmez; `5xx` sunucunun o an tamamlayamadığını söyler, tekrarlamak işe yarayabilir.

```js
// durum-eslemesi.mjs — HTTP durum kodundan hata sozlesmesine gecis
const ESLEME = [
  [400, "istek_bicimi",   false, "İstek gövdesi okunamadı"],
  [401, "kimlik_yok",     false, "Oturum gerekiyor"],
  [403, "yetki_yok",      false, "Bu kayda erişim yok"],
  [404, "bulunamadi",     false, "Kayıt yok"],
  [409, "cakisma",        false, "Kayıt bu arada değişti"],
  [422, "dogrulama",      false, "Alan değerleri kabul edilmedi"],
  [429, "hiz_siniri",     true,  "İstek hızı aşıldı"],
  [500, "sunucu",         true,  "Sunucu isteği tamamlayamadı"],
  [503, "erisilemez",     true,  "Hizmet geçici olarak kapalı"],
];

function httpHatasi(durum, ayrinti = null) {
  const satir = ESLEME.find(([d]) => d === durum);
  if (satir) {
    const [, kod, yenidenDenenebilir, ileti] = satir;
    return { tur: "http", durum, kod, ileti, yenidenDenenebilir, ayrinti };
  }
  // Bilinmeyen kod: sinifina gore karar verilir, uydurma kod uretilmez.
  const sunucuSinifi = durum >= 500;
  return {
    tur: "http",
    durum,
    kod: sunucuSinifi ? "sunucu" : "istek",
    ileti: sunucuSinifi ? "Sunucu isteği tamamlayamadı" : "İstek kabul edilmedi",
    yenidenDenenebilir: sunucuSinifi,
    ayrinti,
  };
}

for (const durum of [401, 404, 422, 429, 500, 418, 504]) {
  const h = httpHatasi(durum);
  console.log(
    String(h.durum).padEnd(4),
    h.kod.padEnd(14),
    "yeniden:", String(h.yenidenDenenebilir).padEnd(5),
    h.ileti,
  );
}
```

```
401  kimlik_yok     yeniden: false Oturum gerekiyor
404  bulunamadi     yeniden: false Kayıt yok
422  dogrulama      yeniden: false Alan değerleri kabul edilmedi
429  hiz_siniri     yeniden: true  İstek hızı aşıldı
500  sunucu         yeniden: true  Sunucu isteği tamamlayamadı
418  istek          yeniden: false İstek kabul edilmedi
504  sunucu         yeniden: true  Sunucu isteği tamamlayamadı
```

Son iki satır tablonun asıl değerini gösterir. Tabloda olmayan kodlar da bir karara
bağlanır ve karar sınıfın anlamından türer; katman "bilinmeyen kod" diye çöküp görünümü
tanımsız bir duruma bırakmaz. 429 ilginç bir istisnadır: 4xx sınıfındadır ama yeniden
denenebilir, çünkü sorun isteğin içeriğinde değil zamanlamasındadır.

## Katmanın Uçtan Uca Çalışması

Aşağıdaki dosya hem yerel bir sunucu kurar hem de istek katmanını o sunucuya karşı
çalıştırır; kendi başına çalışır ve sonunda sunucuyu kapatır. Bağlantı noktası sıfır
verildiği için işletim sistemi boş bir kapı seçer — o sayı çalıştıran makineye göre değişir
ve çıktıda görünmez.

```js
// istek-katmani.mjs — yerel sunucu + istek katmani, tek dosyada calisir
import http from "node:http";

// --- Sunucu: istasyon listesi, olcum gecmisi ve hata yollari ----------------
const ISTASYONLAR = [
  { kod: "KY-01", ad: "Kuzey Yamaç", yukseklik: 1840 },
  { kod: "KY-02", ad: "Kuzey Yamaç Sırt", yukseklik: 2110 },
];

const sunucu = http.createServer((istek, yanit) => {
  const adres = new URL(istek.url, "http://127.0.0.1");
  const gonder = (durum, govde, tur = "application/json") => {
    yanit.writeHead(durum, { "content-type": tur });
    yanit.end(typeof govde === "string" ? govde : JSON.stringify(govde));
  };
  if (adres.pathname === "/istasyonlar") return gonder(200, ISTASYONLAR);
  if (adres.pathname === "/istasyonlar/KY-99")
    return gonder(404, { kod: "istasyon_bulunamadi", ileti: "KY-99 kayıtlı değil" });
  if (adres.pathname === "/olcumler" && istek.method === "POST")
    return gonder(422, { kod: "dogrulama", alanlar: { deger: "aralik_disi" } });
  if (adres.pathname === "/rapor")
    return gonder(500, "<html>sunucu hatası</html>", "text/html");
  if (adres.pathname === "/yavas")
    return setTimeout(() => gonder(200, { hazir: true }), 300);
  return gonder(404, { kod: "yol_yok", ileti: "Bilinmeyen yol" });
});
await new Promise((coz) => sunucu.listen(0, "127.0.0.1", coz));
const TABAN = `http://127.0.0.1:${sunucu.address().port}`;

// --- Hata sozlesmesi --------------------------------------------------------
const ESLEME = new Map([
  [401, ["kimlik_yok", false]], [403, ["yetki_yok", false]],
  [404, ["bulunamadi", false]], [422, ["dogrulama", false]],
  [429, ["hiz_siniri", true]],  [500, ["sunucu", true]],
  [503, ["erisilemez", true]],
]);

const hata = (tur, kod, yenidenDenenebilir, durum = null, ayrinti = null) =>
  ({ tur, kod, yenidenDenenebilir, durum, ayrinti });

// --- Istek katmani ----------------------------------------------------------
async function istek(yol, secenekler = {}) {
  const { yontem = "GET", govde = null, zamanAsimi = 1000, taban = TABAN } = secenekler;
  const denetleyici = new AbortController();
  const sayac = setTimeout(() => denetleyici.abort("zaman_asimi"), zamanAsimi);

  let yanit;
  try {
    yanit = await fetch(taban + yol, {
      method: yontem,
      headers: govde ? { "content-type": "application/json" } : {},
      body: govde ? JSON.stringify(govde) : undefined,
      signal: denetleyici.signal,
    });
  } catch {
    // Alt katmanin iletisi ortama gore degisir; disariya kendi kodumuz cikar.
    return { ok: false, hata: denetleyici.signal.aborted
      ? hata("iptal", String(denetleyici.signal.reason), false)
      : hata("ag", "baglanti", true) };
  } finally {
    clearTimeout(sayac);
  }

  const tur = yanit.headers.get("content-type") ?? "";
  let cozulen = null;
  if (tur.startsWith("application/json")) {
    try { cozulen = await yanit.json(); }
    catch { return { ok: false, hata: hata("govde", "cozumlenemedi", false, yanit.status) }; }
  } else {
    await yanit.text();
  }

  if (!yanit.ok) {
    const [kod, yeniden] = ESLEME.get(yanit.status) ?? [
      yanit.status >= 500 ? "sunucu" : "istek", yanit.status >= 500];
    return { ok: false, hata: hata("http", kod, yeniden, yanit.status, cozulen) };
  }
  return { ok: true, veri: cozulen };
}

// --- Cagri noktalari --------------------------------------------------------
const yaz = (etiket, s) => console.log(
  etiket.padEnd(22),
  s.ok ? `ok    ${JSON.stringify(s.veri)}`
       : `hata  tur=${s.hata.tur} kod=${s.hata.kod} durum=${s.hata.durum} ` +
         `yeniden=${s.hata.yenidenDenenebilir}`);

yaz("istasyon listesi", await istek("/istasyonlar"));
yaz("bilinmeyen istasyon", await istek("/istasyonlar/KY-99"));
yaz("ölçüm gönderimi", await istek("/olcumler", { yontem: "POST", govde: { deger: 900 } }));
yaz("rapor (JSON değil)", await istek("/rapor"));
yaz("zaman aşımı", await istek("/yavas", { zamanAsimi: 50 }));
yaz("aynı yol, bol süre", await istek("/yavas", { zamanAsimi: 1000 }));

// Kapali bir baglanti noktasi: ag hatasi yolu.
const bos = http.createServer();
await new Promise((coz) => bos.listen(0, "127.0.0.1", coz));
const kapaliTaban = `http://127.0.0.1:${bos.address().port}`;
await new Promise((coz) => bos.close(coz));
yaz("sunucu yanıt vermiyor", await istek("/istasyonlar", { taban: kapaliTaban }));

sunucu.close();
```

```
istasyon listesi       ok    [{"kod":"KY-01","ad":"Kuzey Yamaç","yukseklik":1840},{"kod":"KY-02","ad":"Kuzey Yamaç Sırt","yukseklik":2110}]
bilinmeyen istasyon    hata  tur=http kod=bulunamadi durum=404 yeniden=false
ölçüm gönderimi        hata  tur=http kod=dogrulama durum=422 yeniden=false
rapor (JSON değil)     hata  tur=http kod=sunucu durum=500 yeniden=true
zaman aşımı            hata  tur=iptal kod=zaman_asimi durum=null yeniden=false
aynı yol, bol süre     ok    {"hazir":true}
sunucu yanıt vermiyor  hata  tur=ag kod=baglanti durum=null yeniden=true
```

Yedi satır, dört hata sınıfının hepsini gösteriyor. Dikkat edilecek üç nokta var.

Birincisi, **yanıtın gövdesi her durumda tüketilir**. Başarısız yanıtın gövdesi
okunmadan bırakılırsa bağlantı serbest kalmaz; içerik türü JSON değilse bile gövde metin
olarak okunup atılır. Beşinci satırdaki 500 yanıtı HTML döndürüyor — katman bunu çözmeye
çalışmıyor, durum kodundan karar veriyor.

İkincisi, 422 yanıtının gövdesi `ayrinti` alanına taşınır. Alan bazlı doğrulama hatalarını
forma geri bağlayacak olan Doğrulama Şemaları dersi bu alanı kullanacak; istek katmanı
içeriği yorumlamaz, yalnızca taşır.

Üçüncüsü, iptal ile ağ hatası aynı `catch` bloğuna düşer ama ayrı sınıflandırılır. Ayrımı
yapan şey yakalanan değer değil, **iptal işaretinin durumu**dur. İptal nedeni işaretin
üzerinde taşındığı için "kim iptal etti" sorusu da yanıtlanabilir; burada neden
`zaman_asimi` dizgisidir.

## İptal, Zaman Aşımı ve Yarış

Eşzamansız JavaScript ve Çalışma Zamanı kursunda tanıtılan **iptal işareti (abort
signal)** bu katmanın belkemiğidir. İki ayrı ihtiyaca aynı düzenek hizmet eder: bir süre
sonra vazgeçmek ve sonucu artık istemediğimizde vazgeçmek.

Zaman aşımı, sunucunun yanıt vermemesi durumunda isteği sonsuza kadar açık tutmamayı
sağlar. Süresiz bir istek, kullanıcının bakış açısından hiç bitmeyen bir yükleme
göstergesidir. Sürenin ne olacağı işin niteliğine bağlıdır; istasyon listesi için kısa,
büyük bir rapor için uzun olur, bu yüzden katman süreyi çağrı başına alır.

İkinci ihtiyaç daha incedir. İstasyon listesinde bir süzgeç alanı varsa kullanıcı yazdıkça
istek üretilir. Üçüncü isteğin yanıtı ikincisinden önce gelirse ekranda eski sonuç kalır.
Çözümün bir yarısı, yeni istek başlarken öncekini iptal etmektir; katmanın dışarıdan bir
iptal işareti kabul etmesi bunu mümkün kılar. Diğer yarısı — geç gelen yanıtın yok
sayılması — görünüm tarafındadır ve Yükleme ve Hata Durumları dersinde ele alınır.

İptal edilen istek görünüme hata olarak yansımaz. Katmanın `iptal` sınıfını ayrı tutmasının
tek sebebi budur: çağrı noktası bu sonucu görünce hiçbir şey yapmaz, çünkü ekranı zaten
yeni bir istek dolduracaktır.

## Kaynak Adresleri ve Sözleşmenin Sınırı

REST'in katman açısından anlamı sadedir: her kaynağın bir adresi vardır ve HTTP yöntemi ne
yapılacağını söyler. Koleksiyon `/istasyonlar`, tekil kayıt `/istasyonlar/KY-01`, bağlı
koleksiyon `/istasyonlar/KY-01/olcumler` olur. Süzme, sıralama ve sayfalama ölçütleri
adresin sorgu bölümünde taşınır; bunlar kaynağın kimliğini değil görünümünü belirler.

Bu düzenin istemci tarafında iki doğrudan sonucu vardır. Birincisi, **adres bir önbellek
anahtarıdır**: aynı adrese yapılan iki istek aynı kaydı ister ve sonucu paylaşabilir.
İkincisi, **yöntem yeniden denenebilirliği belirler**. İnternet Nasıl Çalışır kursunda
tanımlanan güvenli ve değişmez yöntem ayrımı burada iş görür: bir okuma isteğini
tekrarlamak zararsızdır, bir ölçüm kaydını iki kez göndermek iki kayıt üretebilir.

Sözleşmenin sınırı da bu noktada belirir. İstemci, sunucunun kaynakları hangi
ayrıntı düzeyinde döndürdüğüne uymak zorundadır. Ölçüm geçmişi ekranı istasyon adını,
son ölçümü ve ölçüm listesini birlikte gösteriyorsa üç ayrı istek gerekebilir; tek bir
ekran için üç gidiş dönüş, gecikmenin doğrudan çarpanıdır. Sunucu bunu önlemek için
ekrana göre birleşik yanıtlar tasarlarsa bu kez kaynak modeli ekrana bağlanır ve her yeni
ekran yeni bir uç nokta ister.

## Özet

- İstek katmanı taban adres, ortak başlıklar, gövde çözümleme ve hata yorumlama
  sorumluluklarını görünümden alıp tek yere toplar; önbellek, yeniden deneme ve yükleme
  göstergesi onun işi değildir.
- Hata sözleşmesi dört sınıf tanır: ağ, HTTP, gövde ve iptal. Ağ hatasında isteğin sunucuya
  ulaşıp ulaşmadığı belirsizdir; HTTP hatasında sunucunun kararı bilinir.
- Durum kodu eşlemesi tek bir tabloda tutulur ve tabloda olmayan kodlar sınıflarının
  anlamına göre karara bağlanır; 429, 4xx sınıfında olmasına rağmen yeniden denenebilirdir.
- Başarısız yanıtın gövdesi de tüketilir; sunucunun gönderdiği yapılandırılmış açıklama
  yorumlanmadan `ayrinti` alanında taşınır.
- İptal işareti hem zaman aşımını hem de vazgeçmeyi karşılar; iptal edilen istek görünüme
  hata olarak yansımaz.
- Kaynak adresi aynı zamanda önbellek anahtarıdır; HTTP yönteminin güvenli ve değişmez
  olması yeniden denenebilirliği belirler.

## Sonraki Adım

Bu dersin son bölümü bir gerilim bıraktı: ekranın istediği veri ile kaynağın döndürdüğü
veri aynı biçimde değil. Ölçüm geçmişi ekranı için üç istek atmak da, ekrana özel bir uç
nokta açmak da bedelli. Sorunun kökü, **ne isteneceğine sunucunun karar vermesi**. Sonraki
ders bu kararı istemciye veren bir sorgu diline bakar: istemci hangi alanları istediğini
yazar, tek istekte alır. Bunun karşılığında iki yeni sorun doğar — gelen iç içe yanıtın
önbellekte nasıl saklanacağı ve aynı alan kümesinin ekranlar arasında nasıl paylaşılacağı.
