IlmHamroh
JavaScript Full-stack/23-qism. Next.js27/43-dars18 daqiqa
Mundarija (33)

Next.js request API'lari: cookies(), headers(), draftMode() va after()

Qisqacha: cookies() va headers() — kelgan so'rovni o'qiydigan async funksiyalar (await cookies()). O'qish istalgan server kodida mumkin, lekin cookie yozish faqat Server Action va Route Handler'da: sahifa oqimi boshlangach, javob sarlavhasini o'zgartirib bo'lmaydi. Bu funksiyalar sahifani statikdan dinamikka o'tkazadi, Cache Components'da esa <Suspense> ichida bo'lishi shart. draftMode() — CMS qoralamasini ko'rish rejimi. after() — javob ketgandan keyin bajariladigan ish: log, analitika.

Bu darsda

  • cookies() va headers() bilan so'rovni o'qiysiz va ular sahifani nega dinamik qilishini build jadvalida ko'rasiz.
  • Cookie nega faqat Server Action yoki Route Handler'da yozilishini haqiqiy xato bilan tushuntirasiz.
  • draftMode() bilan statik sahifani faqat muharrir uchun "jonli" qilishni ko'rasiz.
  • after() bilan portfolio ning aloqa formasiga spam loglarini qo'shasiz — foydalanuvchini kutdirmasdan.
  • connection() qachon kerakligini bilasiz.

Oldin bilishingiz kerak: proxy.ts, Route Handlers, Server Actions xavfsizligi, Streaming va Suspense, Cookie'lar JS'dan.

1. Nega bu kerak?

Oldingi darsda proxy so'rovni eshikda ushladi. Lekin ba'zan so'rov ma'lumoti sahifa yoki action'ning o'zida kerak:

  • aloqa formasi: tezlik cheklovi uchun yuboruvchining IP manzili;
  • sayt mavzusi: foydalanuvchi qorong'i rejimni tanlagan bo'lsa, sahifani darhol qorong'i chizish;
  • blog: muharrir hali chiqmagan postni oldindan ko'rishi;
  • log: "spam bot ushlandi" yozuvi — lekin foydalanuvchi bu yozuvni kutib turmasligi kerak.

Next buning uchun kichik funksiyalar to'plamini beradi. Ularning hammasi bitta savolga bog'liq: bu ma'lumot qachon ma'lum? Build vaqtidami (statik sahifa) yoki faqat so'rov kelganda?

Hayotiy o'xshatish: bosmaxona va kafe. Gazeta (statik sahifa) tunda bosiladi — hamma uchun bir xil, ertalab tayyor. Kafedagi buyurtma (dinamik) esa mijoz kelganda tayyorlanadi: u nima istashini oldindan bilib bo'lmaydi. cookies() va headers() — mijozdan so'rash. Ularni chaqirsangiz, sahifa "gazeta" bo'lolmay qoladi.

2. cookies() va headers() — so'rovni o'qish

2.1 Async funksiyalar

Ikkalasi next/headers dan keladi va Promise qaytaradi:

tsx
import { cookies, headers } from "next/headers";

export default async function Sahifa() {
  const tema = (await cookies()).get("tema")?.value;
  const til = (await headers()).get("accept-language");
  return <p>{tema} · {til}</p>;
}
Funksiya Qaytaradi Asosiy metodlar
cookies() so'rov cookie'lari get, getAll, has; action/handler'da set, delete
headers() so'rov sarlavhalari (faqat o'qish) get, has, entries

Next 15 gacha (14 va undan oldin) bu funksiyalar sinxron edi (cookies().get(...)). Next 15 dan async — eski kodda await siz ko'rsangiz, bu eski uslub. Sabab — params ning Promise bo'lgani bilan bir xil: Next sahifaning statik qismini so'rov ma'lumotisiz oldindan tayyorlay olishi uchun.

Cookie — brauzer har so'rovda Cookie sarlavhasida qaytarib yuboradigan kichik nom=qiymat yozuv (HTTP sarlavhalari va cookie). cookies() shu sarlavhani o'qib, qulay obyektga aylantiradi. Ichida nima bo'layotganini oddiy JS'da ko'raylik:

js
function cookieOqi(sarlavha) {
  const natija = {};
  for (const juft of sarlavha.split(";")) {
    const [nom, ...qolgan] = juft.trim().split("=");
    if (nom) natija[nom] = decodeURIComponent(qolgan.join("="));
  }
  return natija;
}

const c = cookieOqi("tema=qorongi; til=uz; savat=osh%2Cmanti");
console.log(c.tema); // qorongi
console.log(c.savat); // osh,manti
console.log(c.yoq ?? "yo'q"); // yo'q

%2C — vergulning kodlangan ko'rinishi: cookie qiymatida maxsus belgilar shunday yoziladi. cookies().get("tema") ham aslida shu ishni qiladi, faqat tayyor va to'liq.

Kanonda cookies() ishlatilmaydi. Shuning uchun biz portfolio (holat 32) nusxasida kichik tajriba sahifasini qo'shdik — /tajriba/cookie (biz ishga tushirdik, next 16.4.0). Birinchi urinish — eng oddiy kod:

tsx
import { cookies } from "next/headers";

export default async function CookieSahifa() {
  const tema = (await cookies()).get("tema")?.value ?? "yo'q";
  return (
    <main id="asosiy">
      <h1>Tema cookie: {tema}</h1>
    </main>
  );
}

next build — yiqildi (stek izi qisqartirildi):

text
Error: Route "/tajriba/cookie": Next.js encountered uncached or runtime data during prerendering.
`fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams`, or `connection()` accessed outside of `<Suspense>` prevents the route from being prerendered, blocking the page load and leading to a slower user experience.
Ways to fix this:
  - [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
  - [cache] For uncached data (`fetch`, database calls): cache the access with `"use cache"` (does not apply to `connection()`)
  - [block] Set `export const instant = false` to allow a blocking route

Tarjimasi: "Next prerender paytida keshlanmagan yoki so'rov vaqtidagi ma'lumotga duch keldi. cookies() va boshqalar <Suspense> dan tashqarida chaqirilsa, sahifani oldindan tayyorlab bo'lmaydi — sahifa yuklanishi bloklanadi". Xato uchta yo'l taklif qiladi: stream (Suspense bilan o'rash), cache (cookie uchun mos emas — u har foydalanuvchida boshqa), block (butun sahifani dinamik qilish).

Bu — Cache Components qoidasi. Next sahifaning iloji boricha katta qismini statik qobiq qilib tayyorlamoqchi. So'rovga bog'liq qism esa alohida "orol" bo'lishi kerak. To'g'ri variant:

tsx
import { cookies } from "next/headers";
import { Suspense } from "react";

async function TemaYozuvi() {
  const tema = (await cookies()).get("tema")?.value ?? "yo'q";
  return <p>Tema cookie: {tema}</p>;
}

export default function CookieSahifa() {
  return (
    <main id="asosiy">
      <h1>Cookie tajribasi</h1>
      <Suspense fallback={<p>Yuklanmoqda…</p>}>
        <TemaYozuvi />
      </Suspense>
    </main>
  );
}

Endi build o'tdi. Jadvalda:

text
├ ◐ /tajriba/cookie

◐ — Partial Prerender: sarlavha va «Yuklanmoqda…» build'da tayyor, cookie qismi esa har so'rovda oqim bilan keladi. curl bilan ikki marta so'radik — cookie'siz va Cookie: tema=qorongi bilan (HTML'dan ko'chirma):

text
Tema cookie: <!-- -->yo&#x27;q
Tema cookie: <!-- -->qorongi

<!-- --> — React matn bo'laklari orasiga qo'yadigan belgi, &#x27; — apostrof. Ya'ni bir xil URL, ikki xil javob — cookie'ga qarab.

Tekshirib ko'ring: portfolio da mavzu (yorug'/qorong'i) localStorage da saqlanadi (Hydration darsi). Uni cookie'ga ko'chirsak, server sahifani darhol to'g'ri rangda chiza olardi. Kanon nega buni qilmadi?

Javob

Cookie'ni o'qigan har sahifa dinamik bo'lib qoladi: hozir hamma sahifa ○ (build'da tayyor, TTFB 4–12 ms), cookie bilan esa root layout'dagi mavzu hamma joyda ◐ yoki ƒ ga aylanardi. Kanon boshqa yo'lni tanladi: <head> dagi kichik skript localStorage ni hydration'dan oldin o'qiydi, sahifalar esa statik qoladi. Narx — bitta inline skript; foyda — CDN keshida turadigan butun sayt.

3.1 Haqiqiy xato

Endi sahifaning o'zida cookie yozishga urinamiz (yana <Suspense> ichida):

tsx
async function TemaniYoz() {
  (await cookies()).set("tema", "qorongi");
  return <p>Yozildi</p>;
}

Build o'tdi (◐ /tajriba/cookie-yoz). Lekin next start da sahifani ochganimizda curl 200 oldi, server logida esa:

text
⨯ Error: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options

Tarjimasi: "Cookie'larni faqat Server Action yoki Route Handler'da o'zgartirish mumkin".

Nega? Cookie brauzerga Set-Cookie javob sarlavhasi bilan yetadi. Sarlavhalar esa javobning eng boshida, matndan oldin ketadi. Sahifa oqim bilan yuborilganda (statik qobiq darhol, <Suspense> ichi keyin) — sarlavhalar allaqachon jo'natilgan. Hujjat ham shuni yozadi: HTTP oqim boshlangandan keyin cookie o'rnatishga ruxsat bermaydi.

Status 200 bo'lgani ham saboq: xato oqim o'rtasida bo'ldi, status esa avval ketgan. Bu proxy darsidagi 404 muammosining egizagi.

sequenceDiagram
  participant B as Brauzer
  participant S as Next server
  B->>S: GET /sahifa (Cookie: tema=...)
  S-->>B: 200 + sarlavhalar + statik qobiq
  Note over S: Suspense ichi render bo'ladi
  S-->>B: qolgan HTML (oqim)
  Note over S: bu yerda Set-Cookie — kech

3.2 To'g'ri joy: Route Handler

Xuddi shu cookie'ni Route Handler'da yozdik:

ts
import { cookies } from "next/headers";

export async function POST() {
  const c = await cookies();
  c.set("tema", "qorongi", {
    httpOnly: true,
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24 * 365,
  });
  return Response.json({ ok: true });
}

Javob sarlavhalari (curl -i -X POST, biz ishga tushirdik):

text
HTTP/1.1 200 OK
set-cookie: tema=qorongi; Path=/; Expires=Thu, 21 Oct 2027 05:25:53 GMT; Max-Age=31536000; HttpOnly; SameSite=lax
{"ok":true}

Variantlar Set-Cookie atributlariga aylandi:

Variant Atribut Ma'nosi
httpOnly: true HttpOnly brauzer JS'i (document.cookie) o'qiy olmaydi
sameSite: "lax" SameSite=lax boshqa saytdan POST'da yuborilmaydi (CSRF'ga qarshi)
maxAge Max-Age, Expires 1 yil yashaydi (soniyalarda)
path: "/" Path=/ butun saytda amal qiladi

Server Action'da ham xuddi shunday yoziladi — u ham alohida POST javobi. Cookie atributlarining to'liq tahlili (Secure, Domain, __Host- prefiksi) — Cookie chuqur darsida.

Tekshirib ko'ring: Tema tugmasi bosilganda cookie yozilishi kerak. Tugma client komponentda. Cookie'ni qayerda yozasiz?

Javob

Server Action'da: tugma onClick da (yoki forma orqali) temaniSaqla("qorongi") action'ini chaqiradi, action ichida (await cookies()).set(...). Action — alohida POST so'rov, uning javobida Set-Cookie bor. Client komponentning o'zi esa document.cookie bilan ham yoza oladi, lekin u holda httpOnly bo'lmaydi.

4. headers() va IP manzil

4.1 Kanondagi ishlatilishi

headers() ni siz allaqachon ishlatgansiz — Server Actions xavfsizligi darsida, tezlik cheklovi uchun:

ts
const ip =
  (await headers()).get("x-forwarded-for")?.split(",")[0]?.trim() ?? "lokal";

x-forwarded-for — teskari proksi (nginx, Vercel) qo'shadigan sarlavha: "bu so'rov aslida qaysi IP'dan keldi". Bir nechta proksi bo'lsa, ro'yxat vergul bilan: birinchi element — eng chetdagi mijoz.

Bu mantiqni alohida sinab ko'ramiz:

js
function ipniOl(sarlavha) {
  return sarlavha?.split(",")[0]?.trim() ?? "lokal";
}

console.log(ipniOl("203.0.113.5")); // 203.0.113.5
console.log(ipniOl("203.0.113.5, 10.0.0.2")); // 203.0.113.5
console.log(ipniOl(null)); // lokal

?. — sarlavha yo'q bo'lsa xato bermasdan undefined, ?? — u holda "lokal" (?? va ?.). 203.0.113.x — hujjatlar uchun ajratilgan namuna manzil.

4.2 Hujumchi nigohi: sarlavhaga ishonish

x-forwarded-for — oddiy sarlavha. Uni brauzer emas, har kim yubora oladi:

bash
curl -H "X-Forwarded-For: 1.2.3.4" https://example.com/aloqa

Agar saytingiz oldida sarlavhani qayta yozadigan proksi bo'lmasa, hujumchi har so'rovda boshqa "IP" yuborib, tezlik cheklovini aylanib o'tadi. Yechim — kod emas, infratuzilma. Ishonchli proksi (Vercel, to'g'ri sozlangan nginx) bu sarlavhani o'zi qayta yozadi yoki haqiqiy manzilni ro'yxat oxiriga qo'shadi. Kod esa faqat ishonchli qismni o'qiydi (3-mashqda). portfolio ning cheklovi — birinchi qatlam, to'liq himoya emas. Teskari proksi ortida Node qanday ishlashini Reverse proxy orqasida Node darsida batafsil ko'ramiz.

Qoida: headers() dagi har qiymat — foydalanuvchi kiritgan ma'lumot kabi. Unga ishonishdan oldin kim qo'yganini biling.

5. draftMode() — qoralamani ko'rish

5.1 Muammo

CMS (Content Management System, "kontent boshqaruv tizimi") — maqolalarni kodsiz yozadigan admin panel: Sanity, Contentful, Strapi. Sayt CMS'dan postlarni olib, statik sahifa yasaydi. Muharrir yangi postni yozdi, lekin hali "nashr" bosmadi. U sahifa saytda qanday ko'rinishini ko'rmoqchi. Statik sahifa esa build'da yasalgan — unda qoralama yo'q.

Draft mode (qoralama rejimi) — shu muammoning yechimi: maxsus cookie bor bo'lsa, statik sahifa shu foydalanuvchi uchun so'rov vaqtida qayta render bo'ladi va qoralama ma'lumotini ko'rsatadi. Qolganlar uchun sahifa statik qoladi.

5.2 Tajriba

Ikki fayl qo'shdik: rejimni yoqadigan Route Handler va holatni ko'rsatadigan sahifa.

ts
// app/api/tajriba/qoralama/route.ts
import { draftMode } from "next/headers";

export async function GET() {
  (await draftMode()).enable();
  return new Response("Qoralama yoqildi");
}
tsx
// sahifa ichida (Suspense ichida)
const { isEnabled } = await draftMode();
return <p>Qoralama rejimi: {isEnabled ? "yoqilgan" : "o'chiq"}</p>;

Build jadvalida sahifa — ○ statik (draftMode uni dinamik qilmadi). Uch so'rov (biz ishga tushirdik):

text
## 5) qoralama: oddiy
HTTP/1.1 200 OK
x-nextjs-cache: HIT
Cache-Control: s-maxage=31536000
Qoralama rejimi: <!-- -->o&#x27;chiq
## 6) qoralamani yoqish
HTTP/1.1 200 OK
set-cookie: __prerender_bypass=5e6eccc673b33a1e643aac4235052172; Path=/; Secure; HttpOnly; SameSite=none
Qoralama yoqildi
## 7) qoralama cookie bilan
HTTP/1.1 200 OK
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate
x-nextjs-cache: HIT
Qoralama rejimi: <!-- -->yoqilgan

O'qing:

  1. Oddiy tashrif — keshdan (HIT), bir yil keshlanadi, "o'chiq".
  2. enable() — __prerender_bypass cookie'sini o'rnatadi. Uning qiymati har next build da yangi — taxmin qilib bo'lmaydi.
  3. Shu cookie bilan — xuddi o'sha sahifa endi private, no-store va "yoqilgan": statik nusxa chetlab o'tildi.

Haqiqiy loyihada CMS "Ko'rish" tugmasi /api/qoralama?secret=...&slug=... ni ochadi, handler sirni tekshirib enable() qiladi va postga redirect qiladi. Sahifa isEnabled bo'lsa CMS'dan qoralama versiyasini oladi. portfolio da CMS yo'q (postlar — Markdown fayllar), shuning uchun kanonda draftMode ishlatilmagan.

Diqqat: Secure; SameSite=none — cookie faqat HTTPS orqali yuboriladi. Lokal http://localhost da brauzer uni saqlamasligi mumkin; hujjat lokal sinash uchun uchinchi tomon cookie'lariga ruxsat berishni aytadi. Biz curl da cookie'ni qo'lda yubordik.

6. after() — javobdan keyin

6.1 G'oya

Aloqa formasiga bot yozdi. Server buni sezdi (asal idish to'lgan) va logga yozmoqchi. Lekin log yozish — foydalanuvchi uchun keraksiz kutish. Javobni darhol qaytarib, logni keyin yozsa bo'ladimi?

after(callback) (next/server dan) aynan shuni qiladi: callback javob (yoki prerender) tugagandan keyin bajariladi. Hujjat bo'yicha u Server Component, Server Action, Route Handler va proxy'da ishlaydi.

G'oyani oddiy JS modeli bilan ko'raylik. Bu Next kodi emas — o'quv modeli: keyin navbatga qo'yadi, javobBer avval javobni "yuboradi", keyin navbatni bajaradi:

js
const navbat = [];
const keyin = (ish) => navbat.push(ish);

function xabarYubor(forma) {
  if (forma.vebSayt) {
    keyin(() => console.log("log: aloqa-spam, asal-idish"));
    return "yuborildi";
  }
  return "qabul";
}

function javobBer(forma) {
  const javob = xabarYubor(forma);
  console.log(`javob ketdi: ${javob}`);
  for (const ish of navbat.splice(0)) ish();
}

javobBer({ vebSayt: "http://spam.example" });
javobBer({ vebSayt: "" });

Konsolda:

text
javob ketdi: yuborildi
log: aloqa-spam, asal-idish
javob ketdi: qabul

after ichidagi ish funksiya ichida yozilgan, lekin javobdan keyin bajarildi. navbat.splice(0) — massivni bo'shatib, elementlarini qaytaradi: har javobdan keyin navbat toza.

6.2 portfolio dagi after()

Kanondagi o'zgarish (xabarYubor.ts, holat 24 → 27):

diff
 import { headers } from "next/headers";
+import { after } from "next/server";
 …
-  if (String(forma.get("veb_sayt") ?? "") !== "")
+  if (String(forma.get("veb_sayt") ?? "") !== "") {
+    // after() — javob ketgandan KEYIN bajariladi (N-27): log foydalanuvchini kutdirmaydi
+    after(() =>
+      console.warn(JSON.stringify({ tur: "aloqa-spam", sabab: "asal-idish" })),
+    );
     return { holat: "yuborildi", ism: "" };
+  }
 …
   if (!ruxsatmi(ip)) {
+    after(() =>
+      console.warn(JSON.stringify({ tur: "aloqa-spam", sabab: "limit" })),
+    );

Ikki joy: asal idish to'lganda va tezlik cheklovidan oshganda. Log — JSON qator: keyin loglar va instrumentation darsida aynan shu formatni yig'amiz.

Kanon o'lchovi (natijalar/27-after.txt):

text
asal idish POST 200, javob tugadi [1792528058635] (162 ms)
server log:
[1792528058635] {"tur":"aloqa-spam","sabab":"asal-idish"}

Kvadrat qavsdagi son — millisekundlardagi vaqt belgisi (Date.now()). Javob tugagan va log yozilgan payt — bir xil millisekund: log javobni kutdirmadi, javobdan keyin darhol bajarildi.

6.3 Qoidalar

Qoida Hujjatdan
after sahifani dinamik qilmaydi statik sahifada callback build vaqtida ishlaydi
Xato bo'lsa ham bajariladi notFound(), redirect() yoki istisno bo'lsa ham
Action va handler'da — cookies()/headers() callback ichida mumkin so'rov konteksti saqlanadi
Server Component'da — callback ichida mumkin emas ma'lumotni oldindan o'qib, o'zgaruvchi bilan bering
Vaqt chegarasi — platformaniki serverless'da maxDuration

Birinchi qatorga e'tibor bering: statik sahifadagi after(() => log("ko'rildi")) har tashrifda emas, build paytida bir marta ishlaydi. Sahifa ko'rishlarini sanash uchun bu noto'g'ri joy.

Tekshirib ko'ring: after() bilan "Rahmat" xatini foydalanuvchiga yuborish yaxshi g'oyami?

Javob

Qisman. Foydalanuvchi xat yuborilishini kutmaydi — bu yaxshi. Lekin after ishonchli navbat emas: server shu payt qayta ishga tushsa yoki serverless funksiya vaqti tugasa, xat yo'qoladi va hech kim bilmaydi. Log va analitika uchun bu yo'qotish arzimas; muhim xat uchun esa haqiqiy navbat (qayta urinish bilan) kerak — backend qismlarida.

7. connection() — "so'rovni kut"

Ba'zan komponent cookie ham, sarlavha ham o'qimaydi, lekin baribir har so'rovda boshqa natija berishi kerak: Math.random(), joriy vaqt. Build vaqtida hisoblansa, hamma bir xil "tasodifiy" sonni ko'rardi. await connection() (next/server) — "prerender shu yerda to'xtasin, qolgani faqat so'rov kelganda".

portfolio buni Serverda ma'lumot olish darsida tajriba qilgan (o'sha yerda batafsil): GitHub so'rovi try/catch ichida bo'lganda prerender rad etishi jimgina "yutilgan" edi. connection() bilan esa nima dinamik ekani aniq belgilandi (natijalar/tajriba/16-build-connection.txt):

text
├ ◐ /haqimda

Keyinroq kanon GitHub blokini "use cache" ga o'tkazdi va /haqimda yana ○ bo'ldi (1h 1d). Xulosa: connection() — kamdan-kam kerak bo'ladigan aniq vosita. Avval "bu ma'lumotni keshlasa bo'ladimi?" deb so'rang.

8. Umumiy jadval

API Qayerdan Sahifani dinamik qiladimi Qayerda ishlaydi
cookies() next/headers ha (Suspense ichida) o'qish — hamma joyda; yozish — action, handler
headers() next/headers ha faqat o'qish
draftMode() next/headers yo'q (faqat cookie egasi uchun) enable() — handler'da
after() next/server yo'q component, action, handler, proxy
connection() next/server ha component

Yana bir yordamchi: userAgent(request) (next/server) — proxy va handler'da User-Agent sarlavhasini brauzer, qurilma va OS'ga ajratib beradi.

9. Portfolio qadami: after() va commit

9.1 O'zgarish

Bitta fayl — src/app/(sayt)/aloqa/_amal/xabarYubor.ts: «portfolio dagi after()» bo'limidagi diff. Oldingi darsdagi src/proxy.ts bilan birga bu holat 27.

9.2 Tekshiruv

bash
npm run lint
npm run tip
npm run build
npm run start

Asal idishni qo'lda sinash: aloqa sahifasida DevTools'da yashirin veb_sayt maydonini topib qiymat yozing va formani yuboring. Ekranda — oddiy "yuborildi" holati, next start terminalida esa:

text
{"tur":"aloqa-spam","sabab":"asal-idish"}

Kanon o'lchovi: JS o'zgarmadi, axe 0, hydration 0; modal oqimi proxy bilan ham ishlaydi (natijalar/27-modal.txt).

9.3 Commit

bash
git add -A
git commit

Commit (kanon): Proxy: noma'lum loyiha/post slug'i darhol 404; aloqa spam loglari after() bilan.

10. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Build: encountered uncached or runtime data during prerendering cookies()/headers() <Suspense> dan tashqarida O'qiydigan qismni alohida komponent + Suspense
Cookies can only be modified in a Server Action or Route Handler. Sahifada cookies().set() Action yoki Route Handler'da yozing
Hamma sahifa ◐/ƒ bo'lib qoldi Root layout'da cookies() Faqat kerakli sahifada; yoki cookie'siz yechim
cookies().get is not a function Eski sinxron uslub (await cookies()).get(...)
Tezlik cheklovi aylanib o'tiladi x-forwarded-for ga ko'r-ko'rona ishonch Sarlavhani qayta yozadigan ishonchli proksi
after ichida log har tashrifda emas Sahifa statik — callback build'da Analitika — client yoki dinamik joyda
Draft mode lokal ishlamaydi Cookie Secure; SameSite=none HTTPS yoki brauzer sozlamasi

11. Mashqlar

Mashqlar kurs/mashqlar/23/27-request-api/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): qayerda mumkin?

  • Server Component'da cookie o'qish —
  • Server Component'da cookie yozish —
  • Route Handler'da cookie yozish —
  • headers() bilan javob sarlavhasini o'zgartirish —
Yechim
  • mumkin — brauzer cookie'ni so'rov bilan yuborgan, o'qish xavfsiz (Suspense ichida).
  • mumkin emas — oqim boshlangan, Set-Cookie kech.
  • mumkin — handler javobini o'zi yaratadi, sarlavha qo'shish mumkin.
  • mumkin emas — headers() faqat so'rov sarlavhalarini o'qiydi. Javob sarlavhasi — Route Handler'ning Response ida, proxy'da yoki next.config headers() da.

setCookieYoz(nom, qiymat, variantlar) funksiyasini yozing: { httpOnly, sameSite, maxAge, path } dan Set-Cookie satrini yasasin. Qiymat encodeURIComponent bilan kodlansin. Tartib: nom=qiymat, Path, Max-Age, HttpOnly, SameSite. Satrlarni massivga yig'ib (push), join("; ") bilan birlashtiring.

Yechim
js
function setCookieYoz(nom, qiymat, v = {}) {
  const qismlar = [`${nom}=${encodeURIComponent(qiymat)}`];
  if (v.path) qismlar.push(`Path=${v.path}`);
  if (v.maxAge !== undefined) qismlar.push(`Max-Age=${v.maxAge}`);
  if (v.httpOnly) qismlar.push("HttpOnly");
  if (v.sameSite) qismlar.push(`SameSite=${v.sameSite}`);
  return qismlar.join("; ");
}

console.log(
  setCookieYoz("tema", "qorongi", {
    path: "/",
    maxAge: 31536000,
    httpOnly: true,
    sameSite: "lax",
  }),
);
console.log(setCookieYoz("savat", "osh,manti"));

Konsolda:

text
tema=qorongi; Path=/; Max-Age=31536000; HttpOnly; SameSite=lax
savat=osh%2Cmanti

Birinchi qatorni darsdagi haqiqiy set-cookie bilan solishtiring: Next yana Expires ni qo'shadi (eski brauzerlar uchun, Max-Age dan hisoblanadi). Ikkinchi qatorda vergul kodlandi — aks holda ba'zi serverlar uni ajratuvchi deb o'ylardi.

3-mashq (qiyin): ishonchli IP

ipniOl ni yaxshilang: ishonchliProksilar soni (n) berilsin. x-forwarded-for ro'yxatini o'ngdan o'qing: oxirgi n ta element — o'zingizning proksilaringiz qo'shgan manzillar, ulardan oldingisi — haqiqiy mijoz. Ro'yxat qisqa bo'lsa — "lokal". Nega o'ngdan? Chunki chap tomonni mijozning o'zi yozishi mumkin. at() manfiy indeks bilan oxiridan oladi.

Yechim
js
function ipniOl(sarlavha, n = 1) {
  const royxat = (sarlavha ?? "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean);
  return royxat.at(-n) ?? "lokal";
}

// mijoz → 1 ta o'z proksimiz
console.log(ipniOl("203.0.113.5", 1));
// hujumchi soxta qiymat qo'shdi, proksi haqiqiysini oxiriga yozdi
console.log(ipniOl("1.2.3.4, 203.0.113.5", 1));
// ikki qatlam: CDN + nginx
console.log(ipniOl("203.0.113.5, 198.51.100.7", 2));
console.log(ipniOl(null, 1));

Konsolda:

text
203.0.113.5
203.0.113.5
203.0.113.5
lokal

Ikkinchi qator — hujumchi X-Forwarded-For: 1.2.3.4 yubordi, lekin o'z proksimiz haqiqiy manzilni ro'yxat oxiriga qo'shdi. Chapdan o'qigan kod 1.2.3.4 ni olardi va aldanardi. O'ngdan n-chi — ishonchli. Shart: n aynan sizning proksilaringiz soniga teng bo'lishi kerak — buni infratuzilmangizdan bilasiz.

4-mashq: Portfolio qadami

O'z portfolio ingizdagi xabarYubor ga ikkita after() qo'shing (asal idish va tezlik cheklovi). Asal idishni DevTools orqali to'ldirib sinang va terminalda JSON log chiqishini ko'ring. Keyin proxy.ts (oldingi dars) bilan birga commit qiling.

Yechim

Namuna — «Portfolio qadami» bo'limidagi diff. Tezlik cheklovini sinash: formani 4 marta tez-tez yuboring (kanon cheklovi — 10 daqiqada 3 ta) — to'rtinchisida ekranda "Juda ko'p urinish" xabari, terminalda {"tur":"aloqa-spam","sabab":"limit"}. Log javobdan keyin chiqadi: brauzer javobni oldin oladi. Commit xabari — kanondagi yoki o'zingizniki.

12. Real ishda

  • Auth tizimlari sessiya cookie'sini Server Action yoki Route Handler'da httpOnly qilib yozadi, sahifalar esa cookies() bilan o'qiydi — bu 28-qism ning asosi.
  • CMS'li saytlar (Sanity, Contentful, Payload) draft mode'ni rasmiy qo'llanmalarida ko'rsatadi: "Preview" tugmasi — draftMode().enable() li Route Handler.
  • Analitika va audit loglari after() bilan yoziladi: foydalanuvchi kutmaydi. Vercel'da after platformaning waitUntil imkoniyatiga ulanadi.
  • Intervyu savoli: "Nega Server Component'da cookie o'rnatib bo'lmaydi?" — javob: oqim boshlangan, Set-Cookie sarlavhasi kech. Bu savol HTTP'ni tushunishni tekshiradi.

Xulosa

  • cookies() va headers() — async, so'rovni o'qiydi; Cache Components'da <Suspense> ichida bo'lishi shart, aks holda build to'xtaydi.
  • Cookie yozish — faqat Server Action va Route Handler'da: sahifa oqimi boshlangach Set-Cookie kech (haqiqiy xato bilan ko'rdik).
  • draftMode() — statik sahifani faqat __prerender_bypass cookie egasi uchun so'rov vaqtida chizadi; qolganlarga kesh.
  • after() — javobdan keyingi ish; portfolio da spam loglari javob bilan bir millisekundda, kutdirmasdan.
  • x-forwarded-for — foydalanuvchi ma'lumoti kabi; connection() — faqat haqiqatan har so'rovda boshqa natija kerak bo'lsa.

Keyingi dars: Client tomonda ma'lumot: TanStack Query bilan Next.js — serverda oldindan olingan ma'lumotni client keshiga uzatamiz va portfolio ga nega Query kerak emasligini o'lchov bilan ko'ramiz.

Manbalar

  • Next.js hujjati (16.4.0, node_modules/next/dist/docs/): cookies, headers, draftMode, after, connection, userAgent API reference; "Draft Mode" guide — nextjs.org/docs/app/api-reference/functions/cookies (2026-10-21 da o'qildi)
  • Kanon natijalari: natijalar/27-after.txt, natijalar/27-modal.txt, natijalar/tajriba/16-build-connection.txt; holat 27 diff
  • Cookie, cookies().set() va draftMode tajribalari — biz ishga tushirdik: portfolio holat 32 nusxasi, next 16.4.0, next build + next start + curl, 2026-10-21
  • RFC 7239 / MDN: X-Forwarded-For — developer.mozilla.org/docs/Web/HTTP/Headers/X-Forwarded-For
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Next.js request API'lari: cookies(), headers(), draftMode() va after() — IlmHamroh