Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Async Server Component
- 2.1 Eng oddiy shakl
- 2.2 fetch faqat bitta yo'l
- 2.3 fetch Next 16 da keshlanmaydi
- 3. Waterfall va Promise.all
- 3.1 Ikki so'rov — ikki yo'l
- 3.2 O'zingiz o'lchab ko'ring
- 3.3 Qachon ketma-ket bo'lishi shart
- 3.4 Komponentlar orasidagi waterfall
- 3.5 Preload naqshi
- 4. Kanon kodi: github.ts
- 4.1 Butun fayl
- 4.2 Bitta so'rov: ol()
- 4.3 Parallel va tekshiruv
- 5. Xatolarni boshqarish
- 5.1 Ikki strategiya
- 5.2 Natija oynasida: uch holat
- 5.3 Biz topdik: try/catch Next.js signalini yutdi
- 6. O'z backend'ingizni chaqirish
- 7. Portfolio qadami: GitHub profili serverda
- 7.1 Fayllar
- 7.2 Zaxirani sinash
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): waterfall'ni toping
- 2-mashq (o'rta): bitta API yiqilsa ham
- 3-mashq (qiyin): vaqt chegarasi
- 4-mashq: Portfolio qadami
- 10. Real ishda
- Xulosa
- Manbalar
Next.js'da serverda ma'lumot olish: async komponent, fetch va Promise.all
Qisqacha: Next.js'da Server Component async funksiya bo'la oladi: ichida
await fetch(...)yoki ma'lumotlar bazasi so'rovini yozasiz va natijani to'g'ridan JSX'ga qo'yasiz —useEffectham, "yuklanmoqda" state'i ham kerak emas. So'rov serverdan ketadi: kalit va sirlar brauzerga tushmaydi, tashrif buyuruvchining IP manzili tashqi API'ga bormaydi. Bir-biriga bog'liq bo'lmagan so'rovlarniPromise.allbilan parallel boshlang — ketma-ketawaitqilinsa vaqtlar qo'shiladi (waterfall). Tashqi API yiqilishi mumkin: xatoni ushlab, sahifaga zaxira ko'rsating, lekin Next.js'ning o'z signallarini yutib yubormang.
Bu darsda
- Async Server Component yozasiz va u
useEffectbilan ma'lumot olishdan nimasi bilan farq qilishini tushuntirasiz. - Waterfall'ni kodda topasiz va uni
Promise.allyoki preload naqshi bilan tuzatasiz. - Tashqi API javobini
unknowndan xavfsiz turga aylantirasiz va xato, limit, tarmoq uzilishida zaxira qaytarasiz. - Kanon topgan haqiqiy tuzoqni ko'rasiz:
try/catchbuild vaqtidagi Next.js signalini "tarmoq xatosi" deb yutib yubordi. portfolioning «Men haqimda» sahifasiga GitHub profilini serverda olib kelasiz.
Oldin bilishingiz kerak: Rendering strategiyalari, Server Components, Ketma-ket va parallel bajarish, Promise kombinatorlari, Bekor qilish: AbortController, Server state va client state.
1. Nega bu kerak?
«Men haqimda» sahifasida GitHub profilingiz ko'rinsa yaxshi bo'lardi: nechta ochiq repo, oxirgi uchta loyiha. Bu ma'lumot sizning kodingizda emas — u GitHub serverida. Uni qandaydir yo'l bilan olib kelish kerak.
React'da buni qanday qilardingiz? 19-qismda TanStack Query bilan: brauzer sahifani ochadi, JavaScript yuklanadi, keyin fetch ketadi, oxirida ma'lumot chiziladi. Ya'ni o'quvchi avval "yuklanmoqda" ni ko'radi. Undan oldin esa JavaScript to'plamining yuklanishini kutadi.
Bu yo'lning yana uchta kamchiligi bor:
| Muammo | Brauzerdan fetch |
Serverdan fetch |
|---|---|---|
| Birinchi ekran | "Yuklanmoqda…" | Tayyor matn (yoki oqim) |
| Kalit yoki sir | Brauzerga tushadi | Serverda qoladi |
| Tashrif buyuruvchi IP'si | GitHub'ga ketadi | Ketmaydi |
| JavaScript o'chiq | Ma'lumot yo'q | Ma'lumot bor |
Next.js'da Server Component serverda ishlaydi (Server Components darsi). Demak u ma'lumotni o'zi olib kela oladi — xuddi oshpaz ovqatni oshxonada tayyorlab, mijozga tayyor likopchada chiqargandek. Mijoz (brauzer) "go'sht qayerdan keldi" deb bozorga bormaydi.
2. Async Server Component
2.1 Eng oddiy shakl
Server Component — oddiy funksiya, va u async bo'la oladi. Ichida await yozasiz, natijani JSX'ga qo'yasiz:
// app/haqimda/page.tsx — eng oddiy shakl (hali kanon emas)
export default async function HaqimdaSahifasi() {
const javob = await fetch("https://api.github.com/users/octocat");
const profil = await javob.json();
return (
<main>
<h1>Men haqimda</h1>
<p>{profil.public_repos} ta ochiq repo</p>
</main>
);
}Uch narsaga qarang:
useStateyo'q,useEffectyo'q. Komponent ma'lumot kelguncha kutadi, keyin bir marta chiziladi.- "Yuklanmoqda" holatini o'zingiz yozmaysiz. Kutish vaqtini qanday ko'rsatish — keyingi darsdagi Suspense'ning ishi.
fetch— brauzerdagifetchning o'zi (fetchasoslari). Node.js'da ham globalfetchbor, Next.js uni serverda ishlatadi.
Client Component'da async funksiya komponent bo'la olmaydi — React xato beradi. Bu imkoniyat faqat serverda. Sababi oddiy: brauzer komponentni har bosishda qayta chizadi, server esa sahifani bir marta tayyorlab yuboradi.
Tekshirib ko'ring: nega Server Component'da
useEffectichidafetchqilish ma'nosiz?
Javob
useEffect faqat brauzerda, komponent ekranga chiqqandan keyin ishlaydi. Server Component esa brauzerda umuman ishlamaydi — uning kodi brauzerga yuborilmaydi. Shuning uchun Server Component'da hooklar (useState, useEffect) yo'q: ma'lumot to'g'ridan await bilan olinadi.
2.2 fetch faqat bitta yo'l
Server Component ichida istalgan asinxron ishni qilish mumkin:
| Manba | Misol | Kursda |
|---|---|---|
| Tashqi HTTP API | fetch("https://api.github.com/…") |
shu dars |
| Fayl tizimi | readFile("src/blog/salom.md") |
Server Components — blog |
| Ma'lumotlar bazasi (ORM) | db.post.findMany() |
26–27-qismlar |
| O'z backend'ingiz | fetch(process.env.API_URL + "/vazifalar") |
25-qismdan |
ORM (Object-Relational Mapping) — ma'lumotlar bazasi bilan oddiy funksiyalar orqali gaplashish kutubxonasi (Prisma, Drizzle). Hozir bilish shart emas, 26-qismda o'rganamiz. Muhimi: Server Component kodi brauzerga yuborilmagani uchun baza paroli ham, so'rov ham serverda qoladi.
Kanon portfolio da bunga ikki misol bor. Blog postlari — fayldan (Server Components darsida qo'shilgan). GitHub profili — tashqi API'dan, bugun qo'shamiz.
2.3 fetch Next 16 da keshlanmaydi
Next.js 13–14 da serverdagi fetch sukut bo'yicha keshlanardi: bir marta olingan javob keyingi so'rovlarda qayta ishlatilardi. Bu ko'p chalkashlik tug'dirdi. Next 15 dan boshlab va 16.4 da ham fetch keshlanmaydi. Next.js hujjati 16.4-bob shunday deydi:
fetch requests are not cached by default and will block the page
from rendering until the request is complete.Tarjimasi: "fetch so'rovlari sukut bo'yicha keshlanmaydi va so'rov tugaguncha sahifa chizilishini to'xtatib turadi". Ya'ni ikkita tanlov bor: natijani keshlash ("use cache" — Cache Components I) yoki kutish vaqtini oqim bilan ko'rsatish (<Suspense> — Streaming). Bugun biz so'rovning o'zini to'g'ri yozishni o'rganamiz.
Yana bir foydali detal: bitta sahifa chizilishida bir xil fetch (manzil va sozlamalar bir xil) bir necha komponentda chaqirilsa, Next.js uni bir marta bajaradi — bu memoizatsiya (memoization). Shuning uchun ma'lumotni props bilan pastga uzatish o'rniga, kerakli komponentning o'zida fetch qilsa bo'ladi. Fayl va baza so'rovlari uchun bu ish React.cache() bilan qilinadi — React cache() va DAL darsida.
3. Waterfall va Promise.all
3.1 Ikki so'rov — ikki yo'l
GitHub profilini to'liq ko'rsatish uchun ikkita so'rov kerak: profil (/users/octocat) va oxirgi repolar (/users/octocat/repos). Ular bir-biriga bog'liq emas — ikkinchisi birinchisining javobini kutmaydi.
Ketma-ket yozsak:
const user = await ol(`${API}/users/${login}`);
const repos = await ol(`${API}/users/${login}/repos`);Ikkinchi so'rov birinchisi tugagandan keyin boshlanadi. Vaqtlar qo'shiladi. Bu waterfall ("sharshara") deyiladi: suv pog'onama-pog'ona tushgandek, har so'rov oldingisini kutadi (J-130 dagi sikl ichidagi await bilan bir xil muammo).
Parallel yozsak:
const [user, repos] = await Promise.all([
ol(`${API}/users/${login}`),
ol(`${API}/users/${login}/repos`),
]);Ikkala ol(...) chaqiruvi darhol ishga tushadi, await esa ikkalasini birga kutadi. Umumiy vaqt — eng uzun so'rov vaqti.
sequenceDiagram
participant S as Server
participant G as GitHub
Note over S,G: Ketma-ket (waterfall)
S->>G: /users/octocat
G-->>S: profil
S->>G: /users/octocat/repos
G-->>S: repolar
Note over S,G: Parallel (Promise.all)
par
S->>G: /users/octocat
and
S->>G: /users/octocat/repos
end
G-->>S: profil va repolarDiagrammaning yuqori qismida ikkinchi so'rov birinchisining javobidan keyin ketadi. Pastki qismida ikkalasi bir paytda chiqadi.
3.2 O'zingiz o'lchab ko'ring
GitHub o'rniga taymer bilan soxta so'rov yasaymiz: profil 300 ms, repolar 200 ms. «Ishga tushir» ni bosing:
const wait = (ms, value) =>
new Promise((resolve) => setTimeout(() => resolve(value), ms));
async function sequential() {
const user = await wait(300, "profil");
const repos = await wait(200, "repolar");
return [user, repos];
}
function parallel() {
return Promise.all([wait(300, "profil"), wait(200, "repolar")]);
}
const round = (ms) => Math.round(ms / 100) * 100;
let start = performance.now();
console.log(await sequential());
console.log("ketma-ket:", round(performance.now() - start), "ms");
start = performance.now();
console.log(await parallel());
console.log("parallel:", round(performance.now() - start), "ms");Konsolda:
[ 'profil', 'repolar' ]
ketma-ket: 500 ms
[ 'profil', 'repolar' ]
parallel: 300 msNatija bir xil, vaqt boshqacha: 300 + 200 = 500 ms va max(300, 200) = 300 ms. Vaqtni 100 ms ga yaxlitladik — taymerlar bir necha millisekund kechikishi mumkin (Taymerlar).
Kanon portfolio da GitHub'dan ikkala so'rov ham Promise.all bilan ketadi. Kanon o'lchovida GitHub bloki issiq serverda 155–164 ms da tayyor bo'ldi — bu ikkita so'rovning yig'indisi emas, kattarog'i.
3.3 Qachon ketma-ket bo'lishi shart
Ba'zan waterfall — xato emas, zarurat. Ikkinchi so'rovga birinchisining javobi kerak bo'lsa, kutmasdan iloji yo'q. Masalan, avval foydalanuvchi profilini olasiz, keyin uning id si bilan buyurtmalarini so'raysiz.
Qoida: har await oldidan o'zingizga savol bering — "keyingi qator shu natijani ishlatadimi?". Ishlatmasa, so'rovlarni oldinroq boshlang.
Tekshirib ko'ring: sahifada uchta so'rov bor: ob-havo (200 ms), valyuta kursi (100 ms) va kurs bo'yicha narx (valyuta kursini talab qiladi, 150 ms). Eng tez yo'l bilan qancha vaqt ketadi?
Javob
Taxminan 250 ms. Ob-havo va valyuta kursi bir-biriga bog'liq emas — parallel boshlanadi. Narx so'rovi valyuta kursini kutadi: 100 + 150 = 250 ms. Ob-havo shu vaqt ichida (200 ms) tugaydi. Umumiy vaqt — eng uzun zanjir: 250 ms. Hammasi ketma-ket bo'lsa 450 ms bo'lardi.
3.4 Komponentlar orasidagi waterfall
Waterfall faqat bitta funksiya ichida emas. Ichma-ich async komponentlar ham uni yaratadi:
async function Sahifa() {
const profil = await githubProfil("octocat"); // 1) kutadi
return <Repolar login={profil.login} />;
}
async function Repolar({ login }: { login: string }) {
const repos = await repolar(login); // 2) 1-dan keyin boshlanadi
return <ul>{/* … */}</ul>;
}Repolar komponenti Sahifa ning await i tugamaguncha yaratilmaydi ham — uning fetch i kech boshlanadi. Bu kodda ko'zga tashlanmaydi: har komponent alohida qaraganda "to'g'ri" ko'rinadi.
Ikkita davo bor:
- Ikkala so'rovni ota komponentda
Promise.allbilan boshlash (kanon shunday qildi —githubProfilichida). - Preload naqshi (preload pattern) — keyingi bo'lim.
3.5 Preload naqshi
Ba'zan bola komponent ma'lumotini ota komponent oldinroq bilmaydi, lekin kirish qiymati (masalan slug) allaqachon bor. Shunda so'rovni await siz boshlab qo'yasiz, javobni esa bola komponentda olasiz. Next.js hujjatidagi naqsh:
// item.tsx — preload komponent yonida turadi
async function getItem(id: string) {
const res = await fetch(`https://api.example.com/items/${id}`);
return res.json();
}
export const preload = (id: string) => {
void getItem(id); // boshlaymiz, lekin kutmaymiz
};
export default async function Item({ id }: { id: string }) {
const item = await getItem(id); // o'sha so'rovning javobi
return <div>{item.name}</div>;
}// page.tsx
const { id } = await params;
preload(id); // Item so'rovi shu yerda boshlandi
const isAvailable = await checkIsAvailable(id); // bu kutayotganda
return isAvailable ? <Item id={id} /> : null;void — "natija menga kerak emas" degan belgi; ESLint "kutilmagan Promise" deb ogohlantirmasligi uchun. Naqsh ishlashi uchun getItem ikkinchi chaqiruvda o'sha so'rovni qaytarishi shart. fetch bilan bu o'zi bo'ladi (bir xil fetch memoizatsiyasi). Baza so'rovi bo'lsa — React.cache() bilan o'raladi (React cache() va DAL).
4. Kanon kodi: github.ts
4.1 Butun fayl
Endi portfolio dagi haqiqiy faylni o'qiymiz. U uchta ishni qiladi: ikkita so'rovni parallel yuboradi, javobni tekshiradi va har qanday xatoda null qaytaradi.
import "server-only";
// GitHub ochiq REST API — kalitsiz (N-15). Cheklov: IP boshiga
// soatiga 60 so'rov; tarmoq yo'q yoki limit tugasa — null.
// So'rov serverdan ketadi: tashrif buyuruvchining IP manzili
// GitHub'ga bormaydi.
export type GithubProfil = {
login: string;
ochiqRepolar: number;
kuzatuvchilar: number;
songgiRepolar: {
nomi: string;
url: string;
yulduz: number;
yangilangan: string;
}[];
};
// Test va tajriba uchun boshqa manzil berish mumkin
const API = process.env.GITHUB_API_URL ?? "https://api.github.com";
const SARLAVHALAR = {
Accept: "application/vnd.github+json",
"User-Agent": "portfolio-v2",
};Kanon fayldagi uzun izoh qatorlari telefonga sig'ishi uchun bo'lindi va qisqartirildi — ma'nosi o'zgarmadi.
| Qator | Nima qiladi |
|---|---|
import "server-only" |
Bu fayl client komponentga import qilinsa — build xatosi (Client va server kompozitsiyasi) |
GithubProfil |
Sahifaga kerakli maydonlar — GitHub javobining hammasi emas |
GITHUB_API_URL |
Muhit o'zgaruvchisi: testda ishlamaydigan manzil berib, zaxirani ko'rish uchun |
User-Agent |
GitHub API usiz so'rovni rad etadi |
Kalitsiz degani — GitHub token yo'q. Kalitsiz API IP manzil boshiga soatiga 60 ta so'rovga ruxsat beradi (kanon /rate_limit dan "limit": 60 ni ko'rgan). Bu muhim raqam, unga Streaming va Cache Components I darslarida qaytamiz.
4.2 Bitta so'rov: ol()
async function ol(url: string): Promise<unknown> {
const javob = await fetch(url, {
headers: SARLAVHALAR,
signal: AbortSignal.timeout(3000),
});
if (!javob.ok)
throw new Error(
`GitHub ${javob.status} (${javob.headers.get("x-ratelimit-remaining") ?? "?"} so'rov qoldi)`,
);
return javob.json();
}Uchta muhim detal:
AbortSignal.timeout(3000)— GitHub 3 soniyada javob bermasa, so'rov bekor qilinadi (AbortController). Busiz sekin API sahifani cheksiz ushlab turishi mumkin.javob.ok—fetch403 yoki 500 da xato tashlamaydi, faqat tarmoq uzilganda tashlaydi (fetchasoslari). Shuning uchun statusni o'zimiz tekshiramiz.x-ratelimit-remaining— GitHub'ning sarlavhasi: "yana nechta so'rov qoldi". Xato matniga qo'shilgan — logda limit tugaganini darhol ko'rasiz.- Qaytgan tur —
unknown. Tashqi API nima yuborishini kodimiz kafolatlay olmaydi (unknown).
throw new Error(...) qatori kanonda 70 belgidan uzun — kanon kodini aynan ko'chirdik.
4.3 Parallel va tekshiruv
function sonmi(x: unknown): number {
return typeof x === "number" ? x : 0;
}
export async function githubProfil(
login: string,
): Promise<GithubProfil | null> {
try {
// Ikkala so'rov parallel — ketma-ket await qilinsa
// "waterfall" (vaqt qo'shiladi)
const [user, repos] = await Promise.all([
ol(`${API}/users/${encodeURIComponent(login)}`),
ol(
`${API}/users/${encodeURIComponent(login)}/repos?sort=pushed&per_page=3`,
),
]);
if (typeof user !== "object" || user === null || !Array.isArray(repos))
return null;
const u = user as Record<string, unknown>;
return {
login,
ochiqRepolar: sonmi(u.public_repos),
kuzatuvchilar: sonmi(u.followers),
songgiRepolar: repos.flatMap((r: unknown) => {
if (typeof r !== "object" || r === null) return [];
const x = r as Record<string, unknown>;
if (typeof x.name !== "string" || typeof x.html_url !== "string")
return [];
return [
{
nomi: x.name,
url: x.html_url,
yulduz: sonmi(x.stargazers_count),
yangilangan: String(x.pushed_at ?? ""),
},
];
}),
};
} catch (xato) {
console.warn(
"GitHub profili olinmadi:",
xato instanceof Error ? xato.message : xato,
);
return null;
}
}Qatorma-qator asosiylari:
| Joy | Ma'nosi |
|---|---|
encodeURIComponent(login) |
Login ichidagi / yoki ? manzilni buzmaydi |
Promise.all([...]) |
Ikkala so'rov bir paytda; bittasi yiqilsa — ikkalasi ham catch ga |
typeof user !== "object" |
Narrowing: unknown ni obyektga toraytirish |
sonmi(...) |
Son bo'lmasa 0 — sahifada undefined ta repo chiqmaydi |
flatMap |
Noto'g'ri repo yozuvi tashlanadi ([]), to'g'risi qoladi |
catch → null |
Har qanday xato — zaxira UI uchun signal |
Nega Zod emas? 15-qismda qo'lda tekshirishni o'rgangan edingiz, va bu yerda faqat beshta maydon bor. Kanon serverga qo'shimcha paket olib kirmadi. Zod forma uchun Server Actions II darsida keladi.
5. Xatolarni boshqarish
5.1 Ikki strategiya
Tashqi API yiqilganda sahifa nima qilishi kerak? Ikki yo'l bor:
| Strategiya | Qanday | Qachon |
|---|---|---|
| Xatoni tashlash | throw → eng yaqin error.tsx |
Sahifa ma'lumotsiz ma'nosiz (masalan blog posti) |
| Zaxira qaytarish | catch → null → "olib bo'lmadi" matni |
Ma'lumot qo'shimcha, sahifaning qolgani muhimroq |
error.tsx ni Maxsus fayllar darsida ko'rgansiz. GitHub bloki esa «Men haqimda» sahifasining kichik qismi. GitHub ishlamasa ham, ism, bilimlar va o'qish rejasi ko'rinishi kerak. Shuning uchun kanon ikkinchi yo'lni tanladi: null va zaxira matn.
5.2 Natija oynasida: uch holat
fetch ni parametr qilib beramiz — shunda haqiqiy GitHub'siz uch vaziyatni sinash mumkin: muvaffaqiyat, limit tugashi (403) va tarmoq uzilishi. Bu kanon funksiyasining kichraytirilgan nusxasi:
type Fetcher = (url: string) => Promise<Response>;
type Profile = { login: string; repos: number };
function toNumber(x: unknown): number {
return typeof x === "number" ? x : 0;
}
async function githubProfile(
login: string,
fetcher: Fetcher,
): Promise<Profile | null> {
try {
const res = await fetcher(`/users/${login}`);
if (!res.ok) throw new Error(`GitHub ${res.status}`);
const data: unknown = await res.json();
if (typeof data !== "object" || data === null) return null;
const u = data as Record<string, unknown>;
return { login, repos: toNumber(u.public_repos) };
} catch (error) {
console.log(
"olinmadi:",
error instanceof Error ? error.message : error,
);
return null;
}
}
const works: Fetcher = async () => Response.json({ public_repos: 8 });
const limited: Fetcher = async () =>
new Response("rate limit", { status: 403 });
const offline: Fetcher = async () => {
throw new TypeError("fetch failed");
};
async function main() {
console.log(await githubProfile("octocat", works));
console.log(await githubProfile("octocat", limited));
console.log(await githubProfile("octocat", offline));
}
main();Konsolda:
{ login: 'octocat', repos: 8 }
olinmadi: GitHub 403
null
olinmadi: fetch failed
nullResponse.json(...) va new Response(...) — Web standart klasslar (Request va Response), Node'da ham, brauzerda ham bor. Kanondagi console.warn o'rniga bu yerda console.log — natija bitta oqimda ko'rinsin. fetch failed — Node'ning fetch i tarmoq uzilganda aynan shu matnli TypeError tashlaydi. Kanon ham GitHub'ni o'chirib build qilganda logda shu satrni ko'rgan:
GitHub profili olinmadi: fetch failed5.3 Biz topdik: try/catch Next.js signalini yutdi
Endi darsning eng muhim saboqi. Kanon GithubBlok komponentini birinchi marta yozganda next build quyidagini ikki marta chiqardi (xom chiqish, uzun qator bo'linmagan):
GitHub profili olinmadi: During prerendering, fetch() rejects when the prerender is complete. Typically these errors are handled by React but if you move fetch() to a different context by using `setTimeout`, `after`, or similar functions you may observe this error and you should handle it in that context. This occurred at route "/haqimda".Tarjimasi: "Prerender paytida, prerender tugaganda fetch() rad etiladi. Odatda bu xatolarni React o'zi ushlaydi, lekin fetch() ni boshqa kontekstga ko'chirsangiz… bu xatoni ko'rishingiz mumkin. Bu /haqimda marshrutida bo'ldi."
Nima bo'ldi? Build paytida Next.js sahifani oldindan chizadi (prerender — Rendering strategiyalari). GitHub bloki Suspense ichida edi, va Next.js uni "dinamik teshik" deb tashlab ketdi. Lekin bungacha blok fetch ni boshlab ulgurgan edi. Prerender tugaganda Next.js bu fetch ni ataylab rad etdi — bu xato emas, "bu qism keyin, so'rov paytida chiziladi" degan ichki signal.
Bizning catch esa farqni bilmaydi: u signalni ushladi, "GitHub profili olinmadi" deb yozdi va null qaytardi. Ikki marta — chunki Promise.all ichida ikkita fetch bor edi. Sahifa baribir to'g'ri ishladi (◐ /haqimda), lekin build logi yolg'on gapirdi: GitHub ishlab turgan edi.
flowchart TB
B["next build: /haqimda prerender"] --> F["GithubBlok fetch boshladi"]
F --> E["prerender tugadi: fetch rad etildi"]
E --> C["bizning catch: 'olinmadi'"]
C --> L["logda yolg'on xato, 2 marta"]Kanon tuzatishi — blok boshida bitta qator:
import { connection } from "next/server";
export async function GithubBlok() {
// connection() — "bu blok har so'rovda": prerender shu yerda
// to'xtaydi, build vaqtida fetch boshlanmaydi
await connection();
const profil = await githubProfil(GITHUB_LOGIN);
// …
}connection() — "bu yerdan keyingi kod faqat haqiqiy so'rov kelganda ishlasin" degan belgi. Next.js hujjati: await connection() // prerendering stops here. Endi build vaqtida fetch boshlanmaydi, rad etish ham, yolg'on log ham yo'q. Build chiqishida route o'zgarmadi — ◐ /haqimda (statik qobiq va dinamik teshik).
Ikkinchi yo'l ham bor — Next.js'ning unstable_rethrow(xato) funksiyasi catch boshida Next.js'ning o'z xatolarini (notFound(), redirect() va boshqalar) qayta tashlaydi. Nomidagi unstable_ — API hali o'zgarishi mumkin. Kanon uni ishlatmadi: connection() muammoning ildizini — build vaqtidagi keraksiz so'rovni — yo'q qiladi.
Umumiy saboq: keng catch — xavfli. Framework ichida ishlaganda catch faqat sizning xatolaringizni emas, framework signallarini ham ushlashi mumkin. Belgisi — logdagi g'alati xabar va "lekin hammasi ishlayapti". Shunday xabarni ko'rsangiz, uni o'qing, tashlab ketmang.
Tekshirib ko'ring:
connection()qo'shilgandan keyin GitHub profili qachon olinadi — build paytidami yoki sahifa ochilgandami?
Javob
Sahifa ochilganda — har so'rovda. connection() prerender'ni shu joyda to'xtatadi, shuning uchun build vaqtida GitHub'ga so'rov ketmaydi. Bu esa yangi muammo tug'diradi: har ko'rish = GitHub'ga 2 ta so'rov, kalitsiz limit soatiga 60 ta. Uni Cache Components I darsida "use cache" bilan yechamiz.
6. O'z backend'ingizni chaqirish
Keyingi qismlarda vazifalar uchun alohida backend yozasiz (NestJS — 25+ qismlar). Next.js sahifasi uni ham xuddi GitHub kabi serverdan chaqiradi. Sxema (hozir ishga tushirilmaydi — backend hali yo'q):
// malumot/vazifalar.ts — sxema, 25-qismdan keyin
import "server-only";
export async function vazifalar(): Promise<unknown> {
const javob = await fetch(`${process.env.API_URL}/vazifalar`, {
headers: { Authorization: `Bearer ${process.env.API_KALIT}` },
signal: AbortSignal.timeout(3000),
});
if (!javob.ok) throw new Error(`API ${javob.status}`);
return javob.json();
}Bu yerda ikki narsa serverda qolishi muhim:
API_KALIT— sir. UNEXT_PUBLIC_bilan boshlanmaydi, demak brauzer to'plamiga tushmaydi (Env o'zgaruvchilar va sirlar darsida batafsil).API_URL— ichki manzil bo'lishi mumkin (masalanhttp://api:4000). Brauzer uni umuman ko'rmaydi.
Bunday tuzilma BFF (Backend for Frontend — frontend uchun backend) deyiladi: Next.js server foydalanuvchi bilan haqiqiy backend o'rtasida turadi. Qachon BFF, qachon to'g'ridan backend — Route Handler, Server Action yoki alohida backend? darsida.
7. Portfolio qadami: GitHub profili serverda
Bu dars, Rendering strategiyalari va Streaming darslari portfolio ning bitta holatini quradi. Bugun — ma'lumot qatlami. Suspense va commit — keyingi darsda. Hali commit qilmang — npm run build ham <Suspense> qo'shilguncha to'xtaydi (4-mashqda).
7.1 Fayllar
| Fayl | Nima |
|---|---|
src/malumot/sozlama.ts |
GITHUB_LOGIN = "octocat" — o'zingiznikiga almashtirasiz |
src/malumot/github.ts |
githubProfil(): kalitsiz, parallel, unknown tekshiruvi, xatoda null |
src/app/(sayt)/haqimda/_komponentlar/GithubBlok.tsx |
async Server Component + zaxira matn |
octocat — GitHub'ning rasmiy namuna hisobi. O'z loginingizni yozing — sahifada o'z repolaringiz chiqadi.
GithubBlok ning asosiy qismi:
export async function GithubBlok() {
await connection();
const profil = await githubProfil(GITHUB_LOGIN);
if (!profil) {
return (
<p>
GitHub ma'lumotini hozir olib bo'lmadi.{" "}
<a href={`https://github.com/${GITHUB_LOGIN}`}>
Profilni GitHub'da oching
</a>
.
</p>
);
}
return (
<p>
<a href={`https://github.com/${profil.login}`}>@{profil.login}</a>:{" "}
{profil.ochiqRepolar} ta ochiq repo, {profil.kuzatuvchilar} ta
kuzatuvchi.
</p>
// + oxirgi uchta repo ro'yxati
);
}Zaxira matnda ham foydali havola bor: GitHub ishlamasa ham, o'quvchi profilga o'zi o'ta oladi. Bo'sh joy yoki "Xato!" degan quruq matndan ko'ra yaxshiroq.
7.2 Zaxirani sinash
GitHub'ni "o'chirish" uchun internetni uzish shart emas. GITHUB_API_URL ga ishlamaydigan manzil bering — kanon shunday qildi:
GITHUB_API_URL=http://127.0.0.1:9 npm run dev127.0.0.1:9 — kompyuteringizdagi 9-port, unda hech kim tinglamaydi. fetch darhol fetch failed beradi, sahifada zaxira matn chiqadi. Windows PowerShell'da bu sintaksis ishlamaydi — u yerda $env:GITHUB_API_URL="http://127.0.0.1:9" qatorini alohida yozasiz, keyin npm run dev.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Sahifa sekin, so'rovlar logda birin-ketin | Bog'liq bo'lmagan await lar ketma-ket |
Promise.all |
| Bola komponent so'rovi kech boshlanadi | Ota await dan keyin yaratiladi |
So'rovni otada boshlang yoki preload |
| 403/500 da sahifa "muvaffaqiyatli" | javob.ok tekshirilmagan |
if (!javob.ok) throw … |
Cannot read properties of undefined |
Tashqi javob kutilgan shaklda emas | unknown + tekshiruv |
| Sekin API sahifani uzoq ushlaydi | Vaqt chegarasi yo'q | AbortSignal.timeout(ms) |
Build logida fetch() rejects when the prerender is complete |
catch Next.js signalini yutgan |
await connection() yoki unstable_rethrow |
| Bitta API yiqilsa hammasi yo'qoladi | Promise.all bitta rad etishda yiqiladi |
Kerak bo'lsa Promise.allSettled |
| Kalit brauzerda ko'rindi | NEXT_PUBLIC_ prefiksi |
Sirni prefikssiz, faqat serverda |
9. Mashqlar
Mashqlar kurs/mashqlar/23/15-malumot-olish/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): waterfall'ni toping
Quyidagi kodda qaysi await lar parallel bo'la oladi? Har qatorni "kutishi shart" yoki "parallel bo'la oladi" deb belgilang.
const user = await getUser(id); // A
const settings = await getSettings(id); // B
const orders = await getOrders(user.id); // C
const weather = await getWeather(city); // D- B hech qaysi natijani ishlatmaydi: B —
- C
user.idni ishlatadi: C — A ni - D hech qaysi natijani ishlatmaydi: D —
Yechim
A, B va D bir-biriga bog'liq emas — uchalasi Promise.all bilan birga boshlanadi. C ga user.id kerak, demak u A dan keyin. Eng tez variant:
const [user, settings, weather] = await Promise.all([
getUser(id),
getSettings(id),
getWeather(city),
]);
const orders = await getOrders(user.id);Yana tezroq yo'l ham bor: C ni A tugashi bilan boshlash, B va D ni kutmasdan — getUser(id).then((u) => getOrders(u.id)) ni ham Promise.all ga qo'shish. Bu holda umumiy vaqt = max(A + C, B, D).
2-mashq (o'rta): bitta API yiqilsa ham
«Men haqimda» sahifasi profil va repolarni oladi. Repolar API'si yiqilsa ham, profil ko'rinishi kerak (repolar o'rnida bo'sh ro'yxat). Promise.allSettled (Promise kombinatorlari) bilan loadAbout() funksiyasini yozing. Soxta so'rovlar: profile() 100 ms dan keyin { login: "octocat", repos: 8 }, repos() 50 ms dan keyin Error("GitHub 403") bilan rad etiladi. Natijada { profile, repos: [] } va konsolda bitta ogohlantirish (console.log) chiqsin.
Yechim
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
async function profile() {
await wait(100);
return { login: "octocat", repos: 8 };
}
async function repos() {
await wait(50);
throw new Error("GitHub 403");
}
async function loadAbout() {
const [p, r] = await Promise.allSettled([profile(), repos()]);
if (r.status === "rejected") {
console.log("repolar olinmadi:", r.reason.message);
}
return {
profile: p.status === "fulfilled" ? p.value : null,
repos: r.status === "fulfilled" ? r.value : [],
};
}
console.log(await loadAbout());Konsolda:
repolar olinmadi: GitHub 403
{ profile: { login: 'octocat', repos: 8 }, repos: [] }Promise.all bo'lganda repolar xatosi profilni ham "olib ketardi" — butun natija catch ga tushardi. allSettled har so'rov natijasini alohida beradi. Kanon esa ataylab Promise.all ni tanladi: GitHub limiti tugasa, ikkala so'rov ham yiqiladi — alohida ishlov foyda bermaydi. Bu ham qaror: avval qanday xato bo'lishi mumkinligini o'ylang.
3-mashq (qiyin): vaqt chegarasi
getWithTimeout(fetcher, ms) funksiyasini yozing: fetcher(signal) ga AbortSignal.timeout(ms) beradi va ms dan uzoq ketsa null qaytaradi, konsolga vaqt tugadi yozadi. Soxta slowApi(signal) 500 ms dan keyin "javob" qaytaradi, lekin signal bekor qilinsa darhol signal.reason bilan rad etiladi. getWithTimeout(slowApi, 1000) va getWithTimeout(slowApi, 100) ni sinang. Ishora: vaqt tugaganda signal.reason — TimeoutError nomli DOMException (AbortController).
Yechim
function slowApi(signal) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => resolve("javob"), 500);
signal.addEventListener("abort", () => {
clearTimeout(timer);
reject(signal.reason);
});
});
}
async function getWithTimeout(fetcher, ms) {
try {
return await fetcher(AbortSignal.timeout(ms));
} catch (error) {
if (error.name === "TimeoutError") {
console.log("vaqt tugadi");
return null;
}
throw error; // boshqa xatolarni yutmaymiz
}
}
console.log(await getWithTimeout(slowApi, 1000));
console.log(await getWithTimeout(slowApi, 100));Konsolda:
javob
vaqt tugadi
nullcatch faqat kutilgan xatoni (TimeoutError) ushlaydi, qolganini qayta tashlaydi — «Biz topdik» bo'limidagi saboq. Haqiqiy fetch ham signal ni xuddi shunday hurmat qiladi: vaqt tugasa TimeoutError bilan rad etiladi.
4-mashq: Portfolio qadami
O'z portfolio ingizga src/malumot/sozlama.ts (o'z GitHub loginingiz), src/malumot/github.ts va GithubBlok.tsx ni qo'shing. GithubBlok ni «Men haqimda» sahifasiga «GitHub» sarlavhasi ostida qo'ying. next dev da tekshiring: profil chiqdimi? Keyin GITHUB_API_URL=http://127.0.0.1:9 bilan qayta ishga tushiring — zaxira matn chiqishi kerak. Commit — keyingi darsda.
Yechim
Namuna — «Kanon kodi: github.ts» va «Portfolio qadami» bo'limlaridagi kod. Tekshiruv ro'yxati:
- terminal logida
GitHub profili olinmadifaqatGITHUB_API_URLbilan chiqadi; - DevTools → Network: brauzerdan
api.github.comga birorta ham so'rov ketmaydi — hammasi serverda.
Hozircha sahifa GitHub javobini kutib turadi (Suspense hali yo'q). Shuning uchun next dev terminalida qizil xabar chiqadi — sahifa esa ochilaveradi (biz ishga tushirdik, holat 16 nusxasi Suspense siz):
Error: Route "/haqimda": Next.js encountered uncached data during prerendering.
`fetch(...)` or `connection()` accessed outside of `<Suspense>` prevents the route from being prerendered, blocking the page load and leading to a slower user experience.Bu Rendering strategiyalari darsidagi [stream]/[cache]/[block] xatosi. npm run build esa hozir shu xato bilan to'xtaydi — bu kutilgan holat. Keyingi darsda GithubBlok ni <Suspense> ga o'raysiz: build toza o'tadi va fetch() rejects when the prerender is complete xabari ham chiqmaydi.
10. Real ishda
- Har Next.js loyihasida ma'lumotning katta qismi Server Component'da olinadi: e-commerce mahsulot sahifasi, blog, dashboard.
useEffect+fetch— App Router'da kam uchraydigan naqsh. - Waterfall — Next.js loyihalaridagi sekinlikning eng ko'p sababi. Senior dasturchi code review'da birinchi bo'lib ketma-ket
awaitlarni qidiradi. - Tashqi API limitlari (GitHub, ob-havo, valyuta kursi) — real muammo. Kalit serverda qoladi, natija keshlanadi, xatoda zaxira ko'rsatiladi.
- BFF — katta kompaniyalarda keng tarqalgan: Next.js server bir nechta ichki xizmatdan ma'lumot yig'adi va brauzerga bitta sahifa beradi.
- Intervyuda: "Server Component'da ma'lumot olishning afzalligi nima?", "Waterfall nima va qanday oldini olasiz?" — tez-tez beriladi.
Xulosa
- Server Component
asyncbo'la oladi:await fetch(...)va natija to'g'ridan JSX'da; hooklar kerak emas. - So'rov serverdan ketadi: sir va kalit serverda qoladi, tashrif buyuruvchi IP'si tashqi API'ga bormaydi.
- Next 15+ da
fetchsukut bo'yicha keshlanmaydi; bir render ichidagi bir xilfetchlar esa birlashtiriladi. - Bog'liq bo'lmagan so'rovlar —
Promise.all; komponentlar orasidagi waterfall — otada boshlash yoki preload naqshi. - Tashqi javob —
unknown:ok, shakl va vaqt chegarasini tekshiring; xatoda zaxira qaytaring. - Keng
catchNext.js signalini yutishi mumkin — kanonconnection()bilan tuzatdi.
Keyingi dars: Streaming va Suspense — GitHub bloki sekin bo'lsa ham, sahifaning qolgani darhol chiqadi.
Manbalar
- Next.js hujjati — Fetching Data (16.4,
node_modules/next/dist/docs): https://nextjs.org/docs/app/getting-started/fetching-data (2026-10-21 da o'qildi) - Next.js hujjati —
connection: https://nextjs.org/docs/app/api-reference/functions/connection - Next.js hujjati —
unstable_rethrow: https://nextjs.org/docs/app/api-reference/functions/unstable_rethrow - GitHub REST API — Rate limits: https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api
- Kanon o'lchovlari:
portfoliov2, holat 16 (next 16.4.0, Node 24.21.0); build logi va oqim vaqtlari — kanon xom natijalari.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!