IlmHamroh
JavaScript Full-stack/23-qism. Next.js21/43-dars17 daqiqa
Mundarija (30)

Next.js Server Actions: "use server" va JavaScript'siz ishlaydigan forma

Qisqacha: Server Action — "use server" bilan belgilangan async funksiya. U serverda ishlaydi, lekin uni client'dan oddiy funksiya kabi chaqirasiz — eng ko'p <form action={xabarYubor}> orqali. Next.js build vaqtida funksiya tanasini brauzer to'plamidan olib tashlaydi va o'rniga "shu ID'li action'ni POST qil" degan havolani qo'yadi. Forma JavaScript yuklanmasdan ham ishlaydi: brauzer oddiy HTML forma kabi POST yuboradi (progressive enhancement). Action ichida FormData o'qiladi, ma'lumot saqlanadi, keyin kesh yangilanadi yoki redirect() qilinadi. Har Server Action — ochiq POST manzil, shuning uchun tekshiruvlar action ichida bo'lishi shart.

Bu darsda

  • "use server" faylida Server Action yozasiz va uni formaga ulaysiz.
  • Brauzer qanday so'rov yuborishini haqiqiy o'lchov bilan ko'rasiz: Next-Action sarlavhasi va JS'siz formadagi yashirin maydonlar.
  • Action'ni forma, tugma (formAction) va hodisa ichidan chaqirish yo'llarini ajratasiz.
  • Mutatsiyadan keyin revalidatePath, updateTag va redirect ni to'g'ri tartibda ishlatasiz.
  • portfolio ning «Aloqa» sahifasiga Server Action bilan forma qo'shasiz (holat 23 ning birinchi qismi).

Oldin bilishingiz kerak: Actions: async transitionlar va form action, Serverda ma'lumot olish, Cache Components II, FormData, Client Components va "use client".

1. Nega bu kerak?

v1 portfolio'dagi aloqa formasini eslang (04 va 09-qismlar). U chiroyli edi: maydonlar, tekshiruv, yuborilmoqda holati. Lekin bitta muammo bor edi — xabar hech qayerga ketmasdi. GitHub Pages'da server yo'q, forma faqat brauzerda "Rahmat" deb qo'yardi.

Xabarni haqiqatan qabul qilish uchun server kerak. Odatda bu ikki qismli ish: backend'da API manzil (POST /api/xabar) yozasiz, frontend'da fetch bilan unga yuborasiz, javobni tekshirasiz, xatolarni ko'rsatasiz. Ikki fayl, ikki til (JSON formati, URL, metod), ikki joyda turlar.

Next.js'da qisqaroq yo'l bor. Serverda ishlaydigan funksiya yozasiz va uni formaga to'g'ridan berasiz. Qolganini — POST so'rov, ma'lumotni uzatish, javobni qaytarish — Next.js o'zi qiladi.

Hayotiy o'xshatish: pochta qutisi. Siz xatni qutiga tashlaysiz — u qanday yetib borishi, qaysi mashinada ketishi sizning ishingiz emas. Manzilni bilsangiz yetarli. Server Action — formangiz uchun shunday "pochta qutisi".

18-qismda React 19 Actions bilan tanishgansiz: <form action={async (formData) => …}>. U yerda funksiya brauzerda ishlardi. Next.js'da aynan o'sha action prop'iga serverdagi funksiya beriladi.

2. Birinchi Server Action

2.1 "use server" fayli

Eng sodda variant — alohida fayl, birinchi qatorida "use server". Fayldagi har bir export qilingan async funksiya — Server Action:

ts
// app/(sayt)/aloqa/_amal/xabarYubor.ts — soddalashtirilgan
"use server";

export async function xabarYubor(forma: FormData) {
  const ism = String(forma.get("ism") ?? "");
  const xabar = String(forma.get("xabar") ?? "");
  console.log("Yangi xabar:", ism, xabar.length, "belgi");
}

_amal papkasi _ bilan boshlangani uchun marshrut emas — u faqat kodni saqlaydi (Route groups va private papkalar).

Formada:

tsx
// app/(sayt)/aloqa/page.tsx — Server Component
import { xabarYubor } from "./_amal/xabarYubor";

export default function AloqaSahifasi() {
  return (
    <form action={xabarYubor}>
      <label htmlFor="ism">Ismingiz</label>
      <input id="ism" name="ism" required />
      <label htmlFor="xabar">Xabar</label>
      <textarea id="xabar" name="xabar" required />
      <button type="submit">Yuborish</button>
    </form>
  );
}

name atributlari muhim: forma.get("ism") aynan shu nom bilan o'qiydi (FormData). console.log brauzer konsolida emas, server terminalida chiqadi — funksiya serverda ishlaydi.

2.2 Ikkita yozish usuli

"use server" ni ikki joyga yozish mumkin:

Joy Misol Qachon
Fayl boshida _amal/xabarYubor.ts Odatiy; client komponentlar ham import qila oladi
Funksiya ichida Server Component ichidagi async function Faqat Server Component'da, kichik misollar

Ikkinchisi — "inline" action:

tsx
export default function Sahifa() {
  async function salomla(forma: FormData) {
    "use server";
    console.log("Salom,", forma.get("ism"));
  }
  return <form action={salomla}>{/* … */}</form>;
}

Client komponent fayli ("use client") ichida inline "use server" yozib bo'lmaydi — u holda action alohida "use server" fayldan import qilinadi. Kanon birinchi yo'lni tanladi: action alohida faylda, forma — client komponentda (keyingi darsda useActionState uchun).

2.3 Biz topdik: faqat async

"use server" fayldagi har export — Server Action. Sinxron funksiya eksport qilinsa nima bo'ladi? Biz kanon xabarYubor.ts oxiriga oddiy yordamchi funksiya qo'shib build qildik:

ts
export function maydonlarSoni() {
  return MAYDONLAR.length;
}
text
Error: Turbopack build failed with 1 error:
./src/app/(sayt)/aloqa/_amal/xabarYubor.ts:50:17
Error: Server Actions must be async functions.

Tarjimasi: "Server Action'lar async funksiya bo'lishi shart". Next.js har eksportni "client'dan chaqiriladigan manzil" deb ko'radi, va tarmoq orqali chaqiruv doim asinxron. Yordamchi funksiyalar, konstantalar, turlar — alohida faylda turadi (kanonda sxema.ts). Turni eksport qilish mumkin (export type kompilyatsiyada o'chib ketadi).

Tekshirib ko'ring: "use server" faylida export const LIMIT = 3; yozish mumkinmi?

Javob

Yo'q. "use server" fayldan faqat async funksiyalar eksport qilinadi — konstanta ham Server Action deb tekshiriladi va build xato beradi. LIMIT ni boshqa faylga (masalan sozlama.ts) qo'ying va ikkala joyda import qiling. Eksport qilinmagan ichki konstanta esa mumkin — kanonda const MAYDONLAR = […] aynan shunday.

3. Pardaning orqasi: nima yuboriladi?

3.1 Brauzerda funksiya yo'q

Build vaqtida Next.js "use server" funksiyaning tanasini client to'plamidan olib tashlaydi. Brauzerga faqat havola boradi: action ID (shifrlangan identifikator) va "shu ID'ni serverga POST qil" degan kichik dispetcher. Funksiya kodi, uning importlari (node:fs, baza klienti), sirlar — hammasi serverda qoladi.

3.2 Biz o'lchadik: JS bilan va JS'siz

Biz kanon holatini (23–24) next build + next start bilan ishga tushirib, «Aloqa» formasini headless Chrome'da ikki marta to'ldirdik — JavaScript yoqilgan va o'chiq holda. Formaning HTML'i, brauzer yuborgan POST va natija:

text
JS yoqilgan: form action= | yashirin: $ACTION_REF_1, $ACTION_1:0, $ACTION_1:1, $ACTION_KEY
  POST /aloqa content-type=multipart/form-data next-action=60fd8924…(42 belgi)
  URL: /aloqa | natija: Rahmat, Aziz! Xabaringiz qabul qilindi — odatda bir kun ichida javob b
JS o'chiq: form action= | yashirin: $ACTION_REF_1, $ACTION_1:0, $ACTION_1:1, $ACTION_KEY
  POST /aloqa content-type=multipart/form-data next-action=yo'q
  URL: /aloqa | natija: Rahmat, Malika! Xabaringiz qabul qilindi — odatda bir kun ichida javob

Action ID'ni 8 belgigacha qisqartirdik — u har build'da boshqa. Nimani ko'ramiz:

JS yoqilgan JS o'chiq
Kim yuboradi React (fetch) Brauzerning o'zi (oddiy forma)
Manzil Joriy sahifa: POST /aloqa Xuddi shu
Action qanday aniqlanadi Next-Action sarlavhasi Yashirin $ACTION_… maydonlari
Javob RSC payload — sahifa joyida yangilanadi Butun HTML sahifa
Natija "Rahmat, Aziz!" "Rahmat, Malika!"

action= bo'sh — forma joriy manzilga (/aloqa) yuboriladi. Next.js server HTML'ga to'rtta yashirin maydon qo'shgan: ular JavaScript bo'lmaganda ham "qaysi action" ekanini serverga aytadi. Shuning uchun forma JS'siz ham ishladi.

Kanonning o'z o'lchovi ham xuddi shu (natijalar/23-forma.txt):

text
3) JS'siz: POST 200 | Rahmat, Malika! Xabaringiz qabul qilindi — odatda bir kun ic
   yashirin maydonlar (JS'siz forma uchun): $ACTION_REF_1, $ACTION_1:0, $ACTION_1:1, $ACTION_KEY
sequenceDiagram
  participant B as Brauzer
  participant N as Next.js server
  B->>N: POST /aloqa (FormData + Next-Action: id)
  N->>N: xabarYubor(forma) ishlaydi
  N-->>B: action natijasi + yangilangan sahifa (RSC)
  Note over B: JS o'chiq bo'lsa: oddiy POST, javob — butun HTML

Diagrammadagi asosiy narsa: bitta so'rov. Next.js hujjati bo'yicha action natijasi va qayta chizilgan sahifa bitta javobda keladi — alohida "keyin ma'lumotni qayta ol" so'rovi yo'q.

3.3 Progressive enhancement

Progressive enhancement (bosqichma-bosqich yaxshilash) — sahifa avval eng oddiy vositalar (HTML) bilan ishlaydi, JavaScript esa uni qulayroq qiladi, lekin shart emas. JS yuklanmagan bo'lsa (sekin internet, eski telefon, JS o'chirilgan, xato), forma baribir yuboriladi.

Aziz sekin internetda telefondan saytingizni ochdi. Sahifaning HTML'i keldi, JS to'plami hali yuklanmoqda. U formani to'ldirib «Yuborish» ni bosdi. Oddiy React formasida (onSubmit + fetch) hech narsa bo'lmasdi — hodisa ishlovchisi hali yo'q. Server Action formasi esa ishlaydi. Bu nazariya emas — biz yuqorida JS o'chiq holatda "Rahmat, Malika!" ni oldik.

Bu imkoniyatni buzish oson — keyingi darsda useOptimistic bilan qanday buzilishini haqiqiy o'lchovda ko'ramiz.

4. Action'ni chaqirish yo'llari

4.1 Forma, tugma va hodisa

Usul Kod JS'siz ishlaydimi
Forma <form action={xabarYubor}> Ha
Tugma <button formAction={ochir}> Ha
Hodisa / effekt startTransition(() => belgila(id)) Yo'q

formAction — bitta formada ikki xil tugma uchun: «Saqlash» va «O'chirish» har biri o'z action'ini chaqiradi (Tugmalar va yuborish).

Hodisa ichidan chaqirish — masalan "yoqdi" tugmasi:

tsx
"use client";
import { useTransition } from "react";
import { yoqtir } from "./_amal/yoqtir"; // "use server" fayl

export function YoqtirTugmasi({ postId }: { postId: string }) {
  const [kutilmoqda, startTransition] = useTransition();
  return (
    <button
      disabled={kutilmoqda}
      onClick={() => startTransition(() => yoqtir(postId))}
    >
      Yoqdi
    </button>
  );
}

Bu yerda forma yo'q — JS'siz ishlamaydi. Kichik interaktiv amallar uchun qabul qilinadi, lekin asosiy oqim (yuborish, ro'yxatdan o'tish, buyurtma) — forma bilan.

Next.js hujjati bitta nozik xulqni ta'kidlaydi: client bir vaqtda faqat bitta action yuboradi, qolganlari navbatda kutadi. Uchta action'ni Promise.all bilan "parallel" chaqirish ularni tezlashtirmaydi. Parallel ish kerak bo'lsa — uni bitta action ichida qiling.

4.2 Qo'shimcha argument: bind

Action'ga formadan tashqari qiymat kerak bo'lsa (masalan, qaysi post), bind bilan "oldindan" beriladi (call, apply, bind):

tsx
// "use server" faylda
export async function izohQosh(postId: string, forma: FormData) {
  // …
}

// Server Component'da
const izohQoshPostga = izohQosh.bind(null, post.slug);
return <form action={izohQoshPostga}>{/* … */}</form>;

bind qilingan qiymat ham JS'siz formada ishlaydi (Next.js uni yashirin maydonlarga qo'yadi). Lekin u brauzerdan keladi — hujumchi uni o'zgartira oladi. postId ga ishonmang, serverda tekshiring (Server Actions xavfsizligi).

4.3 Natija oynasida: React form action

Natija oynasida server yo'q, lekin React 19'ning <form action> mexanizmi aynan o'sha. Server Action o'rnida — oddiy async funksiya (200 ms "tarmoq"). Kodni ishga tushiring:

tsx
import { useState } from "react";

const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));

export default function App() {
  const [result, setResult] = useState("");

  // Next.js'da bu "use server" fayldagi funksiya bo'lardi
  async function sendMessage(formData: FormData) {
    const name = String(formData.get("ism") ?? "");
    const text = String(formData.get("xabar") ?? "");
    console.log("ism:", name);
    console.log("xabar uzunligi:", text.length);
    await wait(200);
    setResult(`Rahmat, ${name}!`);
  }

  return (
    <form action={sendMessage}>
      <label>
        Ismingiz <input name="ism" />
      </label>
      <label>
        Xabar <textarea name="xabar" />
      </label>
      <button type="submit">Yuborish</button>
      <p role="status">{result}</p>
    </form>
  );
}

Konsolda:

text
ism: Aziz
xabar uzunligi: 23

Ekranda:

text
Ismingiz Xabar Yuborish
Rahmat, Aziz!

React action tugagach formani tozalaydi (inputlar bo'shaydi) — buni React 19 o'zi qiladi. Haqiqiy Next.js'da farq bitta: sendMessage serverda ishlaydi, console.log terminalda chiqadi, setResult o'rniga esa action natija qaytaradi (keyingi dars).

Tekshirib ko'ring: natija oynasidagi forma JavaScript o'chiq bo'lsa ishlaydimi?

Javob

Yo'q. Bu yerda sendMessage — brauzerdagi funksiya, uni React JS orqali chaqiradi. JS bo'lmasa, forma qayoqqa yuborishni bilmaydi. Next.js'dagi Server Action esa server manzilini va yashirin maydonlarni HTML'ga yozadi — shuning uchun JS'siz ham ishlaydi.

5. Mutatsiyadan keyin

5.1 Keshni yangilash

Action ma'lumotni o'zgartirdi — endi sahifa yangi ma'lumotni ko'rsatishi kerak. Cache Components II dagi vositalar shu yerda ishlatiladi:

ts
"use server";
import { updateTag } from "next/cache";

export async function postQosh(forma: FormData) {
  await postniSaqla(forma); // bazaga — 26-qismdan
  updateTag("postlar"); // o'zi darhol ko'radi
}

Next.js updateTag, revalidatePath yoki refresh() chaqirilgan action'dan keyin joriy sahifani serverda qayta chizadi va o'sha javobning ichida yuboradi. Alohida qayta yuklash kerak emas.

5.2 redirect — oxirida va try dan tashqarida

Ko'pincha saqlagandan keyin boshqa sahifaga o'tiladi: yangi post sahifasiga yoki "rahmat" sahifasiga.

ts
"use server";
import { redirect } from "next/navigation";
import { updateTag } from "next/cache";

export async function postQosh(forma: FormData) {
  let slug: string;
  try {
    slug = await postniSaqla(forma);
  } catch {
    return { xato: "Saqlab bo'lmadi" };
  }
  updateTag("postlar"); // avval kesh
  // keyin o'tish — bu qatordan keyin hech narsa ishlamaydi
  redirect(`/blog/${slug}`);
}

Ikki qoida:

  1. redirect() xato tashlash orqali ishlaydi. Uni try ichida chaqirsangiz, catch uni ushlab oladi va o'tish bo'lmaydi. Serverda ma'lumot olish darsidagi "keng catch Next.js signalini yutadi" saboqning yana bir ko'rinishi.
  2. Kesh yangilash redirect dan oldin: undan keyingi kod ishlamaydi.

JS'siz formada redirect oddiy HTTP 303 yo'naltirish bo'lib ishlaydi — progressive enhancement saqlanadi.

6. Kanon: «Aloqa» formasi

Kanon «Aloqa» sahifasiga v1 formani qaytardi — endi haqiqiy server bilan. Maydonlar v1 dagidek: ism, email, mavzu (radio: «Ish taklifi» / «Savol»), xabar va bot'lar uchun yashirin "asal idish" maydoni. Fayllar:

Fayl Vazifa Dars
aloqa/_amal/xabarYubor.ts "use server" — Server Action shu dars
aloqa/_amal/sxema.ts Zod sxemasi va FormaHolati turi N-22
aloqa/_komponentlar/AloqaFormasi.tsx Client: useActionState, useFormStatus N-22
src/malumot/xabarlar.ts Xabarni saqlash (server-only) shu dars
src/malumot/tezlik.ts Tezlik cheklovi N-23

6.1 Xabar qayerga ketadi?

Halol javob: hozircha uzoqqa emas. Kanon xabarlar.ts:

ts
import "server-only";
import { appendFile, mkdir } from "node:fs/promises";
import path from "node:path";
import type { Xabar } from "@/app/(sayt)/aloqa/_amal/sxema";

// Xabar qayerga ketadi? Backend 24+ qismlarda. Hozir: dev —
// lokal fayl (.data/, git'ga tushmaydi); prod — faqat log
// (shaxsiy ma'lumotsiz). Resend, Telegram bot, Vercel KV —
// hujjat bo'yicha, kalit talab qiladi.
export async function xabarniSaqla(x: Xabar): Promise<void> {
  if (process.env.NODE_ENV !== "production") {
    const papka = path.join(process.cwd(), ".data");
    await mkdir(papka, { recursive: true });
    await appendFile(
      path.join(papka, "xabarlar.jsonl"),
      JSON.stringify({ ...x, vaqt: new Date().toISOString() }) + "\n",
    );
    return;
  }
  console.info(
    JSON.stringify({
      tur: "aloqa-xabari",
      mavzu: x.mavzu,
      uzunlik: x.xabar.length,
    }),
  );
}

Kanon izohi telefonga sig'ishi uchun bo'lindi.

  • Dev'da — .data/xabarlar.jsonl fayliga, har xabar bitta JSON qator (JSON Lines). .data papkasi .gitignore da — shaxsiy ma'lumot git'ga tushmaydi.
  • Production'da — faqat log: mavzu va uzunlik, ism va email yo'q. Serverless platformada (Vercel) fayl tizimiga yozib bo'lmaydi, loglarga esa shaxsiy ma'lumot yozilmasligi kerak.

Real saytda xabar emailga (Resend kabi xizmat), Telegram botga yoki bazaga ketadi. Ularning hammasi kalit (API key) talab qiladi — kanonda hisob ochilmagan, shuning uchun ular faqat hujjat bo'yicha tilga olinadi. Haqiqiy saqlash — backend qismlarida (25+).

6.2 Action'ning skeleti

Kanon xabarYubor to'liq ko'rinishi keyingi ikki darsda bo'lak-bo'lak ochiladi (validatsiya, holat, xavfsizlik). Bugungi asosiy shakli:

ts
"use server";

// Server Action (N-21…N-23). Har "use server" funksiya — ochiq
// POST endpoint: kim xohlasa chaqira oladi, shuning uchun hamma
// tekshiruv shu yerda.
export async function xabarYubor(
  _oldingi: FormaHolati,
  forma: FormData,
): Promise<FormaHolati> {
  // 1) maydonlarni o'qish         — shu dars
  // 2) asal idish va tezlik       — N-23
  // 3) Zod tekshiruvi             — N-22
  await xabarniSaqla(natija.data); // 4) saqlash — shu dars
  return { holat: "yuborildi", ism: natija.data.ism };
}

E'tibor bering: kanon imzosida ikkita parametr — _oldingi (oldingi holat) va forma. Bu useActionState talabi, uni keyingi darsda ochamiz. Pastki chiziq (_oldingi) — "parametr bor, lekin ishlatilmaydi" degan kelishuv.

7. Portfolio qadami: forma serverga

Bu dars, Server Actions II va Server Actions xavfsizligi — portfolio ning bitta holati (23). Commit — uchinchisida. Hali commit qilmang.

Bugun:

  1. src/malumot/xabarlar.ts — yuqoridagi kod.
  2. .gitignore ga .data qatori.
  3. aloqa/_amal/xabarYubor.ts — "use server", hozircha sodda shakl (2.1 dagidek): maydonlarni o'qib, xabarniSaqla ga beradi.
  4. aloqa/page.tsx dagi formani action={xabarYubor} ga ulash.

Tekshiruv: npm run dev, formani yuboring, keyin .data/xabarlar.jsonl ni oching — bitta JSON qator. DevTools → Network: aloqa so'rovi, metod POST, sarlavhalarda Next-Action. Keyin DevTools'da JavaScript'ni o'chirib (Command Menu → "Disable JavaScript") yana yuboring — fayl ikkinchi qatorni olishi kerak.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Server Actions must be async functions. "use server" fayldan sinxron funksiya yoki konstanta eksporti Yordamchilarni boshqa faylga
forma.get("ism") → null Inputda name yo'q yoki boshqa name="ism"
console.log brauzerda ko'rinmaydi Action serverda ishlaydi Terminalga qarang
redirect ishlamadi try/catch ichida chaqirilgan try dan keyin, oxirida
Saqlandi, lekin sahifa eski Kesh yangilanmagan updateTag / revalidatePath
JS'siz forma ishlamaydi Action client funksiyaga o'ralgan yoki onSubmit action={serverAction} to'g'ridan
Client faylda inline "use server" xatosi Inline faqat Server Component'da Alohida "use server" fayl
Production'da ENOENT/EROFS fayl xatosi Serverless'da diskka yozib bo'lmaydi Tashqi saqlash (email, baza)

9. Mashqlar

Mashqlar kurs/mashqlar/23/21-server-actions/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): bu qayerda ishlaydi?

  • "use server" funksiya ichidagi console.log — (terminalda)
  • <form action={…}> uchun brauzerdan ketadigan HTTP metod —
  • JS'siz formada action'ni aniqlaydigan narsa — yashirin
  • JS bilan yuborilganda action ID turadigan sarlavha —
Yechim
  • serverda — funksiya tanasi brauzerga umuman yuborilmaydi.
  • POST — joriy sahifa manziliga.
  • maydonlar — $ACTION_REF_1, $ACTION_KEY va boshqalar.
  • Next-Action — biz o'lchagan so'rovda 42 belgili ID.

2-mashq (o'rta): ikki tugma, ikki amal

Natija oynasidagi forma misolini o'zgartiring: bitta formada «Qoralama» va «Yuborish» tugmalari bo'lsin. «Qoralama» — formAction={saveDraft}: konsolga qoralama: <xabar> yozadi va ekranda "Qoralama saqlandi" chiqaradi. «Yuborish» — formaning asosiy action i. «Qoralama» ni bosib natijani ko'rsating.

Yechim
tsx
import { useState } from "react";

export default function App() {
  const [status, setStatus] = useState("");

  async function send(formData: FormData) {
    console.log("yuborildi:", formData.get("xabar"));
    setStatus("Yuborildi");
  }

  async function saveDraft(formData: FormData) {
    console.log("qoralama:", formData.get("xabar"));
    setStatus("Qoralama saqlandi");
  }

  return (
    <form action={send}>
      <label>
        Xabar <textarea name="xabar" />
      </label>
      <button type="submit" formAction={saveDraft}>
        Qoralama
      </button>
      <button type="submit">Yuborish</button>
      <p role="status">{status}</p>
    </form>
  );
}

Konsolda:

text
qoralama: Ertaga gaplashamiz

Ekranda:

text
Xabar QoralamaYuborish
Qoralama saqlandi

formAction formaning action ini shu tugma uchun almashtiradi. Next.js'da ikkala funksiya Server Action bo'ladi va ikkalasi ham JS'siz ishlaydi — tugmaning HTML formaction atributi orqali. Ekrandagi QoralamaYuborish — ikki tugma orasida bo'shliq yo'q; haqiqiy sahifada CSS ajratadi.

3-mashq (qiyin): bind va ishonchsiz argument

Post sahifasidagi izoh formasi: addComment(postSlug, formData). bind bilan postSlug beriladi. Funksiya serverda postSlug ni ruxsat etilgan ro'yxat (["portfolio-v2", "server-components"]) bo'yicha tekshirsin: noma'lum slug — { ok: false, xato: "post topilmadi" }. Izoh matni bo'sh bo'lsa — { ok: false, xato: "izoh bo'sh" }. To'g'ri bo'lsa — { ok: true }. Funksiyani FormData bilan uch holatda sinang (Node'da ham FormData bor).

Yechim
js
const POSTS = ["portfolio-v2", "server-components"];

async function addComment(postSlug, formData) {
  if (!POSTS.includes(postSlug)) {
    return { ok: false, xato: "post topilmadi" };
  }
  const text = String(formData.get("izoh") ?? "").trim();
  if (!text) return { ok: false, xato: "izoh bo'sh" };
  return { ok: true };
}

function form(text) {
  const fd = new FormData();
  fd.set("izoh", text);
  return fd;
}

const forPost = addComment.bind(null, "portfolio-v2");
console.log(await forPost(form("Zo'r maqola!")));
console.log(await forPost(form("   ")));

// Hujumchi yashirin maydondagi slug'ni o'zgartirdi:
const forged = addComment.bind(null, "../../etc/passwd");
console.log(await forged(form("salom")));

Konsolda:

text
{ ok: true }
{ ok: false, xato: "izoh bo'sh" }
{ ok: false, xato: 'post topilmadi' }

bind dagi qiymat JS'siz formada yashirin maydon bo'lib brauzerga boradi va qaytib keladi — uni o'zgartirish mumkin. Shuning uchun u ham FormData kabi tekshiriladi. Node konsolida apostrofli satr qo'shtirnoq bilan chiqadi ("izoh bo'sh"), qolganlari bittalik bilan.

4-mashq: Portfolio qadami

«Portfolio qadami» bo'limidagi to'rt qadamni bajaring. .data/xabarlar.jsonl da ikki qator (JS bilan va JS'siz) paydo bo'lishini tekshiring, git status da .data ko'rinmasligiga ishonch hosil qiling. Commit hali yo'q.

Yechim

Faylda har qator — {"ism":"…","email":"…","mavzu":"…","xabar":"…","vaqt":"2026-…"} (keyingi darsdagi sxemadan keyin hamma maydonlar to'ladi; hozirgi sodda variantda faqat siz o'qigan maydonlar). Network panelida JS'siz yuborishda Next-Action sarlavhasi bo'lmaydi, lekin so'rov tanasida $ACTION_… maydonlari bor — «Biz o'lchadik» bo'limidagidek.

10. Real ishda

  • Formalar — Server Actions'ning asosiy joyi: aloqa, ro'yxatdan o'tish, sharh, buyurtma. Next.js loyihalarida yangi formalar deyarli hammasi shunday yoziladi.
  • Admin panellar — CRUD amallari (yarat, tahrirla, o'chir) action'lar bilan, kesh — updateTag bilan.
  • Progressive enhancement — davlat xizmatlari, bank saytlari kabi "hamma uchun ishlashi shart" bo'lgan joylarda talab. Server Actions buni deyarli bepul beradi.
  • Intervyuda: "Server Action nima va u qanday ishlaydi?", "Server Action va Route Handler farqi?" (Route Handler, Server Action yoki alohida backend?).

Xulosa

  • Server Action — "use server" bilan belgilangan async funksiya; client'dan chaqiriladi, serverda ishlaydi.
  • Brauzerga funksiya kodi emas, action ID boradi; so'rov — joriy sahifaga POST (Next-Action sarlavhasi yoki yashirin maydonlar).
  • <form action> va formAction JS'siz ham ishlaydi — biz o'lchadik: JS o'chiq holatda ham "Rahmat, Malika!".
  • "use server" fayldan faqat async funksiyalar eksport qilinadi.
  • Mutatsiyadan keyin: kesh yangilash, keyin redirect — try dan tashqarida.
  • Kanon xabarni dev'da .data/ faylga, prod'da faqat shaxsiy ma'lumotsiz logga yozadi.

Keyingi dars: Server Actions II: holat, validatsiya va optimistik UI — Zod bilan tekshiramiz, xatolarni maydonlar yonida ko'rsatamiz va useOptimistic ning yashirin narxini o'lchaymiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Next.js Server Actions: "use server" va JavaScript'siz ishlaydigan forma — IlmHamroh