Məzmuna keç
Educora
İrəli24 dəq24 / 27

fetch və REST API-lər

Serverlərlə danış: HTTP necə işləyir, REST resursları və metodları, JSON ilə `fetch`, status kodları, xətaların emalı, `AbortController` ilə vaxt limiti və paralel sorğular.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • HTTP sorğusunu və cavabını izah etmək: metod, URL, başlıqlar, gövdə, status kodu
  • fetch ilə GET və POST sorğuları göndərmək, JSON oxumaq, HTTP və şəbəkə xətalarını düzgün emal etmək
  • Etibarlı asinxron şablonlar qurmaq: vaxt limiti, təkrar cəhd, paralel sorğular və URLSearchParams

Tətbiqdəki hava proqnozu, valyuta məzənnələri, sosial şəbəkədəki lent — hamısı serverlərdən API vasitəsilə gəlir. Sənin kodun HTTP sorğusu göndərir, server isə verilənlərlə, adətən JSON ilə cavab verir. fetch bunun üçün brauzerlərdə və Node.js-də (18-ci versiyadan) hazır olan müasir alətdir.

Bir dəqiqədə HTTP

Sorğu metoddan, URL-dən, başlıqlardan (headers) və bəzən gövdədən (body) ibarətdir. Cavabda status kodu, başlıqlar və gövdə olur. Status kodunun birinci rəqəmi mənanı verir: 2xx — uğur, 3xx — yönləndirmə, 4xx — müştərinin xətası (səhv sorğu), 5xx — serverin xətası.

Text
POST /api/orders HTTP/1.1
Host: shop.example.com
Content-Type: application/json
Authorization: Bearer <token>

{"productId": 7, "quantity": 2}

HTTP/1.1 201 Created
Content-Type: application/json

{"id": 1043, "productId": 7, "quantity": 2, "status": "new"}
Yuxarıda sorğu, aşağıda serverin cavabı. Başlıqlarla gövdəni boş sətir ayırır.
Tərif
REST

API qurmaq üslubu: verilənlər öz URL-i olan resurslardır (/users, /users/42), əməliyyatlar isə HTTP metodları ilə ifadə olunur. Hər sorğu müstəqildir (server əvvəlki sorğunu «xatırlamır»), verilənlər adətən JSON kimi ötürülür.

MetodMənasıNümunə və uğurlu cavab
GEToxumaqGET /users/42 → 200 OK
POSTyaratmaqPOST /orders → 201 Created
PUT / PATCHtam / qismən yeniləməkPATCH /users/42 → 200 OK
DELETEsilməkDELETE /orders/1043 → 204 No Content
Tez-tez görəcəyin xəta kodları: 400 (səhv verilənlər), 401 (giriş lazımdır), 403 (icazə yoxdur), 404 (tapılmadı), 429 (çox sorğu), 500 və 503 (server problemi).

fetch ilə GET

fetch(url) Response obyekti ilə yerinə yetirilən Promise qaytarır. İş iki addımdır: əvvəl cavabı gözləyirsən (başlıqlar gəlir), sonra gövdəni response.json() ilə oxuyursan — bu da Promise-dir. Vacib məqam: fetch yalnız şəbəkə xətasında (internet yoxdur, domen tapılmır, CORS) rədd olunur. 404 və 500 cavabları da uğurla «gəlir», ona görə response.ok-u (status 200–299) həmişə yoxla.

JavaScript
async function getUser(id) {
  const response = await fetch(`https://api.example.com/users/${id}`);
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.json();
}

try {
  const user = await getUser(42);
  console.log(user.name, user.city);
  await getUser(999999);
} catch (error) {
  console.log('Request failed:', error.message);
}
Gözlənilən nəticə
Aysel Baku
Request failed: HTTP 404
api.example.com şərti ünvandır: real API-də sahələrin adları fərqli olacaq. Bu səhifədəki icraçı real sorğu göndərmir, ona görə aşağıda serveri təqlid edəcəyik.

Response obyektini JavaScript-in özündə yaratmaq olar. Bu, serveri təqlid etməyə və internetsiz məşq etməyə imkan verir: fakeFetch real fetch kimi Response qaytarır, sənin kodun isə fərqi hiss etmir. Testlərdə də məhz belə edirlər.

JavaScript
const db = { 42: { id: 42, name: 'Aysel', city: 'Baku' } };

async function fakeFetch(url) {
  await new Promise((resolve) => setTimeout(resolve, 50));
  const id = Number(url.split('/').pop());
  const user = db[id];
  return new Response(JSON.stringify(user ?? { error: 'Not found' }), {
    status: user ? 200 : 404,
    headers: { 'Content-Type': 'application/json' },
  });
}

for (const id of [42, 7]) {
  const response = await fakeFetch(`/api/users/${id}`);
  console.log(response.status, response.ok, response.headers.get('content-type'));
  console.log(await response.json());
}
▸ Gözlənilən nəticə
200 true application/json
{ id: 42, name: 'Aysel', city: 'Baku' }
404 false application/json
{ error: 'Not found' }

POST, başlıqlar və xətalar

Verilən göndərmək üçün fetch-ə ikinci arqument kimi seçimlər obyekti verilir: method, headers və body. Gövdə mətn olmalıdır, ona görə obyekt JSON.stringify ilə sətrə çevrilir, Content-Type: application/json başlığı isə serverə formatı bildirir. Giriş tələb edən API-lər adətən Authorization: Bearer <token> başlığını gözləyir.

JavaScript
async function createOrder(order, token) {
  const response = await fetch('https://api.example.com/orders', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify(order),
  });

  if (response.status === 401) throw new Error('Please log in again');
  if (!response.ok) throw new Error(`Server error ${response.status}`);
  return response.json();
}

const saved = await createOrder({ productId: 7, quantity: 2 }, token);
console.log('Created order', saved.id, saved.status);
Gözlənilən nəticə
Created order 1043 new

Etibarlı asinxron şablonlar

Real şəbəkə yavaş və etibarsızdır. Dörd vərdiş kömək edir: sorğu parametrlərini URL və URLSearchParams ilə qur (xüsusi simvollar avtomatik kodlaşdırılır); asılı olmayan sorğuları Promise.all ilə paralel göndər; vaxt limiti qoy — fetch(url, { signal: AbortSignal.timeout(8000) }) 8 saniyədən sonra sorğunu TimeoutError ilə ləğv edir; müvəqqəti xətalarda (5xx, 429) fasiləni artıraraq təkrar cəhd et.

JavaScript
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'plov & dolma');
url.searchParams.set('city', 'Baku');
url.searchParams.set('page', 2);

console.log(url.toString());
console.log(url.searchParams.get('q'));
console.log(Object.fromEntries(url.searchParams));
▸ Gözlənilən nəticə
https://api.example.com/search?q=plov+%26+dolma&city=Baku&page=2
plov & dolma
{ q: 'plov & dolma', city: 'Baku', page: '2' }
Əl ilə yapışdırılmış ?q=plov & dolma sorğunu pozardı: & yeni parametr kimi başa düşülərdi. URLSearchParams onu %26 kimi kodlaşdırır.
JavaScript
function slowServer(ms, signal) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => resolve('data'), ms);
    signal.addEventListener('abort', () => {
      clearTimeout(timer);
      reject(new DOMException('Request took too long', 'TimeoutError'));
    });
  });
}

async function withTimeout(ms, limit) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), limit);
  try {
    return await slowServer(ms, controller.signal);
  } catch (error) {
    return `failed: ${error.name}`;
  } finally {
    clearTimeout(timer);
  }
}

console.log(await withTimeout(50, 200));
console.log(await withTimeout(500, 200));
▸ Gözlənilən nəticə
data
failed: TimeoutError
AbortController ləğv siqnalı yaradır; real fetch bu signal-ı qəbul edir. AbortSignal.timeout(ms) eyni işi bir sətirdə görür.
Tapşırıq

getJSON(url) köməkçisini yaz: o, fakeFetch(url)-i çağırsın, cavab uğurlu deyilsə, HTTP <status> mesajlı xəta atsın, uğurludursa, JSON-u qaytarsın.

Tapşırıq · JavaScript
const products = { 1: { id: 1, title: 'Notebook', price: 5 } };

async function fakeFetch(url) {
  const id = Number(url.split('/').pop());
  const product = products[id];
  return new Response(JSON.stringify(product ?? { error: 'Not found' }), {
    status: product ? 200 : 404,
    headers: { 'Content-Type': 'application/json' },
  });
}

async function getJSON(url) {
  // call fakeFetch, check response.ok, return the JSON
}

for (const url of ['/api/products/1', '/api/products/99']) {
  try {
    console.log(await getJSON(url));
  } catch (error) {
    console.log('Error:', error.message);
  }
}
▸ Gözlənilən nəticə
{ id: 1, title: 'Notebook', price: 5 }
Error: HTTP 404
Tapşırıq

flakyFetch ilk iki dəfə 503 qaytarır. fetchWithRetry(url, retries) funksiyasını yaz: uğursuz hər cəhddə Attempt N: 503 çap etsin, 5xx xətasında 100, sonra 200 ms (hər dəfə iki qat) gözləyib yenidən cəhd etsin, retries təkrar bitəndə və ya 4xx-də isə HTTP <status> xətası atsın.

Tapşırıq · JavaScript
let calls = 0;
async function flakyFetch(url) {
  calls++;
  const ok = calls >= 3;
  return new Response(JSON.stringify(ok ? { rates: { USD: 1.7 } } : {}), { status: ok ? 200 : 503 });
}

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function fetchWithRetry(url, retries) {
  // only one attempt for now: add retries with a growing delay
  const response = await flakyFetch(url);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}

const data = await fetchWithRetry('/api/rates', 3);
console.log('Rates:', data.rates);
▸ Gözlənilən nəticə
Attempt 1: 503
Attempt 2: 503
Rates: { USD: 1.7 }

Əsas fikirlər

  • HTTP: sorğu = metod + URL + başlıqlar + gövdə; cavab = status + başlıqlar + gövdə. 2xx uğur, 4xx müştəri xətası, 5xx server xətası.
  • REST: resurslar URL-lərdədir, əməliyyatlar metodlarla: GET oxuyur, POST yaradır, PUT/PATCH yeniləyir, DELETE silir.
  • fetch yalnız şəbəkə xətasında rədd olunur — həmişə response.ok-u yoxla, gövdəni await response.json() ilə bir dəfə oxu.
  • POST üçün method, Content-Type: application/json başlığı və body: JSON.stringify(data) göndər.
  • Etibarlılıq: vaxt limiti (AbortSignal.timeout), müvəqqəti 5xx xətalarında təkrar cəhd, paralel sorğular üçün Promise.all, parametrlər üçün URLSearchParams.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
404 qaytaran ünvana fetch göndərilir. Nə baş verir?