Fetch API ve HTTP
Neden Bu Konu Önemli?
Bir web uygulamasının kalbi, sunucuyla iletişimidir. Kullanıcı bir butona tıkladığında sipariş oluşturulması, sayfanın verilerle doldurulması, formun gönderilmesi — bunların hepsi HTTP istekleri ile gerçekleşir. Ve modern JavaScript'te bu isteklerin standart aracı Fetch API'dir.
Fetch API'den önce XMLHttpRequest (XHR) kullanılıyordu — ama XHR'ın callback tabanlı, karmaşık sözdizimi geliştiricilerin kabusu olmuştu. jQuery'nin $.ajax() fonksiyonu o dönemde neden bu kadar popüler olduğunu açıklıyor. Fetch API ise Promise tabanlı, temiz ve güçlü bir alternatif sunarak modern web geliştirmenin standartı haline geldi.
Düşün ki bir kütüphaneden kitap istiyorsun. Kütüphaneciye rafın adresini (URL), ne istediğini (GET) veya ne vermek istediğini (POST) söylüyorsun. Kütüphaneci gidip kitabı arıyor, bulursa sana veriyor (200 OK), bulamazsa "yok" diyor (404). İşte HTTP tam olarak bu diyaloğun dijital versiyonu ve Fetch API de bu diyaloğu kurmanın modern yolu.
HTTP Temelleri (Hızlı Özet)
Fetch API'yi anlamak için HTTP'nin temellerini bilmek şart. Kısaca:
HTTP Metotları
GET → Veri OKU (kitap isteme)
POST → Yeni veri OLUŞTUR (yeni kitap bağışlama)
PUT → Veriyi tamamen GÜNCELLE (kitabı yenisiyle değiştirme)
PATCH → Veriyi kısmen GÜNCELLE (kitabın sadece kapağını değiştirme)
DELETE → Veriyi SİL (kitabı raftan kaldırma)HTTP Durum Kodları
2xx → Başarılı (200 OK, 201 Created, 204 No Content)
3xx → Yönlendirme (301 Moved, 304 Not Modified)
4xx → İstemci Hatası (400 Bad Request, 401 Unauthorized, 404 Not Found)
5xx → Sunucu Hatası (500 Internal Server Error, 503 Service Unavailable)fetch() Temelleri
fetch() fonksiyonu bir URL alır ve bir Promise döner. Bu Promise, HTTP yanıtını temsil eden bir Response nesnesiyle resolve olur.
// En basit fetch çağrısı
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
// response bir Response nesnesidir — veri DEĞİL!
console.log(response.status); // 200
console.log(response.ok); // true (status 200-299 arasıysa)
console.log(response.statusText); // "OK"
// Veriyi almak için body'yi parse etmemiz gerekir
const user = await response.json(); // JSON → JavaScript objesi
console.log(user.name); // "Leanne Graham"Response Body Metotları
Response nesnesi, body'yi farklı formatlarda parse etmek için metotlar sunar:
const response = await fetch(url);
// JSON olarak parse et (en yaygın)
const data = await response.json();
// Düz metin olarak al
const text = await response.text();
// Binary veri (resim, dosya)
const blob = await response.blob();
// ArrayBuffer (düşük seviye binary)
const buffer = await response.arrayBuffer();
// Form verisi
const formData = await response.formData();⚠️ Dikkat: Body metotları yalnızca bir kez çağrılabilir! İkinci kez çağırırsan hata alırsın çünkü body stream'i zaten tüketilmiştir.
// ❌ Body iki kez okunamaz!
const response = await fetch(url);
const json = await response.json(); // ✅ İlk okuma — sorunsuz
const text = await response.text(); // ❌ Hata! Body zaten tüketildi
// ✅ Body'yi hem text hem JSON olarak kullanman gerekirse:
const response2 = await fetch(url);
const text2 = await response2.text(); // Önce text olarak al
const json2 = JSON.parse(text2); // Sonra kendin parse etGET İstekleri
GET istekleri veri okumak için kullanılır. Fetch'in varsayılan metodu GET'tir — ekstra ayar gerekmez.
// Basit GET isteği
async function kullanicilariGetir() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error(`HTTP Hatası: ${response.status}`);
}
const kullanicilar = await response.json();
kullanicilar.forEach((k) => {
console.log(`${k.id}. ${k.name} — ${k.email}`);
});
return kullanicilar;
} catch (err) {
console.error("Veri çekilemedi:", err.message);
return [];
}
}Query Parameters (Sorgu Parametreleri)
// URL'ye parametre eklemenin temiz yolu: URLSearchParams
async function urunAra(arama, sayfa = 1, limit = 10) {
const params = new URLSearchParams({
q: arama,
page: sayfa,
limit: limit,
sort: "price_asc",
});
// URL: /api/products?q=laptop&page=1&limit=10&sort=price_asc
const response = await fetch(`/api/products?${params}`);
return response.json();
}
// URLSearchParams özel karakterleri otomatik encode eder
const params = new URLSearchParams({ q: "çay bardağı" });
console.log(params.toString()); // "q=%C3%A7ay+barda%C4%9F%C4%B1"POST İstekleri
POST istekleri yeni veri oluşturmak için kullanılır. Body'de gönderilecek veriyi belirtmemiz gerekir.
// JSON veri gönderme
async function yeniKullaniciOlustur(kullanici) {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json", // Gönderdiğimiz verinin formatı
},
body: JSON.stringify(kullanici), // JS objesi → JSON string
});
if (!response.ok) {
throw new Error(`Oluşturma hatası: ${response.status}`);
}
const olusturulan = await response.json();
console.log("Oluşturuldu:", olusturulan);
return olusturulan;
} catch (err) {
console.error("Hata:", err.message);
}
}
// Kullanım
yeniKullaniciOlustur({
name: "Mehmet Yılmaz",
email: "mehmet@ornek.com",
phone: "0555-123-4567",
});Form Verisi Gönderme
// FormData ile dosya yükleme
async function profilResmiYukle(dosya) {
const formData = new FormData();
formData.append("avatar", dosya);
formData.append("userId", "123");
const response = await fetch("/api/upload", {
method: "POST",
// ⚠️ Content-Type AYARLAMA! FormData otomatik ayarlar
// (multipart/form-data ile boundary)
body: formData,
});
return response.json();
}
// HTML'den dosya alma
// <input type="file" id="avatarInput" />
const input = document.getElementById("avatarInput");
input.addEventListener("change", (e) => {
const dosya = e.target.files[0];
if (dosya) {
profilResmiYukle(dosya);
}
});⚠️ Dikkat:
FormDatagönderirkenContent-Typeheader'ını kendin ayarlama. Tarayıcı otomatik olarakmultipart/form-data; boundary=...şeklinde ayarlar. Kendin ayarlarsan boundary bilgisi eksik kalır ve sunucu veriyi parse edemez.
PUT ve DELETE İstekleri
// PUT — Kaynağı tamamen güncelle
async function kullaniciGuncelle(id, data) {
const response = await fetch(`/api/users/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`Güncelleme hatası: ${response.status}`);
}
return response.json();
}
// PATCH — Kaynağı kısmen güncelle
async function emailGuncelle(id, yeniEmail) {
const response = await fetch(`/api/users/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: yeniEmail }), // Sadece değişen alanı gönder
});
return response.json();
}
// DELETE — Kaynağı sil
async function kullaniciSil(id) {
const response = await fetch(`/api/users/${id}`, {
method: "DELETE",
});
if (!response.ok) {
throw new Error(`Silme hatası: ${response.status}`);
}
// 204 No Content → body yok
if (response.status === 204) {
console.log(`Kullanıcı #${id} silindi`);
return true;
}
return response.json(); // Bazı API'ler silinen kaydı döner
}Headers (Başlıklar)
HTTP headers, istek ve yanıt hakkında meta bilgi taşır. Fetch API'de Headers nesnesi ile yönetilir.
// Headers nesnesi oluşturma
const headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", "Bearer eyJhbGciOi...");
headers.append("X-Custom-Header", "özel değer");
// Veya obje olarak (daha yaygın)
const response = await fetch("/api/data", {
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer token123",
"Accept": "application/json",
"Accept-Language": "tr-TR",
},
});
// Yanıt header'larını okuma
console.log(response.headers.get("Content-Type"));
console.log(response.headers.get("X-Total-Count")); // Sayfalama bilgisi
// Tüm header'ları listeleme
for (const [key, value] of response.headers) {
console.log(`${key}: ${value}`);
}Yaygın Header'lar
// İstek header'ları
const commonHeaders = {
// Gönderilen verinin formatı
"Content-Type": "application/json",
// Kabul edilen yanıt formatı
"Accept": "application/json",
// Kimlik doğrulama
"Authorization": "Bearer <token>",
// Dil tercihi
"Accept-Language": "tr-TR,tr;q=0.9,en;q=0.8",
// CSRF token (form güvenliği)
"X-CSRF-Token": "abc123",
// Özel API anahtarı
"X-API-Key": "key_abc123",
};Request ve Response Nesneleri
Fetch API, Request ve Response sınıflarını kullanır. Bu nesneleri doğrudan oluşturup kullanabilirsin:
// Request nesnesi oluşturma
const request = new Request("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Ahmet" }),
});
// Request özellikleri
console.log(request.method); // "POST"
console.log(request.url); // "https://api.example.com/users"
console.log(request.headers); // Headers nesnesi
// fetch() ile kullanma
const response = await fetch(request);// Response nesnesi detayları
const response = await fetch("/api/data");
// Durum bilgisi
console.log(response.status); // 200
console.log(response.statusText); // "OK"
console.log(response.ok); // true (200-299 arası)
// URL ve yönlendirme bilgisi
console.log(response.url); // İsteğin gittiği son URL
console.log(response.redirected); // Yönlendirme oldu mu?
console.log(response.type); // "basic", "cors", "opaque"...
// Response klonlama (body'yi birden fazla kez okumak için)
const klon = response.clone();
const json = await response.json();
const text = await klon.text(); // Klondan farklı format okunabilirHata Yönetimi: fetch'in Tuzağı
Fetch API'nin en büyük tuzağı şudur: HTTP hata kodları (404, 500 vb.) reject edilmez! Fetch sadece ağ hatalarında (network error) reject olur.
// ⚠️ fetch 404'te bile reject OLMAZ!
try {
const response = await fetch("/api/olmayan-endpoint");
// response.status = 404 ama hata fırlatılmadı!
console.log(response.ok); // false
console.log(response.status); // 404
// catch'e DÜŞMEDİ çünkü fetch başarıyla tamamlandı
// (sunucu yanıt verdi — yanıtın 404 olması fetch için "başarı")
} catch (err) {
// Buraya sadece AĞ HATALARI düşer:
// - İnternet yok
// - DNS çözümlenemedi
// - Sunucu tamamen kapalı
// - CORS engeli
console.error("Ağ hatası:", err);
}Doğru Hata Yönetimi
// ✅ Kendi hata kontrolünü yaz
async function güvenliFetch(url, options = {}) {
const response = await fetch(url, options);
if (!response.ok) {
// HTTP hata kodlarını kendimiz yakalıyoruz
const hataMetni = await response.text();
throw new HttpError(response.status, response.statusText, hataMetni);
}
return response;
}
class HttpError extends Error {
constructor(status, statusText, body) {
super(`HTTP ${status}: ${statusText}`);
this.name = "HttpError";
this.status = status;
this.statusText = statusText;
this.body = body;
}
}
// Kullanım
try {
const response = await güvenliFetch("/api/users/999");
const data = await response.json();
} catch (err) {
if (err instanceof HttpError) {
if (err.status === 404) {
console.log("Kullanıcı bulunamadı");
} else if (err.status === 401) {
console.log("Oturum süresi dolmuş, giriş yapın");
} else {
console.error(`Sunucu hatası: ${err.status}`);
}
} else {
console.error("Ağ hatası:", err.message);
}
}Abort Controller: İstekleri İptal Etme
Bazen başlatılmış bir isteği iptal etmek gerekir: kullanıcı sayfadan ayrıldığında, yeni bir arama yapıldığında öncekini iptal etmek istediğinde...
// AbortController ile isteği iptal etme
const controller = new AbortController();
// fetch'e signal'ı ver
fetch("/api/buyuk-veri", { signal: controller.signal })
.then((r) => r.json())
.then((data) => console.log(data))
.catch((err) => {
if (err.name === "AbortError") {
console.log("İstek kullanıcı tarafından iptal edildi");
} else {
console.error("Gerçek hata:", err);
}
});
// 2 saniye sonra iptal et
setTimeout(() => {
controller.abort();
}, 2000);Arama Kutusunda Debounce + Abort
// Gerçek dünya: Arama kutusu — önceki isteği iptal et
let aktifController = null;
async function aramaYap(terim) {
// Önceki istek varsa iptal et
if (aktifController) {
aktifController.abort();
}
// Yeni controller oluştur
aktifController = new AbortController();
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(terim)}`, {
signal: aktifController.signal,
});
const sonuclar = await response.json();
gosterSonuclar(sonuclar);
} catch (err) {
if (err.name === "AbortError") {
// İptal edildi — sessizce geç
return;
}
console.error("Arama hatası:", err);
}
}
// Her tuş vuruşunda çağrılır
const aramaInput = document.getElementById("arama");
aramaInput.addEventListener("input", (e) => {
aramaYap(e.target.value);
});Timeout Mekanizması
// AbortController ile timeout
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
// Timeout zamanlayıcısı
const timeoutId = setTimeout(() => {
controller.abort();
}, timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId); // Başarılı — timeout'u iptal et
return response;
} catch (err) {
clearTimeout(timeoutId);
if (err.name === "AbortError") {
throw new Error(`İstek ${timeoutMs}ms içinde tamamlanamadı`);
}
throw err;
}
}
// Kullanım
try {
const response = await fetchWithTimeout("/api/slow-endpoint", 3000);
const data = await response.json();
} catch (err) {
console.error(err.message);
// "İstek 3000ms içinde tamamlanamadı"
}💡 İpucu: Modern tarayıcılarda
AbortSignal.timeout(ms)ile daha kısa timeout yazabilirsin: ``javascript const response = await fetch(url, { signal: AbortSignal.timeout(5000) });``
CORS (Cross-Origin Resource Sharing)
CORS, web güvenliğinin temel taşlarından biridir. Tarayıcı, bir sayfanın farklı bir origin'den (alan adı, port veya protokol) veri çekmesini varsayılan olarak engeller.
Origin Nedir?
https://example.com:443/api/users
└─┬──┘ └────┬─────┘└┬─┘└───┬───┘
protocol hostname port path
Origin = protocol + hostname + port// Aynı origin (izin verilir):
// https://example.com/page1 → https://example.com/api/data ✅
// Farklı origin (CORS gerekir):
// https://example.com → https://api.example.com ❌ (farklı hostname)
// https://example.com → http://example.com ❌ (farklı protocol)
// http://localhost:3000 → http://localhost:8080 ❌ (farklı port)CORS Hatası ve Çözümü
// CORS hatası — tarayıcı konsolu:
// "Access to fetch at 'https://api.other.com/data' from origin
// 'https://mysite.com' has been blocked by CORS policy"
// Bu hata TARAYICIDA oluşur — sunucu yanıtını verir ama
// tarayıcı "bu yanıtı kullanman yasak" der
// Çözüm 1: Sunucu tarafında CORS header'ları ekle
// (Node.js/Express örneği)
// app.use(cors({ origin: "https://mysite.com" }));
// veya sunucu yanıtına header ekle:
// Access-Control-Allow-Origin: https://mysite.com
// Çözüm 2: Proxy kullan (geliştirme ortamında)
// Vite/Webpack dev server proxy ayarı:
// "/api" → "https://api.other.com" yönlendirmesi
// Çözüm 3: mode: "no-cors" (sınırlı kullanım)
const response = await fetch("https://api.other.com/data", {
mode: "no-cors", // CORS hatasını bastırır AMA yanıtı OKUNAMAZ!
});
// response.type === "opaque" — body, status, headers okunamaz
// Sadece <img src>, <script src> gibi "ateşle ve unut" senaryoları içinPreflight İsteği
Bazı istekler (POST + JSON, özel header'lar, PUT, DELETE) gönderilmeden önce tarayıcı otomatik bir OPTIONS isteği gönderir:
// Tarayıcı otomatik olarak şunu yapar (görünmez):
// OPTIONS /api/users HTTP/1.1
// Origin: https://mysite.com
// Access-Control-Request-Method: POST
// Access-Control-Request-Headers: Content-Type, Authorization
// Sunucu yanıtı:
// Access-Control-Allow-Origin: https://mysite.com
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE
// Access-Control-Allow-Headers: Content-Type, Authorization
// Access-Control-Max-Age: 86400 (24 saat cache'le)
// Preflight başarılıysa, gerçek istek gönderilir
// Başarısızsa → CORS hatasıCredentials (Cookie, Auth)
// Varsayılan olarak cross-origin isteklerde cookie gönderilmez
// credentials ayarıyla kontrol edilir
// Aynı origin — cookie otomatik gönderilir
fetch("/api/profile"); // credentials: "same-origin" (varsayılan)
// Cross-origin — cookie göndermek için:
fetch("https://api.other.com/profile", {
credentials: "include", // Cookie'leri gönder
});
// Sunucu tarafında da izin vermeli:
// Access-Control-Allow-Credentials: true
// Access-Control-Allow-Origin: https://mysite.com (wildcard * OLAMAZ!)Interceptor Pattern: İstekleri Merkezi Yönetme
Büyük uygulamalarda her istekte token eklemek, hataları loglamak, loading state yönetmek gerekir. Bunun için wrapper fonksiyonları veya interceptor pattern kullanılır:
// Fetch wrapper — merkezi yönetim
class HttpClient {
constructor(baseUrl = "") {
this.baseUrl = baseUrl;
this.requestInterceptors = [];
this.responseInterceptors = [];
}
// Interceptor ekle
onRequest(fn) {
this.requestInterceptors.push(fn);
}
onResponse(fn) {
this.responseInterceptors.push(fn);
}
async request(endpoint, options = {}) {
let config = {
url: `${this.baseUrl}${endpoint}`,
headers: { "Content-Type": "application/json" },
...options,
};
// Request interceptor'ları çalıştır
for (const interceptor of this.requestInterceptors) {
config = await interceptor(config);
}
const { url, ...fetchOptions } = config;
let response = await fetch(url, fetchOptions);
// Response interceptor'ları çalıştır
for (const interceptor of this.responseInterceptors) {
response = await interceptor(response);
}
return response;
}
async get(endpoint) {
const response = await this.request(endpoint);
return response.json();
}
async post(endpoint, data) {
const response = await this.request(endpoint, {
method: "POST",
body: JSON.stringify(data),
});
return response.json();
}
}
// Kullanım
const http = new HttpClient("https://api.example.com");
// Her istekte token ekle
http.onRequest(async (config) => {
const token = localStorage.getItem("auth_token");
if (token) {
config.headers = {
...config.headers,
Authorization: `Bearer ${token}`,
};
}
return config;
});
// 401 gelirse otomatik logout
http.onResponse(async (response) => {
if (response.status === 401) {
localStorage.removeItem("auth_token");
window.location.href = "/login";
}
return response;
});
// Artık her istek otomatik token'lı
const user = await http.get("/users/me");Yaygın Hatalar ve Tuzaklar
Hata 1: response.ok Kontrolü Yapmamak
// ❌ 404 veya 500 geldiğinde bile "başarılı" gibi davranır
async function veriGetir() {
const response = await fetch("/api/data");
const data = await response.json(); // 404 sayfasının HTML'ini parse etmeye çalışır!
return data;
}
// ✅ Her zaman response.ok kontrol et
async function veriGetir() {
const response = await fetch("/api/data");
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}Hata 2: JSON Parse Hatası
// ❌ Sunucu JSON dönmeyebilir (HTML hata sayfası vb.)
async function veriGetir() {
const response = await fetch("/api/data");
const data = await response.json(); // SyntaxError: Unexpected token < in JSON
return data;
}
// ✅ Güvenli JSON parse
async function veriGetir() {
const response = await fetch("/api/data");
if (!response.ok) {
const text = await response.text();
throw new Error(`HTTP ${response.status}: ${text}`);
}
const contentType = response.headers.get("Content-Type");
if (contentType && contentType.includes("application/json")) {
return response.json();
}
// JSON değilse text olarak döndür
return response.text();
}Hata 3: Body'yi İki Kez Okumak
// ❌ Body stream zaten tüketilmiş
const response = await fetch("/api/data");
const text = await response.text();
const json = await response.json(); // TypeError: Body has already been consumed
// ✅ Klonla veya text'ten parse et
const response2 = await fetch("/api/data");
const clone = response2.clone();
const text2 = await response2.text(); // Orijinalden text
const json2 = await clone.json(); // Klondan JSONGerçek Dünya Örneği: CRUD Uygulaması
Tüm HTTP metotlarını birleştiren kapsamlı bir örnek:
// Tam özellikli CRUD servisi
class TodoService {
constructor() {
this.baseUrl = "https://jsonplaceholder.typicode.com/todos";
}
// Hata kontrolü ile fetch
async request(url, options = {}) {
try {
const response = await fetch(url, {
headers: { "Content-Type": "application/json" },
...options,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
if (response.status === 204) return null;
return response.json();
} catch (err) {
if (err.name === "TypeError") {
throw new Error("Ağ bağlantısı yok");
}
throw err;
}
}
// CREATE
async ekle(todo) {
return this.request(this.baseUrl, {
method: "POST",
body: JSON.stringify(todo),
});
}
// READ
async hepsiniGetir() {
return this.request(this.baseUrl);
}
async getir(id) {
return this.request(`${this.baseUrl}/${id}`);
}
// UPDATE
async guncelle(id, data) {
return this.request(`${this.baseUrl}/${id}`, {
method: "PUT",
body: JSON.stringify(data),
});
}
async tamamla(id) {
return this.request(`${this.baseUrl}/${id}`, {
method: "PATCH",
body: JSON.stringify({ completed: true }),
});
}
// DELETE
async sil(id) {
return this.request(`${this.baseUrl}/${id}`, {
method: "DELETE",
});
}
// Toplu işlem — paralel
async topluSil(idler) {
const sonuclar = await Promise.allSettled(
idler.map((id) => this.sil(id))
);
const basarili = sonuclar.filter((s) => s.status === "fulfilled").length;
const hatali = sonuclar.filter((s) => s.status === "rejected").length;
console.log(`Toplu silme: ${basarili} başarılı, ${hatali} hatalı`);
return { basarili, hatali };
}
}
// Kullanım
async function main() {
const todoService = new TodoService();
// Tüm todo'ları getir
const todolar = await todoService.hepsiniGetir();
console.log(`${todolar.length} todo bulundu`);
// Yeni todo ekle
const yeni = await todoService.ekle({
title: "Fetch API dersini bitir",
completed: false,
userId: 1,
});
console.log("Eklendi:", yeni);
// Todo güncelle
const guncellenmis = await todoService.tamamla(1);
console.log("Tamamlandı:", guncellenmis);
// Toplu sil
await todoService.topluSil([1, 2, 3]);
}
main();Retry Pattern: Başarısız İstekleri Tekrar Deneme
Ağ istekleri bazen geçici nedenlerle başarısız olur. Otomatik yeniden deneme mekanizması eklemek dayanıklı (resilient) uygulamalar için önemlidir:
// Üstel geri çekilme (exponential backoff) ile retry
async function fetchWithRetry(url, options = {}, maxRetry = 3) {
for (let deneme = 0; deneme <= maxRetry; deneme++) {
try {
const response = await fetch(url, options);
// 5xx hataları tekrar denenebilir, 4xx hataları denemez
if (response.ok) return response;
if (response.status >= 500 && deneme < maxRetry) {
console.warn(`Sunucu hatası (${response.status}), tekrar deneniyor...`);
// Üstel bekleme: 1s, 2s, 4s, 8s...
await new Promise((r) => setTimeout(r, Math.pow(2, deneme) * 1000));
continue;
}
throw new Error(`HTTP ${response.status}`);
} catch (err) {
if (err.name === "TypeError" && deneme < maxRetry) {
// Ağ hatası — tekrar dene
console.warn(`Ağ hatası, ${deneme + 1}. tekrar deneme...`);
await new Promise((r) => setTimeout(r, Math.pow(2, deneme) * 1000));
continue;
}
throw err;
}
}
}
// Kullanım
const response = await fetchWithRetry("/api/important-data", {}, 3);
const data = await response.json();Özet
Bu derste Fetch API ile HTTP iletişiminin tüm yönlerini öğrendik:
`fetch()` Promise tabanlı, modern HTTP istemcisidir. Varsayılan metot GET'tir.
HTTP hata kodları (404, 500) fetch'i reject etmez —
response.okkontrolü her zaman yapılmalıdır.POST/PUT/DELETE isteklerinde
method,headersvebodyayarlanmalı, JSON verilerdeJSON.stringify()kullanılmalıdır.AbortController ile devam eden istekler iptal edilebilir — arama kutusu, sayfa değişimi gibi senaryolarda kritiktir.
CORS tarayıcı güvenlik mekanizmasıdır — cross-origin istekler sunucu tarafında izin verilmelidir.
Retry pattern ile geçici hatalar otomatik olarak yeniden denenebilir — üstel geri çekilme (exponential backoff) kullanılmalıdır.
Bu derste asenkron JavaScript bölümünü tamamladık! Bir sonraki bölümde modern ES6+ özelliklerini keşfedeceğiz.
AI Asistan
Sorularını yanıtlamaya hazır