Fetch API üç temel nesne üzerine kuruludur: Request bir isteği, Response bir yanıtı, Headers ise ikisinin de başlık listesini temsil eder. fetch() çağrısına verdiğiniz URL ve seçenekler içeride bir Request nesnesine dönüşür, sonuç olarak da size bir Response gelir. Bu nesneleri doğrudan tanımak; başlıkları güvenle düzenlemenizi, aynı isteği farklı seçeneklerle yeniden kullanmanızı ve gövdenin neden yalnızca bir kez okunabildiğini anlamanızı sağlar.
Bu yazı fetch()'in temel kullanımını bildiğinizi varsayar; GET ve POST örnekleri, response.ok kontrolü ve zaman aşımı için Fetch API kullanımı rehberine bakabilirsiniz. Burada Headers yöntemlerini, Request kurucusunun davranışını, mode, credentials, cache ve redirect seçeneklerini, Response türlerini ve clone() kurallarını inceleyeceğiz. Örneklerin büyük kısmı ağ bağlantısı gerektirmediği için Node.js 24 ile çalıştırılarak denendi; bu nesneler tarayıcıda da aynı standarda göre çalışır.
Headers nesnesi

Headers, ad ve değer çiftlerinden oluşan bir başlık listesi tutar. Üç şekilde oluşturulabilir: boş olarak, düz bir nesneyle veya iki elemanlı dizilerden oluşan bir diziyle. Başlık adları büyük küçük harf duyarsız eşleştirilir ve içeride küçük harfe çevrilir; değerlerin başındaki ve sonundaki boşluklar temizlenir. Düz bir nesne yerine Headers kullanmanın faydası, bu temizliği ve bazı başlıkların yasaklanmasını sizin yerinize yapmasıdır.
const h = new Headers({ "Content-Type": "text/xml" });
h.append("Accept-Encoding", "deflate");
h.append("Accept-Encoding", "gzip");
console.log(h.get("content-type")); // text/xml
console.log(h.get("Accept-Encoding")); // deflate, gzip
h.set("Accept-Encoding", "br");
console.log(h.get("accept-encoding")); // br
console.log(h.has("CONTENT-TYPE")); // true
console.log(h.get("X-Yok")); // null
h.append("X-Istek-Kimligi", " abc-123 ");
console.log(JSON.stringify(h.get("x-istek-kimligi"))); // "abc-123"append() ile set() arasındaki fark tam olarak burada görünür: başlık zaten varsa append() yeni değeri mevcut değerlerin sonuna ekler, set() ise eskisini siler ve yalnızca yeni değeri bırakır. get() aynı adlı birden çok değeri eklenme sırasına göre virgül ve boşlukla birleştirip tek bir metin olarak döndürür; başlık yoksa null verir. delete() başlığı tamamen kaldırır.
Gezinme ve sıralama
Headers nesnesi doğrudan for...of ile gezilebilir; bu, entries() kullanmakla aynıdır. keys(), values() ve forEach() da vardır. Gezinme sırasında adlar sözlük sırasına göre dizilir ve aynı adlı değerler birleştirilmiş olarak gelir:
for (const [ad, deger] of h) {
console.log(`${ad} = ${deger}`);
}
// accept-encoding = br
// content-type = text/xml
// x-istek-kimligi = abc-123
h.delete("content-type");
console.log(h.has("Content-Type")); // falseGeçersiz adlar ve Set-Cookie
Geçerli bir HTTP başlık adı olmayan bir ad verirseniz (örneğin boşluk veya Türkçe karakter içeren bir ad), tüm Headers yöntemleri TypeError fırlatır:
try {
new Headers({ "Geçersiz Ad": "x" });
} catch (e) {
console.log(e.name); // TypeError
}Set-Cookie başlığı özel bir durumdur: birden çok Set-Cookie değerini virgülle birleştirmek çerez söz dizimini bozabilir. getSetCookie() yöntemi bu değerleri ayrı ayrı bir dizi olarak döndürür. MDN bu yöntemin esas olarak sunucu ortamları için tasarlandığını belirtir; tarayıcılar ağdan gelen yanıtlarda Set-Cookie başlığını ön yüz koduna hiç göstermez, bu yüzden sayfada yapılan bir fetch() yanıtında boş dizi alırsınız.
const sc = new Headers({ "Set-Cookie": "name1=value1" });
sc.append("Set-Cookie", "name2=value2");
console.log(JSON.stringify(sc.getSetCookie())); // ["name1=value1","name2=value2"]
console.log(JSON.stringify(new Headers().getSetCookie())); // []Değiştirme kısıtları
Bir Headers nesnesinin değiştirilip değiştirilemeyeceği, nereden geldiğine bağlıdır. MDN'deki kurallar şöyle özetlenebilir:
| Headers nereden geldi | Değiştirilebilir mi |
|---|---|
| new Headers() ile oluşturuldu | Evet, kısıt yok |
| Request başlıkları, mode no-cors | Yalnızca CORS güvenli listesindeki başlıklar |
| Request başlıkları, diğer modlar | Yasaklı istek başlıkları dışındakiler |
| fetch() ile gelen Response | Hayır, değiştirilemez |
| Response.error() veya Response.redirect() | Hayır, değiştirilemez |
| new Response() veya Response.json() | Yasaklı yanıt başlıkları dışındakiler |
Değiştirilemez bir listeye yazmaya çalışmak TypeError fırlatır:
try {
Response.error().headers.set("X-Deneme", "1");
} catch (e) {
console.log(e.name); // TypeError
}
const kendi = new Response("ok");
kendi.headers.set("X-Deneme", "1");
console.log(kendi.headers.get("x-deneme")); // 1Yasaklı istek başlıkları (forbidden request headers), tarayıcının kendisinin yönettiği ve betiklerin ayarlayamadığı başlıklardır; örneğin isteğin geldiği kökeni bildiren Origin başlığını sayfadaki kod değiştiremez. Node.js bu tarayıcı kısıtını uygulamadığı için bu davranışı yalnızca tarayıcıda gözlemleyebilirsiniz.
Request nesnesi
Request() kurucusu fetch() ile aynı argümanları alır: bir URL veya başka bir Request, ardından isteğe bağlı bir RequestInit nesnesi. Seçenekleri doğrudan fetch()'e vermek yerine önce bir Request oluşturup onu göndermek tamamen eşdeğerdir. Bu nesneyle en sık service worker içinde karşılaşırsınız; orada FetchEvent.request olarak hazır gelir.
const r = new Request("https://example.org/flowers.jpg");
console.log(r.method, r.mode, r.credentials, r.cache, r.redirect);
// GET cors same-origin default followVarsayılanları burada açıkça görüyorsunuz: yöntem GET, mod cors, kimlik bilgileri same-origin, önbellek modu default, yönlendirme davranışı follow. Kurucu, URL kullanıcı adı ve parola içeriyorsa (https://user:pass@example.org/ gibi) veya ayrıştırılamıyorsa TypeError fırlatır; seçeneklerde tanımsız bir mod adı vermek de aynı hatayı üretir.
Var olan bir isteği değiştirerek kopyalamak
İlk argüman olarak bir Request verirseniz kopyası oluşturulur ve ikinci argümandaki seçenekler kopyadaki karşılıklarının yerine geçer. MDN'nin örneğinde From başlığı bu şekilde değiştiriliyor:
const oldRequest = new Request("https://github.com/mdn/content/issues/12959", {
headers: { From: "webmaster@example.org" },
});
const newRequest = new Request(oldRequest, {
headers: { From: "developer@example.org" },
});
console.log(oldRequest.headers.get("From")); // webmaster@example.org
console.log(newRequest.headers.get("From")); // developer@example.org
const p1 = new Request("https://example.org/post", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "example1" }),
});
const p2 = new Request(p1, { body: JSON.stringify({ username: "example2" }) });
console.log(p2.method, p2.headers.get("content-type")); // POST application/json
console.log(await p2.text()); // {"username":"example2"}Kopyalanan istek başka bir kökenden geliyorsa referrer değeri silinir; modu navigate olan bir istek kopyalanırken mod same-origin'e çevrilir. Ayrıca bir Request'i fetch()'e ikinci bir seçenekler nesnesiyle birlikte verirseniz ve aynı seçenek iki yerde de tanımlıysa, fetch()'e doğrudan verilen değer kazanır.
RequestInit seçenekleri
Aşağıdaki tablo, günlük işlerde en çok kullanılan seçenekleri ve varsayılan değerlerini gösterir. Tam liste için MDN'deki RequestInit sayfasına bakabilirsiniz.
| Seçenek | Varsayılan | Görevi |
|---|---|---|
| method | GET | HTTP yöntemi |
| headers | boş | Headers nesnesi veya düz nesne |
| body | yok | Gönderilecek gövde; GET ve HEAD ile kullanılamaz |
| mode | cors | Kökenler arası davranış: cors, same-origin, no-cors |
| credentials | same-origin | Çerez ve kimlik bilgisi gönderimi: omit, same-origin, include |
| cache | default | Tarayıcının HTTP önbelleğiyle ilişkisi |
| redirect | follow | Yönlendirmeyi izle, hata ver veya manual |
| referrer | about:client | Referer başlığı; boş metin başlığı kaldırır |
| integrity | boş metin | Alt kaynak bütünlüğü özeti (sha256, sha384, sha512) |
| keepalive | false | Sayfa kapansa da isteği sürdür; gövde 64 KiB ile sınırlı |
| priority | auto | Aynı türdeki isteklere göre öncelik: high, low, auto |
| signal | yok | İptal için AbortSignal |
mode: cors, same-origin, no-cors
cors modunda başka kökene giden istek CORS kurallarıyla değerlendirilir ve yalnızca CORS güvenli listesindeki yanıt başlıkları koda açılır. same-origin başka kökene isteği ağ hatasıyla sonuçlandırır. no-cors ise CORS denetimini kapatır, karşılığında ağır kısıtlar getirir: yöntem yalnızca GET, HEAD veya POST olabilir, yalnızca güvenli listedeki istek başlıkları gönderilebilir (Range da hariç) ve dönen yanıt opaktır; durum kodu 0, başlıklar boş, gövde null'dır. MDN, bu modun asıl kullanım alanının service worker önbelleği olduğunu ve çoğu sitenin onu kullanmaması gerektiğini vurgular. CORS mekanizmasının kendisi CORS nedir yazısında anlatılıyor.
credentials: omit, same-origin, include
Bu seçenek hem isteğe kimlik bilgisi eklenip eklenmeyeceğini hem de yanıttaki Set-Cookie başlıklarının dikkate alınıp alınmayacağını belirler. include ile başka kökene çerez gönderebilmek için sunucu Access-Control-Allow-Credentials: true döndürmeli ve Access-Control-Allow-Origin değerinde yıldız yerine sizin kökeninizi açıkça yazmalıdır.
cache modları
default modunda tarayıcı önbelleğe bakar; taze bir kopya varsa onu kullanır, bayatlamışsa sunucuya koşullu istek gönderir. no-store önbelleğe hiç bakmaz ve indirdiğini de kaydetmez. reload önbelleğe bakmadan indirir ama sonucu önbelleğe yazar. no-cache eşleşme taze olsa bile her seferinde koşullu istekle doğrulama yapar. force-cache eşleşmeyi bayat olsa da kullanır. only-if-cached yalnızca önbellekten yanıt verir ve yalnızca same-origin moduyla kullanılabilir. Sunucu tarafındaki Cache-Control ve ETag başlıkları için Cache-Control ve koşullu istekler yazısına bakabilirsiniz.
// Önbelleği tamamen atla
fetch("some.json", { cache: "no-store" });
// Önbelleği atla ama sonucu önbelleğe yaz
fetch("some.json", { cache: "reload" });
// Önbellekteki kopyayı her seferinde sunucuyla doğrula
fetch("some.json", { cache: "no-cache" });Response nesnesi

Response() kurucusu bir gövde ve isteğe bağlı olarak status, statusText ve headers seçeneklerini alır. Varsayılan durum kodu 200, varsayılan durum metni boş metindir. Gövde null, metin, Blob, ArrayBuffer, typed array, DataView, FormData, URLSearchParams veya ReadableStream olabilir:
const ikili = new Response(new Uint8Array([72, 105]));
console.log(await ikili.text()); // Hi
const blobRes = new Response(new Blob(["blob içerik"], { type: "text/plain" }));
const blob = await blobRes.blob();
console.log(blob.size, blob.type); // 12 text/plain
const bos = new Response(null, { status: 204 });
console.log(bos.body, bos.ok); // null trueBlob boyutunun 12 çıkmasına dikkat edin: "blob içerik" metni 11 karakterdir ama ç harfi UTF-8'de iki bayt tuttuğu için boyut bayt cinsinden 12'dir.
Statik yöntemler
Response.json(veri, seçenekler) JSON gövdeli ve Content-Type: application/json başlıklı bir yanıt üretir; veri JSON'a çevrilemiyorsa (örneğin döngüsel başvuru içeriyorsa) TypeError fırlatır. Response.redirect(url, durum) yönlendirme yanıtı üretir; durum kodu 301, 302, 303, 307 veya 308 olmalıdır, verilmezse 302 kullanılır. Response.error() ise ağ hatasını temsil eden bir yanıt döndürür; service worker bu yanıtı verirse sayfadaki fetch() sözü reddedilir.
const js = Response.json({ some: "data" }, { status: 307, statusText: "Temporary Redirect" });
console.log(js.status, js.statusText, js.ok); // 307 Temporary Redirect false
const yonlendir = Response.redirect("https://www.example.com", 301);
console.log(yonlendir.status, yonlendir.headers.get("location")); // 301 https://www.example.com/
try {
Response.redirect("https://www.example.com", 200);
} catch (e) {
console.log(e.name); // RangeError
}
const hata = Response.error();
console.log(hata.type, hata.status, hata.body); // error 0 nullResponse.type değerleri
type özelliği, betiğin yanıtın ne kadarını görebileceğini belirler:
| type | Ne zaman | Kodun görebildiği |
|---|---|---|
| basic | Aynı köken, data: URL, navigate veya websocket modu | Set-Cookie dışındaki tüm başlıklar ve gövde |
| cors | Başka köken, CORS ile başarılı | Yalnızca CORS güvenli listesindeki başlıklar ve gövde |
| opaque | Başka köken, mode no-cors | Hiçbir şey: status 0, başlık yok, body null |
| opaqueredirect | redirect manual ve sunucu yönlendirdi | Hiçbir şey: status 0, başlık yok, body null |
| error | Response.error() ile üretildi | Hiçbir şey; fetch() bu türü döndürmez |
Sunucu, varsayılan güvenli liste dışındaki bir başlığı (örneğin kendi X-Toplam-Kayit başlığınızı) koda göstermek istiyorsa Access-Control-Expose-Headers başlığıyla bunu açıkça bildirmelidir.
Gövde yalnızca bir kez okunur: bodyUsed ve clone()
İstek ve yanıt gövdeleri akıştır. Bir okuma yöntemi çağrıldığında akış sonuna kadar tüketilir ve bodyUsed true olur. Bundan sonra gövdeyi tekrar okumak da, nesneyi kopyalamak da TypeError verir. Gövdeyi iki yerde kullanacaksanız, okumadan önce clone() çağırırsınız:
const res = new Response("merhaba");
console.log(res.bodyUsed); // false
const txt = await res.text();
console.log(txt, res.bodyUsed); // merhaba true
try {
res.clone();
} catch (e) {
console.log(e.name); // TypeError
}
const res2 = new Response("iki kez");
const kopya = res2.clone();
console.log(await res2.text(), "|", await kopya.text()); // iki kez | iki kezMDN önemli bir uyarı ekler: klonlanan gövde, altta ReadableStream.tee() gibi çalışır. Akışın hızı iki kopyadan hızlı olanına göre ayarlanır ve yavaş taraf için okunmamış veri sınırsız biçimde bellekte biriktirilir. Kopyalardan yalnızca biri okunursa gövdenin tamamı bellekte tutulur. Bu yüzden clone(), bir yanıtı sırayla iki kez okumak için uygundur; çok büyük gövdeleri farklı hızlarda paralel okumak için uygun değildir.
Bu kalıbın en bilinen kullanımı service worker önbelleğidir: yanıtın bir kopyası önbelleğe yazılır, orijinali sayfaya döndürülür. Aşağıdaki kod service worker bağlamında çalışır:
async function cacheFirst(request) {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
try {
const networkResponse = await fetch(request);
if (networkResponse.ok) {
const cache = await caches.open("MyCache_1");
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
return Response.error();
}
}Önbellek stratejilerinin tamamı service worker çevrimdışı önbellek stratejileri yazısında, caches.open() ve cache.put() ayrıntıları ise Cache API yazısında anlatılıyor.
Sık yapılan hatalar
- append yerine set beklemek: aynı başlığa iki kez
append()yaparsanız değerler birleşir; tek değer istiyorsanızset()kullanın. - Gelen yanıtın başlığını değiştirmeye çalışmak:
fetch()yanıtının başlıkları değiştirilemez; yeni başlık gerekiyorsanew Response(eski.body, { headers })ile yeni bir yanıt oluşturun. - Set-Cookie değerini okumayı beklemek: tarayıcı bu başlığı ön yüz koduna hiç vermez.
- Okunmuş yanıtı klonlamak:
clone()mutlaka ilk okumadan önce çağrılmalıdır. - no-cors ile veri okumaya çalışmak: opak yanıtın gövdesi ve başlıkları hiçbir zaman okunamaz; CORS hatasının çözümü değildir.
- Özel başlığın görünmemesini kod hatası sanmak: CORS yanıtında sunucu
Access-Control-Expose-Headersgöndermeden özel başlıkları göremezsiniz.
Sık Sorulan Sorular
Headers.get() neden virgüllü bir değer döndürüyor?
Aynı adla birden çok değer eklendiyse get() hepsini eklenme sırasıyla "virgül ve boşluk" ile birleştirip tek metin olarak verir. Ayrı değerlere ihtiyacınız varsa metni bölebilirsiniz; Set-Cookie için ise getSetCookie() kullanılır.
Request nesnesi ile fetch() seçenekleri arasında fark var mı?
Hayır, Request() kurucusu fetch() ile aynı parametreleri alır. Fark yalnızca kullanım kolaylığıdır: bir Request'i saklayabilir, kopyalayabilir, service worker'da inceleyebilir ve değiştirilmiş bir kopyasını oluşturabilirsiniz.
Response.type neden opaque geliyor?
İsteği mode: "no-cors" ile başka bir kökene gönderdiğiniz içindir. Bu modda tarayıcı CORS denetimini atlar ama yanıtın içeriğini koda göstermez. Veriyi okumanız gerekiyorsa sunucunun CORS başlıklarını doğru ayarlaması gerekir; CORS hataları rehberi bu konuda yardımcı olur.
clone() bellek sorununa yol açar mı?
Kopyalardan biri hiç okunmazsa veya ikisi çok farklı hızlarda okunursa, okunmamış veri bellekte birikir. Küçük ve orta boy yanıtlarda bu genellikle sorun değildir; büyük dosyalarda iki kopyayı da makul bir sürede tüketmeye dikkat edin.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · Fetch API kullanımı · CORS nedir · Service worker önbellek stratejileri · Cache-Control ve ETag · Web API hataları