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.
Ushbu bo‘lim mundarijasi
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.
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 #
(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();
})();
Holat kodi : 200
ok : true
Content-Type: application/json
Turi : massiv
Elementlar : 2
Birinchi : Husanboy
fetch ikki bosqichliconst 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:
| Metod | Nima 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:
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 #
(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();
})();
Xato bo'ldimi? : false
Holat kodi : 404
Holat matni : Not Found
Xato tanasi : Topilmadi
fetch 404 va 500 da xato tashlamaydiBu fetch ning eng ko'p chalkashtiriladigan xususiyati:
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:
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 #
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();
})();
Muvaffaqiyat: 2 ta talaba
Xato ushlandi: HTTP 404: Topilmadi
Status xossasi: 404
POST so'rovi #
(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();
})();
Holat kodi: 201
Yaratilgan id: 3
Ism : Nodira
Ball : 85
body ni JSON.stringify qilishni unutmang// 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:
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.
| Yuborilayotgan | Content-Type | body |
|---|---|---|
| JSON | application/json | JSON.stringify(obyekt) |
| Forma | qo'ymang | new FormData(forma) |
| Matn | text/plain | satr |
HTTP metodlari #
| Metod | Vazifasi | Tanasi bormi |
|---|---|---|
GET | O'qish | Yo'q |
POST | Yangi yaratish | Ha |
PUT | To'liq almashtirish | Ha |
PATCH | Qisman o'zgartirish | Ha |
DELETE | O'chirish | Odatda yo'q |
await fetch(url, { method: "DELETE" });
await fetch(url, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ball: 95 })
});
So'rovni bekor qilish #
(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();
})();
Xato nomi : AbortError
Bekor qilindimi?: true
timeout xatosi : TimeoutError
Foydalanuvchi tez yozayotganda eski so'rovlar keraksiz bo'lib qoladi. Undan ham yomoni - ular kech kelib, yangi natijani bosib o'tishi mumkin.
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 #
(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();
})();
Uchala so'rov keldimi?: true
Parallel ishladimi? : true
So'rov sarlavhalari #
(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();
})();
Javob keldi: true
content-type: application/json
Yo'q sarlavha: null
has() bilan : true
// 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:
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:
fetch(url, { credentials: "include" }); // cookie avtomatik ketadi
CORS haqida #
(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();
})();
Node da so'rov o'tdi: true
Brauzerda esa server ruxsat berishi kerak:
Access-Control-Allow-Origin sarlavhasi
CORS (Cross-Origin Resource Sharing) xatosi shunday ko'rinadi:
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:
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:
| URL | https://saytim.uz ga nisbatan |
|---|---|
https://saytim.uz/api | Bir xil |
http://saytim.uz | Boshqa (protokol) |
https://api.saytim.uz | Boshqa (subdomen) |
https://saytim.uz:8080 | Boshqa (port) |
fetchbilan JSON o'qing vastatusni tekshiring.- Tanani ikki marta o'qishga urinib, xatoni ko'ring.
- 404 da
fetchyiqilmasligini isbotlang. okni tekshiradigan o'ralgan funksiya yozing.- POST bilan JSON yuboring.
FormDatayuborishdaContent-Typeqo'ymaslik sababini ayting.AbortControllerbilan so'rovni bekor qiling.AbortSignal.timeoutbilan vaqt chegarasi qo'ying.Promise.allbilan uch so'rovni parallel yuboring.- CORS nima uchun faqat serverda hal qilinishini tushuntiring.
Xulosa #
fetchikki bosqichli: sarlavhalar, keyin tana.- Tanani faqat bir marta o'qish mumkin.
fetch404 va 500 da yiqilmaydi -okni tekshiring.- U faqat tarmoq darajasidagi muammoda xato tashlaydi.
- So'rovni o'ralgan funksiyaga joylang - takrorlanish kamayadi.
bodyuchunJSON.stringifyqiling.FormDatayuborgandaContent-Typeqo'ymang.AbortControllerbilan 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.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.