Geonode logo
Geonode Team

Geonode Team

Güncellenme: 7 Ekim 2026

Yayınlanma: 2 Eylül 2026

node-fetch ile Başlıkları Ayarlama

Node, v18 sürümünden beri yerleşik bir `fetch`'e sahiptir; bu da `node-fetch` adresini arayan çoğu kişinin artık bu pakete hiç ihtiyaç duymadığı anlamına gelir. Her iki durumda da başlıkların ayarlanması oldukça basittir. Ancak `set` ile `append` arasındaki farkların ne olduğu, hangi başlıkların ayarlanmasına izin verilmediği ve yapılandırdığınız bir başlığın neden ağ üzerinde görünmediği gibi konular o kadar açık değildir. Bu kılavuz, tüm bu konuları ve ayrıca Node'daki diğer tüm HTTP istemcilerinden farklı olan proxy davranışını ele almaktadır.

Burada dikkat edilmesi gereken belirli bir tuzak var: Biz Geonode olarak proxy satıyoruz ve Node’un yerleşik fetch komutu, HTTP_PROXY ve HTTPS_PROXY ortam değişkenlerini tamamen görmezden geliyor. Ekosistemdeki diğer tüm HTTP istemcileri bu değişkenleri dikkate alıyor; bu nedenle kullanıcılar bir proxy yapılandırıyor, isteklerin başarılı olduğunu görüyor ve sistemin çalıştığını varsayıyor — oysa trafik doğrudan gidiyor. Ne bir uyarı ne de bir hata mesajı görüntülenir. Düzeltme birkaç satırlık bir koddan ibarettir ve aşağıdaki proxy bölümünde yer almaktadır. Node'fetch trafiğini proxy üzerinden yönlendiriyorsanız ve bir dağıtıcıyı (dispatcher) açıkça ayarlamadıysanız, isteklerinizin proxy'nizden geçmediği neredeyse kesindir.

Yerel fetch mi, yoksa node-fetch paketi mi?

Buradan başlayın, çünkü bu, neyi yükleyeceğinizi belirler.

Node'un dokümantasyonu, fetch işlevinin v17.5.0 ve v16.15.0 sürümlerinde eklendiğini, v18.0.0 sürümünden itibaren --experimental-fetch bayrağının arkasından çıktığını ve v21.0.0 sürümünden itibaren "artık deneysel olmadığını" belirtir. Bu, "Node.js için sıfırdan yazılmış bir HTTP/1.1 istemcisi olan undici'ye dayalı, fetch() işlevinin tarayıcı uyumlu bir uygulaması" olarak tanımlanmaktadır. Headers, Request ve Response de aynı zaman çizelgesini takip etmektedir.

Dolayısıyla, şu anda desteklenen herhangi bir Node sürümünde, fetch bir global değişkendir ve herhangi bir bağımlılığa gerek yoktur.

node-fetch paketi iki durumda yararlı olmaya devam eder: eski bir çalışma zamanında kodu sürdürmek ve API’sının farklı olduğu az sayıdaki davranışlardan birine ihtiyaç duyulması. Sürüm 3’ün yalnızca ESM’ye özel olduğunu ve bu durumun hâlâ require kullanan projeler için sorun teşkil ettiğini unutmayın.

API kasıtlı olarak aynı tutulduğundan, aşağıda belirtilen her şey her ikisi için de geçerlidir.

Başlıkları Ayarlamanın Üç Yolu

Düz bir nesne — en yaygın durum ve çoğu zaman kullanılması gereken yöntem:

const res = await fetch("https://api.example.com/items", {
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer eyJhbG...",
    "Accept": "application/json",
  },
});

**Bir Headers

nesnesi** — koşullu olarak yapılandırma oluşturduğunuzda:

const headers = new Headers({ "Accept": "application/json" });
if (token) headers.set("Authorization", `Bearer ${token}`);
if (locale) headers.set("Accept-Language", locale);

const res = await fetch(url, { headers });

Çiftlerden oluşan bir dizi — başlığın meşru bir şekilde tekrarlandığı durumlarda kullanışlıdır:

const res = await fetch(url, {
  headers: [
    ["Accept", "application/json"],
    ["X-Trace", "a"],
    ["X-Trace", "b"],
  ],
});

Basit durumlarda bu üçü de eşdeğerdir. Headers

nesnesi, koşullu mantığa ihtiyaç duyduğunuzda veya göndermeden önce oluşturduğunuz içeriği incelemek istediğinizde kullanışlıdır.

set

ile append Arasındaki Fark: Sürprizlere Yol Açan Ayrım.

MDN belgeleri, set() komutunu “mevcut bir başlık için yeni bir değer belirleme” ve mevcut değerlerin üzerine yazma olarak tanımlarken, append() komutu ise “mevcut bir başlığa yeni bir değer ekler veya başlık yoksa onu ekler”.

const h = new Headers();
h.append("X-Custom", "one");
h.append("X-Custom", "two");
h.get("X-Custom");        // "one, two"

h.set("X-Custom", "three");
h.get("X-Custom");        // "three"

append set biriktirir; değiştirir. Gönderdiğiniz hemen hemen her başlık için set istediğiniz şeydir — iki Authorization değeri göndermek anlamlı bir istek değildir. append , birden fazla değerin geçerli olduğu başlıklar için önemlidir ve pratikte bu durum çok az sayıda başlıkta görülür.

Başlık adlarında büyük/küçük harf ayrımı yapılmaz. MDN, bunların her yöntemde "büyük/küçük harf ayrımı yapılmayan bayt dizisiyle eşleştirildiğini" belirtir; dolayısıyla h.get("content-type") ve h.get("Content-Type") aynı değeri döndürür. Okunabilirlik için bir kural belirleyin ve bu konuyu dert etmeyi bırakın.

Ayrıca, varlığını test etmek için has() , kaldırmak için delete() ve tüm Set-Cookie değerlerinin bir dizisini döndüren getSetCookie() vardır — bu, söz konusu başlığın gerçekten birden fazla değerin bir arada bulunduğu ana durum olması ve basit bir get() komutunun bunları güvenilir bir şekilde ayıramayacağınız bir şeye birleştireceği için gereklidir.

Ayarlanamayan Başlıklar

Yapılandırdığınız bir başlığın görünmemesinin nedeni.

MDN, Headers nesneleri üzerinde neyin değiştirilebileceğini belirleyen bir “koruma” mekanizmasını açıklamaktadır. Tek başına bir new Headers() üzerinde herhangi bir kısıtlama yoktur. Request adresine eklenen başlıklar, "yasaklanmamış istek başlıklarının" değiştirilmesine izin verir. Ayrıca, "Response.error(), Response.redirect() veya fetch()" adreslerinden alınan Response üzerindeki başlıklar değiştirilemez; yanıtı aldıktan sonra yanıtın başlıklarını değiştiremezsiniz.

Yasaklanmış istek başlıkları, çalışma zamanı tarafından kontrol edilen başlıklardır ve bunları ayarlama girişimleri, hata vermeden sessizce göz ardı edilir. Bu liste, Host, Connection, Content-Length, Transfer-Encoding, Origin, bazı bağlamlarda Referer ile Sec- ve Proxy- önekli aileleri içerir.

Bunun iki pratik sonucu vardır.

Sessizlik, hata modudur. İstisna yok, uyarı yok; başlık basitçe gönderilmez. Bir sunucu, sizin ayarladığınız bir şeyi almadığını ısrarla söylüyorsa, kodunuzu yeniden okumak yerine, ağ üzerinden gerçekte neyin gönderildiğini doğrulayın.

Node, bunların bazıları için tarayıcıdan daha esnektir, çünkü korunması gereken bir kaynak yoktur. Node'da bir başlığı başarıyla ayarlayan kod, tarayıcıda bu başlığın atlandığını görebilir; bu da paylaşılan kodlar için gerçek bir taşınabilirlik tuzağıdır.

Gerçekte ne gönderdiğinizi kontrol etmek için, bunu geri yankılayan bir servise istek gönderin:

const res = await fetch("https://httpbin.org/headers", {
  headers: { "X-Test": "value", "User-Agent": "MyBot/1.0" },
});
console.log(await res.json());

Yanıt Başlıklarını Okuma

Diğer yarısında ise, bilinmesi gereken bir davranış daha var.

const res = await fetch(url);

res.headers.get("content-type");
res.headers.has("etag");

for (const [name, value] of res.headers) {
  console.log(name, value);
}

Başlık kümesi normalleştirildiği için yineleme işlemi küçük harfli adlar verir.

**Set-Cookie

özel bir işleme tabi tutulmalıdır.** Birden fazla çerez, birden fazla başlık olarak gelir ve basit bir get("set-cookie")

komutu, bunları virgülle birleştirilmiş olarak verir — bu durum belirsizliğe yol açar, çünkü çerez değerlerinin kendileri de Expires

tarihinde virgül içerebilir. getSetCookie()

tam da bu amaçla mevcuttur ve bir dizi döndürür:

const cookies = res.headers.getSetCookie();

Yanıt başlıkları değiştirilemez. fetch

komutunun döndürdüğü değeri değiştiremezsiniz. Değiştirilmiş bir sürüme ihtiyacınız varsa, yeni bir Response

oluşturun.

Ve herhangi bir başlıktan daha önemli olan kontrol: fetch

, HTTP hata durumlarında reddetmez. 404 veya 500 hataları normal şekilde çözümlenir, bu nedenle gövdeyi yorumlamadan önce res.ok

test edilmelidir.

const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);

Bunu atlamak, .json()

çağrısında Unexpected token '<'

hatasının doğrudan nedenidir — bir hata sayfasını ayrıştırmış olursunuz.

Varsayılan Başlıklar ve Node’un Ekledikleri

Node, birkaç başlığı otomatik olarak ayarlar; bunların hangileri olduğunu bilmek, karışıklığı önler.

Host — URL’den türetilir, ayarlanamaz. Connection — bağlantı havuzu tarafından yönetilir. Content-Length — gövdeden hesaplanır. Accept — siz ayarlamadığınız sürece varsayılan olarak */* olur. Accept-Encoding — Node, sıkıştırma desteğini bildirir ve yanıtı şeffaf bir şekilde açar. User-Agent — Node varsayılan olarak kendi kullanıcı aracısını gönderir; bu genellikle undici'yi tanımlar.

Sonuncusu, üçüncü taraflarla iletişim kuran her şey için önemlidir. Varsayılan çalışma zamanı kullanıcı aracısı doğru bir tanımlamadır, ancak otomatik istemciler için yetersizdir — iletişim URL'si içeren dürüst bir ad, anonim bir çalışma zamanı dizesinden daha iyi değerlendirilir:

headers: { "User-Agent": "AcmeBot/1.0 (+https://acme.example.com/bot)" }

İçerik gövdeleriyle ilgili bir not: Bir FormData nesnesi ilettiğinizde, Content-Type değerini kendiniz ayarlamayın. Bu değer, çok parçalı sınırı içerdiğinden çalışma zamanı tarafından oluşturulmalıdır; bu değeri geçersiz kılmak, sunucunun ayrıştıramayacağı bir istek oluşturur. Bu, açıklanamayan 400 veya 415 hatalarının en yaygın nedenlerinden biridir.

Her İstek İçin Başlık Ayarlama

Komut dosyası dışındaki her şey için, bunu merkezi bir şekilde halledin.

const DEFAULTS = {
  "Accept": "application/json",
  "User-Agent": "AcmeBot/1.0 (+https://acme.example.com/bot)",
};

async function api(path, options = {}) {
  const res = await fetch(`https://api.example.com${path}`, {
    ...options,
    headers: { ...DEFAULTS, ...options.headers },
  });
  if (!res.ok) {
    const body = await res.text();
    throw new Error(`HTTP ${res.status} ${path}: ${body.slice(0, 200)}`);
  }
  return res;
}

Buradaki iki ayrıntı özellikle önemlidir. Öncelikle varsayılan değerleri yaymak, çağrı yapanın bunlardan herhangi birini geçersiz kılabileceği anlamına gelir; bu da tam olarak istediğiniz davranıştır. Ayrıca hata gövdesinin ilk 200 karakterini dahil etmek, anlamsız bir durum kodunu, üzerinde işlem yapabileceğiniz bir mesaja dönüştürür.

Nesne yayılımının yüzeysel olduğunu ve tam anahtar dizesiyle eşleştiğini unutmayın; bu nedenle, çağrı yapan tarafın seçeneklerindeki "content-type" değeri, varsayılanlardaki "Content-Type" değerini geçersiz kılmaz — her ikisini de gönderirsiniz ve çalışma zamanı bunlardan birini seçer. Çağrı yapan tarafların büyük/küçük harf kullanımında serbestlik olması durumunda, bunun yerine bir Headers nesnesi oluşturun ve büyük/küçük harfe duyarlı olmayan set() yönteminin birleştirme işlemini düzgün bir şekilde halletmesine izin verin.

Çalışmayan Başlıkların Hata Ayıklaması

En fazla olasılığı eleyen sırayla, neredeyse her başlık sorununu birkaç dakika içinde çözen bir adımlar dizisi.

Bir — ağ üzerinden gerçekte neyin aktarıldığını kontrol edin. Bunu yapana kadar bu listedeki diğer hiçbir şeyin önemi yoktur. Bir başlık yankılama hizmeti en hızlı yoldur:

const res = await fetch("https://httpbin.org/headers", { headers: myHeaders });
console.log(JSON.stringify(await res.json(), null, 2));

Başlığınız burada yoksa, işlemden hiç çıkmamıştır — yasaklanmış, yanlış yazılmış veya üzerine yazılmıştır. Eğer burada mevcutsa ve hedef aksini söylüyorsa, siz ile hedef arasında bir şey başlığı siliyor demektir.

İki — Headers nesnesini oluşturun ve göndermeden önce inceleyin. Bu, “ben yanlış oluşturdum” ile “çalışma zamanı onu düşürdü” durumlarını birbirinden ayırır:

const h = new Headers(myHeaders);
console.log([...h.entries()]);

Headers nesnesini oluşturmak, çalışma zamanının uygulayacağı normalleştirme işlemini uygular; dolayısıyla burada ayakta kalan bir ad, gönderilecek olan addır.

Üç — yanlışlıkla yapılan yinelemeleri kontrol edin. Yüzeysel birleştirme tuzağı: bir nesnenin yayılması, anahtarları tam dize eşleşmesine göre yapar; bu nedenle {...{"Content-Type": "a"}, ...{"content-type": "b"}} her iki girişi de üretir. Çağıranlar rastgele büyük/küçük harf kullanımı sağlayabiliyorsa, Headers nesnesi oluşturun ve set() kullanın; çünkü bu, büyük/küçük harfe duyarlı olmayan eşleştirme sayesinde birleştirmeyi doğru şekilde gerçekleştirir.

Dört — bunu curl ile yeniden oluşturun. Aynı istek terminalden çalışıyorsa da Node'dan çalışmıyorsa, fark sunucuda değil kodunuzdadır:

curl -v -H "Authorization: Bearer $TOKEN" https://api.example.com/items 2>&1 | grep '^>'

İki > bloğunu yan yana karşılaştırmak genellikle farkı açıkça ortaya çıkarır.

Beş — sadece durumu değil, yanıtın tamamını okuyun. 400 veya 401 kodlu yanıtlar genellikle hangi başlığın hatalı olduğunu tam olarak açıklayan bir gövde içerir ve bunu göz ardı eden kod, yanıtı çöpe atmış olur:

if (!res.ok) console.error(res.status, (await res.text()).slice(0, 300));

Ayrıca yönlendirmeyi de kontrol edin. fetch varsayılan olarak yönlendirmeleri takip eder ve bazı başlıklar — özellikle Authorization — yönlendirme farklı bir kaynağa geçtiğinde atılır. Bir istek, orijinal URL'ye değil de son URL'ye doğrudan ulaşırsa, bunun nedeni neredeyse kesinlikle budur.

Proxy Tuzağı

Diğer tüm Node HTTP istemcilerinden farklı olan davranış ve bu bölümün var olma nedeni.

**Node'un fetch

ayarı, HTTP_PROXY

, HTTPS_PROXY

veya NO_PROXY

adreslerini okumaz.** Bunları ayarlamak hiçbir şeyi değiştirmez. İstekler doğrudan gönderilir, başarılı olur ve proxy'nin atlandığını gösteren hiçbir işaret yoktur.

Çözüm, undici'nin ProxyAgent

adresinde yer alan yazısıdır:

import { ProxyAgent, setGlobalDispatcher } from "undici";

setGlobalDispatcher(new ProxyAgent("http://user:pass@proxy.example.com:9000"));

// now every fetch in this process goes through the proxy
const res = await fetch("https://api.example.com/items");

Tüm işlem yerine tek bir istek için, her çağrı başına bir dispatcher aktarın:

const agent = new ProxyAgent("http://proxy.example.com:9000");
const res = await fetch(url, { dispatcher: agent });

dispatcher

'in standart Fetch API'sinin bir parçası değil, Node'a özgü bir uzantı olduğunu unutmayın; bu nedenle bunu kullanan kodlar tarayıcılara taşınamaz.

Her zaman etkinleştiğini doğrulayın. Dispatcher kullanıldığında ve kullanılmadığında hizmetin hangi adresi gördüğünü sorun:

const res = await fetch("https://api.ipify.org?format=json");
console.log(await res.json());

Adres değişmezse, proxy yolunda değildir — ve sizi uyaran bir hata olmadığı göz önüne alındığında, bu kontrol, çalışan bir yapılandırma ile sessizce atlanan bir yapılandırma arasındaki tek farktır. Bu, proxy'leri test etmenin önemi başlıklı yazımızda bahsettiğimiz sessiz hata türünün aynısıdır.

Buradaki HTTP istemcileri arasındaki davranış farklılıkları, axios ile fetch'in karşılaştırılması başlıklı yazımızda karşılaştırdığımız türden farklılıklardır.

Sık Sorulan Sorular

node-fetch ile başlıkları nasıl ayarlayabilirim?

Seçenekler bölümüne bir headers nesnesi aktarın: fetch(url, { headers: { "Authorization": "Bearer ..." } }). Ayrıca bir Headers örneği veya isim-değer çiftlerinden oluşan bir dizi de aktarabilirsiniz. Aynı sözdizimi, Node'un yerleşik fetch işleviyle de çalışır.

Hâlâ node-fetch paketine ihtiyacım var mı?

Genellikle gerekmez. Node, v17.5.0 sürümünden beri fetch global değişkenine sahiptir; bu değişken v18 sürümünden itibaren bayraksız hale gelmiş ve v21 sürümünden itibaren kararlı hale gelmiştir. Paketi yalnızca eski çalışma zamanları için veya belirli bir davranış farkı nedeniyle yükleyin — ayrıca sürüm 3'ün yalnızca ESM ile uyumlu olduğunu unutmayın.

headers.set ile headers.append arasındaki fark nedir?

set, o başlık için mevcut herhangi bir değeri değiştirir; append ise başka bir değer ekler; dolayısıyla iki kez append işlemi yapıldığında virgülle ayrılmış bir liste oluşur. Hemen hemen her durumda set kullanın — append yalnızca birden fazla değerin geçerli olduğu başlıklar için önemlidir.

Başlığım neden gönderilmiyor?

Büyük olasılıkla, çalışma zamanı tarafından kontrol edilen yasaklanmış bir başlıktır — Host, Connection, Content-Length ve Sec- ailesi bunlara dahildir. Bunlar, hata vermeden sessizce göz ardı edilir. Gerçekte ağ üzerinden neyin aktarıldığını görmek için bir başlık yankılama hizmetine istek gönderin.

Fetch'te başlık adlarında büyük/küçük harf duyarlılığı var mı?

Hayır. MDN, başlık adlarının tüm Headers yöntemlerinde büyük/küçük harf duyarlılığı gözetilmeden bayt dizisiyle eşleştirildiğini belirtir; dolayısıyla get("content-type") ve get("Content-Type") eşdeğerdir. Bir Headers nesnesini döngüye soktuğunuzda küçük harfli adlar elde edersiniz.

Birden fazla Set-Cookie başlığını nasıl okuyabilirim?

Bir dizi döndüren res.headers.getSetCookie() işlevini kullanın. Basit bir get("set-cookie") işlevi bunları virgülle birleştirir; ancak çerez değerlerinin kendileri de Expires tarihinde virgül içerebileceğinden bu durum belirsizliğe yol açar.

Node fetch, HTTP_PROXY ayarımı neden yok sayıyor?

Çünkü diğer hemen hemen tüm Node HTTP istemcilerinin aksine, bu ortam değişkenlerini hiç okumaz. undici'nin ProxyAgent komutunu setGlobalDispatcher ile birlikte kullanın ya da her istek için bir dispatcher aktarın — ve ardından çıkış adresini doğrulayın, çünkü atlanan bir proxy hata üretmez.

FormData gönderirken Content-Type'ı ayarlamalı mıyım?

Hayır. Çalışma zamanı, multipart sınırını da içerecek şekilde bunu oluşturur ve bunu kendiniz ayarladığınızda sınır kaldırılır, bu da sunucunun çözümleyemediği bir istek oluşturur. Bu, açıklanamayan 400 ve 415 yanıtlarının yaygın bir nedenidir.

Sonuç

Node’da başlık ayarlamak, hangi API’yi kullanırsanız kullanın tek satırlık bir işlemdir ve yerleşik fetch sayesinde çoğu proje artık bunun için bir pakete hiç ihtiyaç duymamaktadır.

Neredeyse tüm kafa karışıklıklarının sebebi üç farklı davranış biçimidir. set başlıkları değiştirirken, append başlıkları biriktirir; bunları tersine uygulamak ise sunucuların reddettiği virgülle ayrılmış başlık değerleri ortaya çıkarır. Yasaklanmış başlıklar hata mesajı vermeden sessizce atılır; bu nedenle, sunucunun almadığı bir başlığın, düzenleyicinizde yeniden okunmak yerine ağ üzerinde doğrulanması gerekir. Ayrıca, fetch HTTP hatalarında çözümlenir; bu nedenle, gövdenin bir anlam ifade edebilmesi için önce res.ok kontrol edilmelidir.

Node’a özgü bir tuzak da proxy ile ilgilidir ve bu, çok sessiz bir şekilde hata verdiği için tekrar edilmeye değer: yerleşik fetch, HTTP_PROXY adresini tamamen görmezden gelir. Proxy üzerinden trafik aktarımı gerekiyorsa, bir dağıtıcıyı açıkça ayarlayın — ve ardından çıkış adresini doğrulayın; çünkü hiçbir şey yapmayan bir yapılandırma, çalışan bir yapılandırmaya tıpatıp benzer.