Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ikki xil env
- 2.1 Qoida
- 2.2 Biz ishga tushirdik: nima ichiga kirdi
- 3. .env fayllari
- 3.1 Yuklanish tartibi
- 3.2 Biz ishga tushirdik
- 3.3 Qaysi fayl git'ga tushadi
- 4. Tuzoq: server env ham "muzlaydi"
- 4.1 Kanon tajribasi
- 4.2 Nega?
- 4.3 "Bitta Docker image — ko'p muhit"
- 5. src/env.ts: Zod bilan bitta joyda
- 5.1 Muammo
- 5.2 Kanon fayli
- 5.3 Jonli: || undefined nega kerak
- 5.4 Noto'g'ri qiymat — build to'xtaydi
- 6. server-only: sir client'ga o'tmasin
- 6.1 Xavf
- 6.2 Biz ishga tushirdik
- 7. Boshqa yo'llar
- 7.1 @t3-oss/env-nextjs (hujjat bo'yicha)
- 7.2 Taint API (ko'rsatkich)
- 8. Portfolio qadami: src/env.ts va commit (holat 37)
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qayerda ko'rinadi?
- 2-mashq (o'rta): Telegram kanali uchun maydon
- 3-mashq (qiyin): deploy oldi tekshiruvi
- 4-mashq: Portfolio qadami
- 11. Real ishda
- Xulosa
- Manbalar
Next.js env o'zgaruvchilar va sirlar: NEXT_PUBLIC_, .env fayllari va Zod
Qisqacha: Next.js'da muhit o'zgaruvchilari ikki xil.
NEXT_PUBLIC_bilan boshlanadiganlari build vaqtida brauzer JS'iga matn bo'lib yoziladi — ularni hamma ko'radi va build'dan keyin o'zgartirib bo'lmaydi. Qolganlari faqat serverda (process.env) qoladi — sirlar shu yerda. Lekin statik sahifalarga server env ham build vaqtida "muzlab" qoladi: bitta build'ni turli muhitlarda ishlatsangiz, eski qiymat chiqadi. Qiymatlar.env*fayllaridan ma'lum tartibda yuklanadi. Hammasini bitta faylda (src/env.ts) Zod bilan tekshirish vaserver-onlybilan himoyalash — noto'g'ri sozlamani build vaqtida ushlaydi.
Bu darsda
NEXT_PUBLIC_va oddiy env farqini haqiqiy build chiqishi bilan ko'rsata olasiz..env,.env.local,.env.productionqaysi tartibda o'qilishini va qaysilari git'ga tushishini bilasiz.- "Bitta Docker image — ko'p muhit" tuzog'ini tushuntirib, unga qarshi yo'lni tanlay olasiz.
src/env.tsni Zod bilan yozasiz: noto'g'ri qiymat build'ni to'xtatadi.server-onlysirni client komponentga oqishdan qanday saqlashini ko'rasiz;portfolioholat 37 ni commit qilasiz.
Oldin bilishingiz kerak: Vite III: env o'zgaruvchilar va rejimlar, next.config chuqur, Zod chuqurroq, Server Components, Cache Components II: revalidatsiya.
1. Nega bu kerak?
portfolio da ikkita qiymat bor, ular kodga yozilmasligi kerak. Birinchisi — QAYTA_YANGILASH_SIRI: revalidatsiya darsida yozilgan /api/qayta-yangila marshrutining paroli. U GitHub'ga tushsa, har kim saytingiz keshini tozalay oladi. Ikkinchisi — SAYT_URL: canonical, sitemap va RSS uchun saytning to'liq manzili. Lokal'da bir xil, internetda boshqa.
Vite darsida bu muammoni ko'rgansiz: .env fayllari va VITE_ prefiksi. Brauzer kodiga faqat VITE_ bilan boshlangan qiymat kiradi, va u yerda sir saqlab bo'lmaydi. Next.js'da ham shunday, faqat bitta muhim farq bor: kodning bir qismi serverda ishlaydi. Qaysi env brauzerga o'tadi, qaysi biri serverda qoladi — shu darsning asosiy savoli. Uning javobi client chegarasi ("use client") bilan bevosita bog'liq.
O'xshatish: bank. Kassir oynasidagi e'lon (ish vaqti, valyuta kursi) — NEXT_PUBLIC_: hamma o'qiydi, va u bosilib osib qo'yilgan — o'zgartirish uchun yangisini bosish kerak. Seyf kodi — server env: faqat ichkarida, mijoz uni hech qachon ko'rmaydi.
2. Ikki xil env
2.1 Qoida
NEXT_PUBLIC_SALOM |
QAYTA_YANGILASH_SIRI |
|
|---|---|---|
| Server kodida o'qiladimi | ha | ha |
| Client komponentda | ha — matn sifatida | yo'q — undefined |
| Qachon o'qiladi | build vaqtida, JS'ga yoziladi | server ishlaganda |
| Sir uchunmi | hech qachon | ha |
Next build paytida client kodidagi har process.env.NEXT_PUBLIC_SALOM ni qiymatning o'zi bilan almashtiradi. Brauzerga process.env umuman bormaydi — faqat tayyor satr.
2.2 Biz ishga tushirdik: nima ichiga kirdi
Tekshirish uchun portfolio (holat 43 nusxasi) bosh sahifasiga vaqtincha client komponent qo'shdik:
"use client";
export function EnvSinov() {
return (
<p id="env-sinov">
{process.env.NEXT_PUBLIC_SALOM} |{" "}
{process.env.QAYTA_YANGILASH_SIRI ?? "sir yo'q"}
</p>
);
}Build — ikkala o'zgaruvchi bilan: NEXT_PUBLIC_SALOM=salom-build, QAYTA_YANGILASH_SIRI=sir-build-1234567890. Keyin .next/static (brauzerga boradigan JS) ichidan qidirdik:
static JS ichida 'salom-build': 1 fayl; 'sir-build': 0 fayl
,function(){return(0,o.jsxs)("p",{id:"env-sinov",children:["salom-build"," |"," ",n.default.env.QAYTA_YANGILASHIkkinchi qator — siqilgan JS'dan parcha. "salom-build" — tayyor satr, process.env emas. Sir esa JS'da yo'q: Next uni almashtirmadi, n.default.env.QAYTA_YANGILASH… brauzerda bo'sh obyektdan o'qiydi.
So'ng serverni boshqa qiymatlar bilan ishga tushirdik: NEXT_PUBLIC_SALOM=salom-start. Brauzerdagi matn:
brauzerda: salom-build | sir yo'qIkki xulosa. Birinchisi: sir brauzerga chiqmadi — "sir yo'q". Ikkinchisi: salom-start hech qanday ta'sir qilmadi. NEXT_PUBLIC_ qiymati build paytida JS faylga "bosib" qo'yilgan. Uni o'zgartirishning yagona yo'li — qayta build.
flowchart LR
E["env:<br/>NEXT_PUBLIC_SALOM=salom-build"] --> B["next build"]
B --> J["static JS:<br/>"salom-build""]
B --> S["server kodi:<br/>process.env.X"]
R["next start:<br/>NEXT_PUBLIC_SALOM=salom-start"] --> S
J --> BR["brauzer: salom-build"]Chap tomonda — build vaqti, o'ngda — ishga tushish vaqti. Brauzerga ketadigan JS faqat chap tomondan qiymat oladi.
Tekshirib ko'ring: Dasturchi Telegram bot tokenini
NEXT_PUBLIC_BOT_TOKENdeb nomladi, "client komponentda xabar yuboraman" dedi. Nima bo'ladi?
Javob
Token build vaqtida JS faylga matn bo'lib yoziladi. Saytni ochgan har kim DevTools → Sources'da uni o'qiy oladi va bot nomidan istagan xabarni yuboradi. Token faqat server env'da turishi va yuborish serverda (Server Action yoki Route Handler) bo'lishi kerak. NEXT_PUBLIC_ = "ochiq e'lon".
3. .env fayllari
3.1 Yuklanish tartibi
Next hujjati bo'yicha qiymat shu tartibda qidiriladi va birinchi topilgani olinadi:
process.env— terminal yoki platforma bergan haqiqiy muhit;.env.$(NODE_ENV).local— masalan.env.production.local;.env.local—NODE_ENV=testda o'qilmaydi;.env.$(NODE_ENV)—.env.developmentyoki.env.production;.env.
NODE_ENV — next dev da development, next build va next start da production, testlarda test. .env.local testda o'qilmasligi ataylab: testlar har kompyuterda bir xil natija bersin, sizning lokal sozlamangizga bog'liq bo'lmasin.
3.2 Biz ishga tushirdik
Uch fayl yaratdik, har birida boshqa SAYT_URL:
printf 'SAYT_URL=https://env.example.com\n' > .env
printf 'SAYT_URL=https://local.example.com\n' > .env.local
printf 'SAYT_URL=https://prod.example.com\n' > .env.production
npx next buildBuild boshidagi qatorlar:
▲ Next.js 16.4.0 (Turbopack)
- Environments: .env.local, .env.production, .env
✓ Running next.config.ts took 70msEnvironments: qatori qaysi fayllar topilganini ustunlik tartibida yozadi. Bosh sahifaning canonical havolasi:
<link rel="canonical" href="https://local.example.com"/>.env.local yutdi: u .env.production dan yuqori. Terminalda SAYT_URL=… npx next build desangiz — u hammasidan ustun bo'lardi (1-qadam).
3.3 Qaysi fayl git'ga tushadi
portfolio ning .gitignore i:
# env files (can opt-in for committing if needed)
.env*
!.env.exampleHamma .env* — git'dan tashqarida, faqat .env.example kiradi. Bu fayl — "qaysi o'zgaruvchilar kerak" degan namuna, qiymatsiz yoki xavfsiz qiymat bilan:
# Nusxa oling: cp .env.example .env.local (.env* git'ga tushmaydi)
# /api/qayta-yangila uchun sir (ixtiyoriy; bo'lmasa marshrut 404).
# Yaratish: node -e "console.log(crypto.randomUUID())"
QAYTA_YANGILASH_SIRI=
# Saytning to'liq manzili (canonical, sitemap, RSS). Statik
# sahifalarga build vaqtida yoziladi — o'zgarsa qayta build.
SAYT_URL=https://example.com
# Ixtiyoriy: GitHub API manzili (test/tajriba uchun)
GITHUB_API_URL=Kanondagi izoh qatorlari telefonga sig'ishi uchun bo'lindi. Yangi dasturchi loyihani klonlaydi, .env.example ni .env.local ga ko'chiradi va o'z qiymatlarini yozadi. Sir hech qachon repoga tushmaydi.
Diqqat:
.env.localni bir marta git'ga qo'shib, keyin o'chirsangiz ham, u tarixda qoladi. Sir oqdi deb hisoblang: yangisini yarating, eskisini bekor qiling (bunday sirni tarixdan tozalashni 32-qismda o'rganasiz).
4. Tuzoq: server env ham "muzlaydi"
4.1 Kanon tajribasi
"Sirlar serverda, server env esa ishga tushganda o'qiladi" — shunday deb o'ylash tabiiy. Lekin kanon boshqacha natija oldi. SAYT_URL — server env, NEXT_PUBLIC_ yo'q. Kanon uni ikki xil berdi:
- build:
SAYT_URL=https://build.example.uz - start:
SAYT_URL=https://run.example.uz
Natija (kanon xom chiqishi):
<link rel="canonical" href="https://build.example.uz"/>
<loc>https://build.example.uz/</loc>
<link>https://build.example.uz/blog</link>Canonical, sitemap va RSS — uchalasi ham build qiymatini ko'rsatdi. run.example.uz hech qayerda yo'q.
4.2 Nega?
Sahifalar statik (○). Ular build vaqtida tayyor HTML bo'lib yozilgan (Rendering strategiyalari). Manzil o'sha paytda o'qilgan va HTML ichiga tushgan. Server (next start) faqat tayyor faylni beradi — kodni qayta ishlatmaydi. Keshlangan ("use cache") marshrutlar (sitemap, RSS) ham shunday: keshlangan natija build vaqtidagi qiymat bilan.
Demak qoida kengroq: build vaqtida bajarilgan har kod o'sha paytdagi env'ni ko'radi — NEXT_PUBLIC_ bo'lsin, bo'lmasin.
4.3 "Bitta Docker image — ko'p muhit"
Katta jamoalarda odat: ilova bir marta yig'iladi (masalan Docker image — ilova va uning hamma fayllari bitta paketda; Docker darsida o'rganasiz), keyin o'sha paket avval test serverida, keyin production'da ishga tushiriladi. Faqat env farq qiladi. Next hujjati ham ogohlantiradi: bunday holatda NEXT_PUBLIC_ qiymatlari build paytidagi qiymat bilan "muzlab" qoladi. Kanon tajribasi ko'rsatdiki, statik sahifalardagi server env ham.
Uch yo'l:
| Yo'l | Qanday | Narxi |
|---|---|---|
| Har muhit — o'z build'i | test uchun alohida, prod uchun alohida build | build vaqti ×2; Vercel shunday qiladi |
| So'rov vaqtida o'qish | await connection() dan keyin process.env |
sahifa dinamik bo'ladi — keshsiz, sekinroq |
| Brauzerga API orqali | server /api/sozlama qaytaradi, client so'raydi |
qo'shimcha so'rov |
portfolio birinchi yo'lni tanladi: Vercel har muhit (Preview, Production) uchun alohida build qiladi, demak SAYT_URL har birida to'g'ri. Bu .env.example dagi izohda ham, README'da ham ochiq yozilgan: "o'zgarsa — qayta build".
Next 15 dagi serverRuntimeConfig va publicRuntimeConfig (eski "runtime sozlama" usuli) Next 16 da olib tashlangan — rasmiy yangilash hujjati o'rniga env va connection() ni tavsiya qiladi. Eski loyihada getConfig() ni ko'rsangiz — sababi shu.
Tekshirib ko'ring:
/haqimdasahifasi"use cache"bilan har soatda yangilanadi.GITHUB_API_URLni server qayta ishga tushganda o'zgartirdingiz. Yangi qiymat qachon ishlatiladi?
Javob
Keyingi qayta hisoblashda — kesh muddati tugab, funksiya yana bajarilganda (yoki revalidateTag bilan tozalanganda). O'shanda kod yangi server jarayonida ishlaydi va yangi process.env ni o'qiydi. Build vaqtidagi birinchi natija esa muddati tugaguncha beriladi. Ya'ni: keshlangan kod env'ni "keyingi hisoblashgacha" muzlatadi, statik sahifa — "keyingi build'gacha".
5. src/env.ts: Zod bilan bitta joyda
5.1 Muammo
process.env.SAYT_URL ni o'n joyda o'qisangiz, uch muammo bor. Turi string | undefined — har joyda tekshiruv kerak. Xato yozilgan qiymat (htps://…) jim o'tadi va sitemap buzuq chiqadi. Nom xatosi (SAYT_ULR) — undefined, TypeScript sezmaydi.
Yechim: env'ni bitta faylda o'qish, Zod bilan tekshirish va tayyor, tiplangan obyektni eksport qilish.
5.2 Kanon fayli
import "server-only";
import { z } from "zod";
// Server env (N-39): bir joyda, Zod bilan tekshiriladi.
// NEXT_PUBLIC_ prefiksli o'zgaruvchi YO'Q: brauzerga env kerak emas.
const SXEMA = z.object({
QAYTA_YANGILASH_SIRI: z
.string()
.min(16, "QAYTA_YANGILASH_SIRI kamida 16 belgi")
.optional(),
GITHUB_API_URL: z.url().optional(),
// DIQQAT: statik sahifalarga BUILD vaqtida yoziladi
SAYT_URL: z.url().default("https://example.com"),
});
export const env = SXEMA.parse({
QAYTA_YANGILASH_SIRI: process.env.QAYTA_YANGILASH_SIRI || undefined,
GITHUB_API_URL: process.env.GITHUB_API_URL || undefined,
SAYT_URL: process.env.SAYT_URL || undefined,
});Kanon izohlari qisqartirildi, kod — aynan (bitta qator 70 belgidan uzun). Loyihada process.env endi faqat shu faylda: boshqa hamma joy import { env } from "@/env" qiladi.
| Qism | Nima qiladi |
|---|---|
import "server-only" |
Bu faylni client komponent import qilsa — build xatosi |
.min(16, …) |
Sir qisqa bo'lsa — xato, o'zbekcha xabar bilan |
z.url() |
URL ekanini tekshiradi (Zod 4 dagi yuqori darajadagi format) |
.default(…) |
Berilmasa — namuna manzil |
|| undefined |
Bo'sh satrni "yo'q" ga aylantiradi |
5.3 Jonli: || undefined nega kerak
.env.example da QAYTA_YANGILASH_SIRI= — bo'sh. Uni nusxalagan dasturchida process.env.QAYTA_YANGILASH_SIRI qiymati "" bo'ladi, undefined emas. Zod uchun "" — "berilgan, lekin 16 belgidan qisqa". Natija oynasida ikkala variantni solishtiring:
import { z } from "zod";
const Schema = z.object({
QAYTA_YANGILASH_SIRI: z.string().min(16, "kamida 16 belgi").optional(),
SAYT_URL: z.url().default("https://example.com"),
});
// .env.local dan o'qilgandek: bo'sh qator — "" bo'ladi
const raw = { QAYTA_YANGILASH_SIRI: "", SAYT_URL: "" };
const direct = Schema.safeParse(raw);
console.log(direct.success);
console.log(direct.error?.issues.map((i) => i.path[0]).join(", "));
const cleaned = Schema.safeParse({
QAYTA_YANGILASH_SIRI: raw.QAYTA_YANGILASH_SIRI || undefined,
SAYT_URL: raw.SAYT_URL || undefined,
});
console.log(cleaned.success, cleaned.data?.SAYT_URL);Konsolda:
false
QAYTA_YANGILASH_SIRI, SAYT_URL
true https://example.comTo'g'ridan berilganda ikkala maydon yiqildi. Bo'sh sir 16 belgidan qisqa, bo'sh SAYT_URL esa URL emas. Sukut qiymati (.default()) faqat undefined da ishlaydi. || undefined dan keyin sir "berilmagan" (optional), SAYT_URL esa sukut qiymatini oldi. || — mantiqiy operatorlar darsidan: chap tomon "yolg'on" bo'lsa (bo'sh satr ham), o'ng tomon olinadi.
5.4 Noto'g'ri qiymat — build to'xtaydi
Kanon SAYT_URL=notaurl bilan build qildi. Biz qayta ishga tushirib to'liq chiqishni oldik (qisqartirilgan):
Error: Failed to collect configuration for /api/qayta-yangila
[cause]: Error [ZodError]: [
{
"code": "invalid_format",
"format": "url",
"path": [
"SAYT_URL"
],
"message": "Invalid URL"
}
]
at module evaluation (src\env.ts:16:26)
> Build error occurred
Error: Failed to collect page data for /api/qayta-yangilaexit 1 — build yiqildi. Tarjimasi: "/api/qayta-yangila sozlamasini yig'ib bo'lmadi; sababi — Zod xatosi: SAYT_URL maydonida noto'g'ri URL". Bu yaxshi xabar: noto'g'ri sozlama production'ga chiqmadi, deploy shu yerda to'xtadi. Zod'siz build o'tib ketardi, sitemap esa notaurl/blog kabi buzuq manzillar bilan chiqardi — va buni Google sizdan oldin sezardi.
Xato aynan /api/qayta-yangila da chiqdi, chunki build sahifalarni yig'ishda birinchi bo'lib shu faylga yetdi. Xato qayerda chiqishi muhim emas — env.ts ni import qilgan birinchi joyda.
6. server-only: sir client'ga o'tmasin
6.1 Xavf
env.ts da sir bor. Kimdir client komponentda import { env } from "@/env" yozsa nima bo'ladi? Client Components darsidan bilasiz: client komponent import qilgan hamma narsa brauzer JS'iga yig'iladi. Next process.env.QAYTA_YANGILASH_SIRI ni almashtirmaydi (prefiksi yo'q) — lekin xavf baribir bor: kod tuzilishi, sukut qiymatlar, xato xabarlari brauzerga ketadi. Va ertaga kimdir sirni modul darajasidagi o'zgarmasga ko'chirsa — u ham.
6.2 Biz ishga tushirdik
env.ts ning birinchi qatori — import "server-only". Sinov uchun client komponent yaratdik:
"use client";
import { env } from "@/env";
export function EnvSinov() {
return <p>{env.SAYT_URL}</p>;
}next build (chiqish qisqartirilgan, ranglar olib tashlangan):
> Build error occurred
Error: Turbopack build failed with 2 errors:
./src/env.ts:1:1
Error: You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
./src/env.ts:1:1
Error: 'server-only' cannot be imported from a Client Component module
> 1 | import "server-only";
It should only be used from a Server Component.Ikkinchi xabar aniq: "server-only client komponent modulidan import qilinishi mumkin emas. U faqat Server Component'dan ishlatilishi kerak". Birinchisi esa chalg'ituvchi: "siz uni Pages Router'da ishlatyapsiz" deydi — bizda Pages Router umuman yo'q. Next 16.4 bir xatoni ikki xil matn bilan aytdi. Saboq: build xabaridagi har gapga emas, import izi (Import traces) ga qarang. U yerda aniq zanjir bor edi: EnvSinov.tsx [Client Component Browser] → env.ts.
Kanonda server-only yana malumot/github.ts va boshqa server modullarida bor (Server Components darsida qo'shilgan). Qoida: sir yoki server resursiga tegadigan har modul — birinchi qatorda import "server-only".
7. Boshqa yo'llar
7.1 @t3-oss/env-nextjs (hujjat bo'yicha)
Ommabop kutubxona (2026-10 holatiga 0.13.11) — env.ts naqshining tayyor ko'rinishi. Server va client qismlarini alohida e'lon qiladi va client'da server o'zgaruvchisini o'qishga urinilsa, xato beradi:
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
server: {
QAYTA_YANGILASH_SIRI: z.string().min(16).optional(),
},
client: {
NEXT_PUBLIC_SAYT_NOMI: z.string(),
},
runtimeEnv: {
QAYTA_YANGILASH_SIRI: process.env.QAYTA_YANGILASH_SIRI,
NEXT_PUBLIC_SAYT_NOMI: process.env.NEXT_PUBLIC_SAYT_NOMI,
},
});runtimeEnv da har o'zgaruvchi qo'lda yoziladi — chunki Next NEXT_PUBLIC_ ni faqat to'liq yozilgan process.env.NEXT_PUBLIC_… ko'rinishida almashtiradi (process.env[nom] ishlamaydi). Kanonga kirmadi: portfolio da NEXT_PUBLIC_ umuman yo'q, uchta server o'zgaruvchisi uchun 15 qatorlik o'z faylimiz yetadi. Client env'i ko'p loyihada esa bu kutubxona vaqt tejaydi. Biz uni o'rnatib sinamadik.
7.2 Taint API (ko'rsatkich)
Next 16 yangilash hujjati yana bir himoyani tilga oladi: React'ning taint (dog' qo'yish) API'si — server qiymatini "client'ga o'tmasin" deb belgilash. U next.config.ts da taint sozlamasi bilan yoqiladi va hozircha eksperimental. Kanonda yo'q — server-only yetadi.
8. Portfolio qadami: src/env.ts va commit (holat 37)
Holat 37 ning uchinchi, oxirgi qismi:
| Fayl | O'zgarish |
|---|---|
src/env.ts |
Yangi: server-only + Zod, uch o'zgaruvchi |
src/malumot/sozlama.ts, github.ts, api/qayta-yangila/route.ts |
process.env o'rniga env |
.env.example |
Har o'zgaruvchi izoh bilan |
Endi process.env faqat ikki joyda qoldi: env.ts va standart NODE_ENV/NEXT_RUNTIME tekshiruvlari. NEXT_PUBLIC_ — yo'q: brauzerga hech qanday env kerak emas.
Kanon tekshiruvi (holat 37): lint 3 s, tip 3 s, build 9 s — hammasi toza; JS o'zgarmadi (bosh sahifa 161.7 KB gzip), axe 0, hydration 0.
Uch dars (instrumentation, next.config, env) — bitta commit:
git add -A
git commitCommit (kanon): Sozlama: instrumentation (JSON log), CSP va xavfsizlik sarlavhalari, v1 redirect'lari, env Zod bilan.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Client'da process.env.API_KEY — undefined |
Prefikssiz env brauzerga bormaydi | Sir bo'lsa — serverga ko'chiring; ochiq bo'lsa — NEXT_PUBLIC_ |
NEXT_PUBLIC_ qiymati o'zgarmayapti |
Build vaqtida yozilgan | Qayta build |
Prod'da eski SAYT_URL |
Statik sahifa build qiymatini saqlagan | Har muhit uchun build yoki connection() |
process.env[nom] client'da ishlamaydi |
Next faqat to'liq yozilgan nomni almashtiradi | process.env.NEXT_PUBLIC_X aynan |
Testda .env.local qiymati yo'q |
NODE_ENV=test da o'qilmaydi |
.env.test yoki test sozlamasida bering |
ZodError … "Invalid URL", build exit 1 |
Env noto'g'ri | Qiymatni tuzating — xato o'z vaqtida ushlandi |
'server-only' cannot be imported from a Client Component module |
Client komponent server modulini import qildi | Qiymatni Server Component'dan prop qilib bering |
Bo'sh QAYTA_YANGILASH_SIRI= Zod xatosi beradi |
"" — "berilgan" |
|| undefined |
10. Mashqlar
Mashqlar kurs/mashqlar/23/37-env/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): qayerda ko'rinadi?
NEXT_PUBLIC_SAYT_NOMIbrauzer JS'ida bormi?QAYTA_YANGILASH_SIRIbrauzer JS'ida bormi?NODE_ENV=testda o'qilmaydigan fayl —.env.- Git'ga tushadigan yagona env fayli —
.env. - Client komponent import qilsa build'ni to'xtatadigan paket —
Yechim
- ha — build vaqtida matn bo'lib yoziladi.
- yo'q — prefikssiz env faqat serverda.
- local — testlar hamma kompyuterda bir xil ishlashi uchun.
- example — namuna, sirsiz; qolgan
.env*—.gitignoreda. - server-only — birinchi qatorda
import "server-only".
2-mashq (o'rta): Telegram kanali uchun maydon
Kanon sxemasiga yangi maydon qo'shing: TELEGRAM_KANAL — ixtiyoriy, lekin berilsa @ bilan boshlanib, kamida 6 belgi bo'lsin (z.string().startsWith(…), .min(…); Zod). Xabar o'zbekcha bo'lsin. Uchta holatni sinang: berilmagan (""), "@ilm" va "@sizning_kanal".
Yechim
import { z } from "zod";
const Schema = z.object({
TELEGRAM_KANAL: z
.string()
.startsWith("@", "kanal nomi @ bilan boshlansin")
.min(6, "kamida 6 belgi")
.optional(),
});
function check(value: string) {
const r = Schema.safeParse({ TELEGRAM_KANAL: value || undefined });
const name = r.data?.TELEGRAM_KANAL ?? "berilmagan";
if (r.success) return `ok: ${name}`;
return `xato: ${r.error.issues.map((i) => i.message).join("; ")}`;
}
console.log(check(""));
console.log(check("@ilm"));
console.log(check("@sizning_kanal"));Konsolda:
ok: berilmagan
xato: kamida 6 belgi
ok: @sizning_kanal"@ilm" @ bilan boshlanadi, lekin 4 belgi — faqat bitta xato. Zod hamma tekshiruvni bajarib, hamma xatoni bir vaqtda qaytaradi; "ilm" desangiz, ikkala xabar ham chiqardi.
3-mashq (qiyin): deploy oldi tekshiruvi
Deploy'dan oldin ikkita muhitni solishtiradigan funksiya yozing: compareEnv(build, run). U NEXT_PUBLIC_ bilan boshlanadigan va qiymati farq qiladigan har kalit uchun "<kalit>: build qiymati qoladi (<qiymat>)" ogohlantirishini qaytarsin. Prefikssiz kalitlar uchun farq bo'lsa — "<kalit>: statik sahifalarda build qiymati (<qiymat>)". Sirlarni (nomida SIRI, TOKEN yoki KEY bor) qiymatsiz ko'rsating: "<kalit>: farq bor (qiymat yashirilgan)". Natijani alifbo tartibida chiqaring.
Yechim
const SECRET = /SIRI|TOKEN|KEY/;
function compareEnv(build, run) {
const all = [...Object.keys(build), ...Object.keys(run)];
const keys = [...new Set(all)];
return keys
.filter((key) => build[key] !== run[key])
.sort()
.map((key) => {
if (SECRET.test(key)) {
return `${key}: farq bor (qiymat yashirilgan)`;
}
if (key.startsWith("NEXT_PUBLIC_")) {
return `${key}: build qiymati qoladi (${build[key]})`;
}
const value = build[key];
return `${key}: statik sahifalarda build qiymati (${value})`;
});
}
const build = {
NEXT_PUBLIC_SAYT_NOMI: "Portfolio",
SAYT_URL: "https://build.example.uz",
QAYTA_YANGILASH_SIRI: "aaaaaaaaaaaaaaaa",
};
const run = {
NEXT_PUBLIC_SAYT_NOMI: "Aziz",
SAYT_URL: "https://run.example.uz",
QAYTA_YANGILASH_SIRI: "bbbbbbbbbbbbbbbb",
};
for (const line of compareEnv(build, run)) console.log(line);Konsolda:
NEXT_PUBLIC_SAYT_NOMI: build qiymati qoladi (Portfolio)
QAYTA_YANGILASH_SIRI: farq bor (qiymat yashirilgan)
SAYT_URL: statik sahifalarda build qiymati (https://build.example.uz)Uchinchi qator — kanon tajribasining aynan o'zi: SAYT_URL prefikssiz, lekin statik sahifada build qiymati qoladi. Sir esa hisobotda ham ko'rinmaydi — log va hisobot ham "sir yozilmaydigan joy" (Xatolar va instrumentation). Sir sahifalarga yozilmaydi, server uni har so'rovda o'qiydi — u uchun "build qiymati qoladi" xavfi yo'q.
4-mashq: Portfolio qadami
src/env.ts ni yarating (server-only + Zod, uch o'zgaruvchi), process.env ni boshqa fayllardan olib tashlang, .env.example ni izohlar bilan yangilang. Tekshiring: (1) SAYT_URL=notaurl npm run build — build yiqilishi kerak; (2) client komponentda @/env ni import qilib ko'ring — server-only xatosi; tekshirgach qaytaring. Keyin npm run lint, npm run tip, npm run build — toza bo'lsa, holat 37 ni commit qiling.
Yechim
Namuna — «src/env.ts: Zod bilan bitta joyda» bo'limidagi kanon fayli. process.env qolgan joylarni topish: git grep "process.env" src — natijada faqat env.ts, NODE_ENV va NEXT_RUNTIME qolishi kerak. (1) da ZodError va exit 1; (2) da ikki xabar va import izi. Commit xabari — kanondagidek yoki o'zingizniki, uch darsning o'zgarishini sanab.
11. Real ishda
- Sir oqishi — eng ko'p uchraydigan xavfsizlik hodisalaridan biri: GitHub'da ochiq qolgan
.env,NEXT_PUBLIC_ga qo'yilgan kalit. GitHub o'zi ham ma'lum token turlarini skanerlab, egasiga xabar beradi (secret scanning). - Platformalar (Vercel, Netlify, Railway) env'ni panelda muhit bo'yicha saqlaydi: Development, Preview, Production — har biri o'z qiymati bilan (Deploy I: Vercel).
- Intervyu savollari: "
NEXT_PUBLIC_qachon o'qiladi?", "Bitta Docker image'ni turli muhitlarda ishlatsak nima bo'ladi?", "Sirni client'ga oqishdan qanday himoya qilasiz?". - Env sxemasi (
env.ts+ Zod yoki t3-env) — zamonaviy Next loyihalarining deyarli standarti: ilova noto'g'ri sozlama bilan ishga tushmaydi.
Xulosa
NEXT_PUBLIC_qiymatlari build vaqtida JS'ga matn bo'lib yoziladi — ochiq va muzlagan; sir uchun emas.- Prefikssiz env faqat serverda; lekin statik va keshlangan sahifalarda u ham build qiymatini saqlaydi (kanon tajribasi).
.envtartibi:process.env→.env.$(NODE_ENV).local→.env.local→.env.$(NODE_ENV)→.env; git'ga faqat.env.example.src/env.ts+ Zod: bitta joy, tiplangan qiymat, noto'g'ri env — buildexit 1; bo'sh satr uchun|| undefined.import "server-only"sirli modulni client'ga o'tkazmaydi — build xatosi va import izi bilan.
Keyingi dars: Next.js performance — Lighthouse, brauzer yuklagan JS, prefetch narxi va template animatsiyasini nega olib tashlaganimiz.
Manbalar
- Next.js hujjati — Environment Variables (yuklanish tartibi,
NEXT_PUBLIC_, runtime env): https://nextjs.org/docs/app/guides/environment-variables (16.4.0 bilan kelgan nusxa, 2026-10-21 da o'qildi) - Next.js hujjati — Upgrading to version 16 (Runtime Configuration olib tashlangani, taint): https://nextjs.org/docs/app/guides/upgrading/version-16
@t3-oss/env-nextjs— https://env.t3.gg (npm: 0.13.11, 2026-10-21 holatiga)- Zod 4 hujjati — string formatlari (
z.url()): https://zod.dev/api - Kanon:
portfoliov2, holat 37 (src/env.ts,.env.example, build/start env tajribasi);NEXT_PUBLIC_inlining,.envtartibi,server-onlyva to'liq ZodError chiqishi — biz ishga tushirdik (next 16.4.0, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!