18-bo‘lim

Fetch va API

fetch bilan so'rov yuborish, JSON o'qish, POST va sarlavhalar, xatolarni to'g'ri qayta ishlash, bekor qilish va CORS.

🕑 17 daqiqa o‘qish 📄 740 so‘z 👁 2 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Sinov serveri
  2. Birinchi so'rov
  3. fetch xato holatlarda yiqilmaydi
  4. O'ralgan so'rov funksiyasi
  5. POST so'rovi
  6. HTTP metodlari
  7. So'rovni bekor qilish
  8. Parallel so'rovlar
  9. So'rov sarlavhalari
  10. CORS haqida
  11. Xulosa

Zamonaviy ilova ma'lumotni serverdan oladi. fetch - buning standart vositasi.

Sinov serveri #

Misollarni tekshirish uchun Node ning o'zida kichik server ishlatamiz. Haqiqiy loyihada bu server alohida mashinada turadi.

JavaScript
const http = require("node:http");

const server = http.createServer((sorov, javob) => {
    const url = new URL(sorov.url, "http://localhost");

    if (url.pathname === "/talabalar") {
        javob.writeHead(200, { "Content-Type": "application/json" });
        javob.end(JSON.stringify([
            { id: 1, ism: "Husanboy", ball: 92 },
            { id: 2, ism: "Malika", ball: 78 }
        ]));
        return;
    }

    if (url.pathname === "/qoshish" && sorov.method === "POST") {
        let tana = "";
        sorov.on("data", (bolak) => { tana += bolak; });
        sorov.on("end", () => {
            const kelgan = JSON.parse(tana);
            javob.writeHead(201, { "Content-Type": "application/json" });
            javob.end(JSON.stringify({ id: 3, ...kelgan }));
        });
        return;
    }

    if (url.pathname === "/sekin") {
        setTimeout(() => {
            javob.writeHead(200, { "Content-Type": "application/json" });
            javob.end('{"holat":"kech"}');
        }, 200);
        return;
    }

    javob.writeHead(404, { "Content-Type": "application/json" });
    javob.end('{"xato":"Topilmadi"}');
});

const tayyor = new Promise((b) => server.listen(0, () => b()));
const manzil = async () => {
    await tayyor;
    return `http://localhost:${server.address().port}`;
};

Birinchi so'rov #

JavaScript
(async () => {
    const asos = await manzil();

    const javob = await fetch(`${asos}/talabalar`);

    console.log("Holat kodi :", javob.status);
    console.log("ok         :", javob.ok);
    console.log("Content-Type:", javob.headers.get("content-type"));

    const malumot = await javob.json();

    console.log("Turi       :", Array.isArray(malumot) ? "massiv" : "obyekt");
    console.log("Elementlar :", malumot.length);
    console.log("Birinchi   :", malumot[0].ism);

    server.close();
})();
Natija
Holat kodi : 200
ok         : true
Content-Type: application/json
Turi       : massiv
Elementlar : 2
Birinchi   : Husanboy
fetch ikki bosqichli
JavaScript
const javob = await fetch(url);      // 1. sarlavhalar keldi
const malumot = await javob.json();  // 2. tanani o'qidik

Birinchi await faqat sarlavhalarni kutadi. Tana hali yuklanmagan bo'lishi mumkin - shuning uchun ikkinchi await kerak.

Tanani o'qish metodlari:

MetodNima qaytaradi
.json()JSON tahlil qilingan obyekt
.text()Xom matn
.blob()Ikkilik ma'lumot (rasm, fayl)
.formData()Forma ma'lumoti
.arrayBuffer()Bayt massivi

Tanani faqat bir marta o'qish mumkin:

JavaScript
const a = await javob.json();
const b = await javob.json();      // TypeError: body used already

Ikki marta kerak bo'lsa, javob.clone() qiling.

fetch xato holatlarda yiqilmaydi #

JavaScript
(async () => {
    const asos = await manzil();

    const javob = await fetch(`${asos}/mavjud-emas`);

    console.log("Xato bo'ldimi? :", javob.ok);
    console.log("Holat kodi     :", javob.status);
    console.log("Holat matni    :", javob.statusText);

    // fetch YIQILMADI - biz o'zimiz tekshirishimiz kerak
    const tana = await javob.json();
    console.log("Xato tanasi    :", tana.xato);

    server.close();
})();
Natija
Xato bo'ldimi? : false
Holat kodi     : 404
Holat matni    : Not Found
Xato tanasi    : Topilmadi
fetch 404 va 500 da xato tashlamaydi

Bu fetch ning eng ko'p chalkashtiriladigan xususiyati:

JavaScript
try {
    const javob = await fetch("/mavjud-emas");
    const malumot = await javob.json();     // 404 bo'lsa ham bu yerga keladi
} catch (xato) {
    // BU ISHLAMAYDI - 404 xato hisoblanmaydi
}

fetch faqat tarmoq darajasidagi muammoda yiqiladi:

  • Internet yo'q;
  • DNS ishlamadi;
  • CORS taqiqladi;
  • So'rov bekor qilindi.

HTTP holat kodi (404, 500) - bu muvaffaqiyatli javob, shunchaki mazmuni xato haqida.

To'g'ri naqsh - ok ni tekshiring:

JavaScript
const javob = await fetch(url);

if (!javob.ok) {
    throw new Error(`HTTP ${javob.status}: ${javob.statusText}`);
}

const malumot = await javob.json();

ok xossasi status 200-299 oralig'ida bo'lsa true bo'ladi.

O'ralgan so'rov funksiyasi #

JavaScript
async function soragan(url, sozlama = {}) {
    const javob = await fetch(url, sozlama);

    if (!javob.ok) {
        let tafsilot = "";
        try {
            const tana = await javob.json();
            tafsilot = tana.xato ?? "";
        } catch {
            tafsilot = await javob.text();
        }

        const xato = new Error(`HTTP ${javob.status}: ${tafsilot}`);
        xato.status = javob.status;
        throw xato;
    }

    return javob.json();
}

(async () => {
    const asos = await manzil();

    const talabalar = await soragan(`${asos}/talabalar`);
    console.log("Muvaffaqiyat:", talabalar.length, "ta talaba");

    try {
        await soragan(`${asos}/mavjud-emas`);
    } catch (xato) {
        console.log("Xato ushlandi:", xato.message);
        console.log("Status xossasi:", xato.status);
    }

    server.close();
})();
Natija
Muvaffaqiyat: 2 ta talaba
Xato ushlandi: HTTP 404: Topilmadi
Status xossasi: 404

POST so'rovi #

JavaScript
(async () => {
    const asos = await manzil();

    const javob = await fetch(`${asos}/qoshish`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({ ism: "Nodira", ball: 85 })
    });

    console.log("Holat kodi:", javob.status);

    const yaratilgan = await javob.json();
    console.log("Yaratilgan id:", yaratilgan.id);
    console.log("Ism          :", yaratilgan.ism);
    console.log("Ball         :", yaratilgan.ball);

    server.close();
})();
Natija
Holat kodi: 201
Yaratilgan id: 3
Ism          : Nodira
Ball         : 85
body ni JSON.stringify qilishni unutmang
JavaScript
// NOTO'G'RI - obyekt "[object Object]" ga aylanadi
body: { ism: "Nodira" }

// TO'G'RI
body: JSON.stringify({ ism: "Nodira" })

Content-Type sarlavhasini ham berish kerak - aks holda server tanani JSON deb tanimasligi mumkin.

Forma yuborayotgan bo'lsangiz, FormData ni to'g'ridan-to'g'ri bering:

JavaScript
fetch(url, {
    method: "POST",
    body: new FormData(forma)      // stringify KERAK EMAS
});

Bu holda Content-Type ni o'zingiz qo'ymang - brauzer uni boundary bilan birga avtomatik qo'yadi. Qo'lda qo'ysangiz so'rov buziladi.

YuborilayotganContent-Typebody
JSONapplication/jsonJSON.stringify(obyekt)
Formaqo'ymangnew FormData(forma)
Matntext/plainsatr

HTTP metodlari #

MetodVazifasiTanasi bormi
GETO'qishYo'q
POSTYangi yaratishHa
PUTTo'liq almashtirishHa
PATCHQisman o'zgartirishHa
DELETEO'chirishOdatda yo'q
JavaScript
await fetch(url, { method: "DELETE" });

await fetch(url, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ ball: 95 })
});

So'rovni bekor qilish #

JavaScript
(async () => {
    const asos = await manzil();
    const boshqaruv = new AbortController();

    // 50 ms dan keyin bekor qilamiz (server 200 ms kutadi)
    setTimeout(() => boshqaruv.abort(), 50);

    try {
        await fetch(`${asos}/sekin`, { signal: boshqaruv.signal });
        console.log("Bu chiqmaydi");
    } catch (xato) {
        console.log("Xato nomi :", xato.name);
        console.log("Bekor qilindimi?:", xato.name === "AbortError");
    }

    // Zamonaviy qisqartma
    try {
        await fetch(`${asos}/sekin`, { signal: AbortSignal.timeout(50) });
    } catch (xato) {
        console.log("timeout xatosi  :", xato.name);
    }

    server.close();
})();
Natija
Xato nomi : AbortError
Bekor qilindimi?: true
timeout xatosi  : TimeoutError
Bekor qilish - jonli qidiruvda majburiy

Foydalanuvchi tez yozayotganda eski so'rovlar keraksiz bo'lib qoladi. Undan ham yomoni - ular kech kelib, yangi natijani bosib o'tishi mumkin.

JavaScript
let joriyBoshqaruv = null;

maydon.addEventListener("input", async () => {
    joriyBoshqaruv?.abort();               // eskisini bekor qilamiz
    joriyBoshqaruv = new AbortController();

    try {
        const javob = await fetch(`/qidiruv?q=${maydon.value}`, {
            signal: joriyBoshqaruv.signal
        });
        korsatish(await javob.json());
    } catch (xato) {
        if (xato.name !== "AbortError") {
            throw xato;                    // haqiqiy xato
        }
    }
});

AbortError ni e'tiborsiz qoldiring - u kutilgan holat, xato emas.

15-bo'limdagi debounce bilan birga ishlatilsa eng yaxshi natija beradi: debounce so'rovlar sonini kamaytiradi, abort esa eskilarini to'xtatadi.

Parallel so'rovlar #

JavaScript
(async () => {
    const asos = await manzil();

    const boshi = Date.now();

    const [birinchi, ikkinchi, uchinchi] = await Promise.all([
        fetch(`${asos}/talabalar`).then((j) => j.json()),
        fetch(`${asos}/talabalar`).then((j) => j.json()),
        fetch(`${asos}/talabalar`).then((j) => j.json())
    ]);

    const vaqt = Date.now() - boshi;

    console.log("Uchala so'rov keldimi?:",
        birinchi.length === 2 && ikkinchi.length === 2 && uchinchi.length === 2);
    console.log("Parallel ishladimi?   :", vaqt < 500);

    server.close();
})();
Natija
Uchala so'rov keldimi?: true
Parallel ishladimi?   : true

So'rov sarlavhalari #

JavaScript
(async () => {
    const asos = await manzil();

    const javob = await fetch(`${asos}/talabalar`, {
        headers: {
            "Accept": "application/json",
            "X-Ilova-Versiyasi": "1.0"
        }
    });

    console.log("Javob keldi:", javob.ok);

    // Javob sarlavhalarini o'qish
    console.log("content-type:", javob.headers.get("content-type"));
    console.log("Yo'q sarlavha:", javob.headers.get("x-yoq"));
    console.log("has() bilan  :", javob.headers.has("content-type"));

    server.close();
})();
Natija
Javob keldi: true
content-type: application/json
Yo'q sarlavha: null
has() bilan  : true
Maxfiy kalitni brauzer kodiga yozmang
JavaScript
// XAVFLI - kalit hammaga ko'rinadi
fetch("https://api.example.com/data", {
    headers: { "Authorization": "Bearer sk_live_maxfiy_kalit" }
});

Brauzerdagi hamma narsa ko'rinadi:

  • F12 → Network yorlig'ida so'rov;
  • Manba kodida qidirish;
  • Qurilgan (build) fayllarda ham.

Kalitni "yashirish" mumkin emas - .env fayl ham yordam bermaydi, chunki u qurish paytida kodga kiritiladi.

To'g'ri yechim - o'z serveringiz orqali:

Natija
Brauzer  ->  Sizning serveringiz  ->  Tashqi API
             (kalit shu yerda)

Brauzer faqat sizning serveringizga murojaat qiladi, kalit esa serverda qoladi.

Foydalanuvchi tokenlari (sessiya) uchun esa HttpOnly cookie ishlating - JavaScript ularni o'qiy olmaydi:

JavaScript
fetch(url, { credentials: "include" });   // cookie avtomatik ketadi

CORS haqida #

JavaScript
(async () => {
    const asos = await manzil();

    // Node da CORS cheklovi yo'q - u brauzer mexanizmi
    const javob = await fetch(`${asos}/talabalar`);
    console.log("Node da so'rov o'tdi:", javob.ok);

    console.log("Brauzerda esa server ruxsat berishi kerak:");
    console.log("  Access-Control-Allow-Origin sarlavhasi");

    server.close();
})();
Natija
Node da so'rov o'tdi: true
Brauzerda esa server ruxsat berishi kerak:
  Access-Control-Allow-Origin sarlavhasi
CORS - brauzer himoyasi, serverniki emas

CORS (Cross-Origin Resource Sharing) xatosi shunday ko'rinadi:

Natija
Access to fetch at 'https://api.example.com' from origin
'https://saytim.uz' has been blocked by CORS policy

Muhim tushunish: so'rov aslida yuborilgan va server javob bergan. Brauzer shunchaki javobni sizning kodingizga bermayapti.

CORS ni faqat server hal qila oladi:

Natija
Access-Control-Allow-Origin: https://saytim.uz

Nima yordam bermaydi:

  • Brauzer sozlamalarini o'zgartirish (foydalanuvchida ishlamaydi);
  • mode: "no-cors" - javobni o'qib bo'lmaydi;
  • Frontend kodini o'zgartirish.

Nima yordam beradi:

  • Serverda sarlavha qo'shish (agar server sizniki bo'lsa);
  • O'z serveringiz orqali proksi qilish;
  • Ishlab chiqish uchun dev-server proksisi.

Bir xil origin degani - protokol, domen va port uchalasi ham bir xil bo'lishi:

URLhttps://saytim.uz ga nisbatan
https://saytim.uz/apiBir xil
http://saytim.uzBoshqa (protokol)
https://api.saytim.uzBoshqa (subdomen)
https://saytim.uz:8080Boshqa (port)
Amaliy topshiriq
  1. fetch bilan JSON o'qing va status ni tekshiring.
  2. Tanani ikki marta o'qishga urinib, xatoni ko'ring.
  3. 404 da fetch yiqilmasligini isbotlang.
  4. ok ni tekshiradigan o'ralgan funksiya yozing.
  5. POST bilan JSON yuboring.
  6. FormData yuborishda Content-Type qo'ymaslik sababini ayting.
  7. AbortController bilan so'rovni bekor qiling.
  8. AbortSignal.timeout bilan vaqt chegarasi qo'ying.
  9. Promise.all bilan uch so'rovni parallel yuboring.
  10. CORS nima uchun faqat serverda hal qilinishini tushuntiring.

Xulosa #

  • fetch ikki bosqichli: sarlavhalar, keyin tana.
  • Tanani faqat bir marta o'qish mumkin.
  • fetch 404 va 500 da yiqilmaydi - ok ni tekshiring.
  • U faqat tarmoq darajasidagi muammoda xato tashlaydi.
  • So'rovni o'ralgan funksiyaga joylang - takrorlanish kamayadi.
  • body uchun JSON.stringify qiling.
  • FormData yuborganda Content-Type qo'ymang.
  • AbortController bilan keraksiz so'rovni bekor qiling.
  • AbortError - kutilgan holat, uni e'tiborsiz qoldiring.
  • Maxfiy kalitni brauzer kodiga yozmang.
  • CORS - brauzer mexanizmi, faqat server hal qiladi.

Keyingi bo'limda modullar va zamonaviy JavaScript imkoniyatlarini ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.