İçeriğe geç
XDD.baby Yazılım Öğrenme
Sözlük Hata rehberi 0/56

Blok 4 · Ders 29

Dosya sistemi, ortam değişkenleri ve gizli bilgiler

Veriyi dosyada kalıcı tutmak, ayarları ortam değişkenlerine taşımak ve şifreleri asla koda ya da yayına koymamak.

Bu dersin sonunda

  • node:fs/promises ile dosya okuyup yazabilir, klasör açıp içindekileri listeleyebilirsin
  • node:path ve import.meta.dirname ile, programı hangi klasörden başlatırsan başlat doğru dosyayı bulan yollar kurabilirsin
  • Express API'nin verisini bir JSON dosyasında kalıcı tutabilir; yarım kalan dosyaya ve aynı anda gelen isteklere karşı güvenli yazabilirsin
  • Ayarları process.env'den okuyabilir, .env dosyasını node --env-file ile yükleyip eksik ya da bozuk ayarı açık bir hatayla yakalayabilirsin
  • Gizli bilgileri .gitignore ile Git'ten, ayrı bir public klasörüyle internetten uzak tutabilir; sızan bir anahtarda ne yapacağını sırasıyla sayabilirsin
  • Geliştirme ve canlı ortam için ayarları ayırıp NODE_ENV ile programın canlıda farklı davranmasını sağlayabilirsin
Bu sayfada
  1. Bellek unutur, disk hatırlar
  2. node:fs/promises: dosya ve klasör
  3. Dosya yolları: node:path ve import.meta.dirname
  4. JSON dosyası: API'nin küçük veritabanı
  5. Ayarlar koddan çıksın: ortam değişkenleri
  6. .env dosyası ve node --env-file
  7. Ayarları tek yerde toplamak: ayarlar.js
  8. Gizli bilgiler: koda ve Git'e girmez
  9. Yayına giden klasörde gizli dosya kalmasın
  10. Geliştirme ve canlı ortam
  11. Görevler
  12. Kendini sına
  13. Kayıt noktası

Ders 27 ve 28'de yazdığın API'nin bir zayıflığı vardı: sunucu kapanınca bütün veri gidiyordu. Bu derste veriyi bir dosyaya yazıp kalıcı yapacaksın ve bunu yaparken çıkan iki gerçek tehlikeyi, yarım kalan dosyayı ve aynı anda gelen istekleri, kendi gözünle göreceksin. Sonra port ve şifre gibi ayarları koddan çıkarıp .env dosyasına taşıyacak, o dosyayı Git'ten ve internetten nasıl uzak tutacağını öğreneceksin. Dersin sonunda yeniden başlatılınca verisini unutmayan, ayarlarını ortamdan okuyan ve hiçbir şifresi koda ya da GitHub'a girmeyen bir API'n olacak.

Bellek unutur, disk hatırlar

Programının değişkenleri bilgisayarın belleğinde (RAM) durur. Program kapanınca bellekteki her şey silinir; Ders 27'deki şampiyon dizisi de bu yüzden kayboldu. Disk ise program kapansa da, bilgisayar kapansa da hatırlar. Bir bilgiyi programdan uzun yaşatmak istiyorsan onu diske, bir dosyaya yazarsın.

Ders 28'deki sampiyon-api projene dokunmuyoruz. Bu ders için yeni ve kısa bir proje kur: kalici-api. Kodu kısa tutuyoruz ki dikkatin dosyalarda ve ayarlarda kalsın; öğrendiklerini sonra Ders 28'deki API'ne taşıyabilirsin.

Terminal — PowerShell
PS C:\kod> mkdir kalici-apiPS C:\kod> cd kalici-apiPS C:\kod\kalici-api> npm init -y# mkdir'in yazdığı tabloyu ve npm init'in yazdığı package.json'u geçebilirsinPS C:\kod\kalici-api> npm install express added 65 packages, and audited 66 packages in 1s 28 packages are looking for funding  run `npm fund` for details found 0 vulnerabilities

Paket sayıları sende biraz farklı çıkabilir. Klasörü VS Code'da aç (File → Open Folder) ve Ders 28'deki gibi hemen .gitignore adında bir dosya oluşturup içine node_modules/ yaz; bu dosyayı ders boyunca büyüteceksin. Sonra package.json'da iki şeyi değiştir: "type": "commonjs" satırını "type": "module" yap (Ders 14), "scripts" kısmını da şöyle yaz:

package.json
  "scripts": {    "start": "node sunucu.js",    "dev": "node --watch sunucu.js"  },

npm run dev sunucuyu Ders 27'deki gibi --watch ile başlatacak. Bu dersteki bütün dosyalar C:\kod\kalici-api klasöründe.

node:fs/promises: dosya ve klasör

Ders 17'de oyunun kaydını readFile ve writeFile ile tek bir dosyaya yazmıştın. Gerçek programlar çoğu zaman klasörlerle de çalışır: her maç için ayrı bir dosya, bir kayıt klasörü, yedekler. dosya-deneme.js adında bir dosya aç ve yaz:

dosya-deneme.js
import { mkdir, writeFile, readdir, readFile } from "node:fs/promises" await mkdir("maclar", { recursive: true })await writeFile("maclar/mac-1.json", JSON.stringify({ sampiyon: "Ahri", sonuc: "zafer" }), "utf8")await writeFile("maclar/mac-2.json", JSON.stringify({ sampiyon: "Garen", sonuc: "yenilgi" }), "utf8") const adlar = await readdir("maclar")console.log(adlar) for (const ad of adlar) {    const metin = await readFile("maclar/" + ad, "utf8")    const mac = JSON.parse(metin)    console.log(`${ad}: ${mac.sampiyon}, ${mac.sonuc}`)}
Çıktı
[ 'mac-1.json', 'mac-2.json' ]mac-1.json: Ahri, zafermac-2.json: Garen, yenilgi

node dosya-deneme.js ile çalıştır. VS Code'un dosya listesinde maclar klasörü ve içinde iki dosya belirir. Programı ikinci kez çalıştır: aynı çıktı gelir, hata çıkmaz. Kullandığın dört fonksiyon ve birazdan işine yarayacak beşincisi:

FonksiyonNe yaparDikkat
readFile(dosya, "utf8")Dosyanın içini metin olarak okurDosya yoksa ENOENT hatası
writeFile(dosya, metin, "utf8")Metni dosyaya yazar; dosya varsa önce içini boşaltır, sonra yazarKlasörü yoksa ENOENT hatası
mkdir(klasor, { recursive: true })Klasör açar; iç içe klasörleri de açarrecursive yoksa, klasör zaten varken EEXIST hatası
readdir(klasor)Klasördeki dosya ve klasörlerin adlarını dizi olarak verirTam yol değil, yalnız ad döner
rename(eski, yeni)Dosyanın adını değiştirir, gerekirse taşırBirazdan çok işine yarayacak

{ recursive: true } iki iş görür: kayitlar/2026/ekim gibi iç içe klasörlerin hepsini tek seferde açar ve klasör zaten varsa hata vermez. Onu yazmasaydın ikinci çalıştırmada EEXIST: file already exists (dosya zaten var) hatası alırdın. E ile başlayan bu büyük harfli kodlar işletim sisteminden gelir: ENOENT "böyle bir şey yok", EEXIST "zaten var" demek.

readdir sana yalnız adları verir: mac-1.json, mac-2.json. Dosyayı okurken başına klasörü eklemen gerekir; bu yüzden "maclar/" + ad yazdık. Yalnız readFile(ad, "utf8") yazsaydın Node dosyayı maclar'ın içinde değil, terminalin bulunduğu klasörde arar ve ENOENT verirdi. Yolu metin birleştirerek kurmak çalışıyor ama kırılgan; bir sonraki bölümün konusu bu.

Sık hataError: EISDIR: illegal operation on a directory, read

Ne zaman çıkar? Dosya bekleyen bir fonksiyona klasör verince. EISDIR "error: is directory", yani "bu bir klasör" demek.

dosya-deneme.js
import { mkdir, readFile } from "node:fs/promises" await mkdir("maclar", { recursive: true })const metin = await readFile("maclar", "utf8")
Hata çıktısı
node:internal/fs/promises:1261    const bytesRead = (await PromisePrototypeThen(                       ^ Error: EISDIR: illegal operation on a directory, read    at async readFileHandle (node:internal/fs/promises:1261:24)    at async file:///C:/kod/kalici-api/dosya-deneme.js:4:15 {  errno: -4068,  code: 'EISDIR',  syscall: 'read'} Node.js v24.20.0

İlk üç satır Node'un kendi içinden geliyor; senin satırın at ile başlayan ikinci satırda: dosya-deneme.js:4. Ders 17'de gördüğün gibi süslü parantezdeki code alanı hatanın türünü söylüyor.

Nasıl düzelir? Yolun sonuna dosyanın adını ekle: readFile("maclar/mac-1.json", "utf8"). Klasörün içindekileri öğrenmek istiyorsan readFile değil readdir kullan.

Sık hataError: ENOENT: no such file or directory, open '…\kayitlar\mac-1.json'

Ne zaman çıkar? Var olmayan bir klasörün içine dosya yazmaya çalışınca. Ders 17'de aynı hatayı okurken görmüştün; bu kez yazarken çıkıyor.

dosya-deneme.js
import { writeFile } from "node:fs/promises" await writeFile("kayitlar/mac-1.json", "{}", "utf8")
Terminal — PowerShell
PS C:\kod\kalici-api> node dosya-deneme.jsnode:internal/fs/promises:1345  return new FileHandle(await PromisePrototypeThen(                        ^ Error: ENOENT: no such file or directory, open 'C:\kod\kalici-api\kayitlar\mac-1.json'    at async open (node:internal/fs/promises:1345:25)    at async writeFile (node:internal/fs/promises:1965:14)    at async file:///C:/kod/kalici-api/dosya-deneme.js:3:1 {  errno: -4058,  code: 'ENOENT',  syscall: 'open',  path: 'C:\\kod\\kalici-api\\kayitlar\\mac-1.json'} Node.js v24.20.0

writeFile dosyayı açar ama klasörü açmaz. kayitlar klasörü olmadığı için dosyanın açılacağı yer de yok.

Nasıl düzelir? Yazmadan önce klasörü aç: await mkdir("kayitlar", { recursive: true }). recursive sayesinde bu satırı her çalıştırmada güvenle tekrarlayabilirsin.

Dosya yolları: node:path ve import.meta.dirname

Dosya yolunu bir metin olarak yazıyoruz, ama yollar sıradan metinler değil. Windows klasörleri ters eğik çizgiyle (\) ayırır, internetteki sunucuların çoğunu çalıştıran Linux ise düz eğik çizgiyle (/). Bir de göreli yolların kime göre çözüldüğü sorusu var. Üç tuzağı sırayla görelim.

Ters eğik çizgi tuzağı

Ders 17'de "\n"'in metnin içinde yeni satır demek olduğunu görmüştün. Ters eğik çizgi metnin içinde özel bir işaret başlatır, bu yüzden Windows yolunu tırnak içine olduğu gibi yazmak bozulur. yol-deneme.js adında bir dosya aç:

yol-deneme.js
const yanlis = "C:\kod\kalici-api\notlar.json"const ciftCizgi = "C:\\kod\\kalici-api\\notlar.json"const egikCizgi = "C:/kod/kalici-api/notlar.json"console.log(yanlis)console.log(ciftCizgi)console.log(egikCizgi)
Çıktı
C:kodkalici-apiotlar.jsonC:\kod\kalici-api\notlar.jsonC:/kod/kalici-api/notlar.json

İlk satırda \k sessizce k oldu, \n ise yeni satıra dönüştü; ortada hata mesajı bile yok. İkinci satırda her \ iki kez yazıldı: \\ "gerçekten bir ters eğik çizgi" demek. Üçüncü satır düz eğik çizgi kullanıyor; Node, Windows'ta da /'yi anlar. Kendi kodunda yolları elle birleştirmek yerine bir sonraki bölümdeki aracı kullanacaksın.

path.join: parçaları doğru birleştirmek

Node'un node:path modülü yollarla çalışmak için yazılmış küçük fonksiyonlar verir. yol-deneme.js'in içini sil ve şunu yaz:

yol-deneme.js
import path from "node:path" console.log(path.join("maclar", "mac-1.json"))console.log(path.join("C:/kod/kalici-api", "maclar/", "mac-1.json"))console.log(path.join("maclar", "..", "veri.json"))console.log(path.extname("mac-1.json"))console.log(path.basename("C:\\kod\\kalici-api\\veri.json"))
Çıktı
maclar\mac-1.jsonC:\kod\kalici-api\maclar\mac-1.jsonveri.json.jsonveri.json
  • path.join(...) parçaları işletim sisteminin ayırıcısıyla birleştirir. Windows'ta \ koydu; aynı kod Linux'ta maclar/mac-1.json üretir. İkinci satırda fazladan /'yi temizledi, eğik çizgileri de Windows'unkine çevirdi.
  • .. "bir üst klasör" demek: maclar'a girip geri çıktı, geriye veri.json kaldı.
  • path.extname uzantıyı, path.basename yolun son parçasını, yani dosyanın adını verir.

Kodun bir gün internetteki bir Linux sunucusunda çalışacak. Yolları "maclar\\" + ad diye elle yazarsan orada bozulur; path.join(...) iki yerde de doğru yolu kurar.

Göreli yol kime göre?

Ders 17'nin sonunda bir tuzak görmüştün: oyunu bir üst klasörden başlatınca kayıt dosyası proje klasörüne değil, başka bir yere yazılmıştı. Sebebi, "kayit.json" gibi göreli yolların programın dosyasına göre değil, terminalin o an bulunduğu klasöre göre çözülmesi.

Farkı gözünle gör. nerede.js adında bir dosya aç, yaz ve iki farklı klasörden çalıştır:

nerede.js — kalici-api — Visual Studio Code
nerede.js
import path from "node:path" console.log("Çalışma klasörü:", process.cwd())console.log("Dosyanın klasörü:", import.meta.dirname)console.log("Göreli yol:", path.resolve("veri.json"))console.log("Sabit yol:", path.join(import.meta.dirname, "veri.json"))
PROBLEMSOUTPUTDEBUG CONSOLETERMINAL
PS C:\kod\kalici-api> node nerede.jsÇalışma klasörü: C:\kod\kalici-apiDosyanın klasörü: C:\kod\kalici-apiGöreli yol: C:\kod\kalici-api\veri.jsonSabit yol: C:\kod\kalici-api\veri.jsonPS C:\kod\kalici-api> cd ..PS C:\kod> node kalici-api\nerede.jsÇalışma klasörü: C:\kodDosyanın klasörü: C:\kod\kalici-apiGöreli yol: C:\kod\veri.jsonSabit yol: C:\kod\kalici-api\veri.json
Ln 1, Col 1Spaces: 4UTF-8JavaScript

İkinci çalıştırmada göreli yol C:\kod\veri.json'a kaydı; sabit yol yerinden oynamadı. Denemeden sonra cd kalici-api ile proje klasörüne dön.

  • process Node'un, çalışan programı anlatan hazır objesi. process.cwd() çalışma klasörünü verir.
  • import.meta.dirname bu kodun yazılı olduğu dosyanın klasörü. Programı nereden başlatırsan başlat değişmez.
  • path.resolve("veri.json") göreli bir yolu çalışma klasörüne göre tam yola çevirir. Çıktıda göreli yolun nereye gittiğini bu yüzden görebildik.
Diskte C:\kod ├ veri.json └ kalici-api ├ nerede.js └ veri.json "veri.json" (göreli) terminal C:\kod'dayken Node C:\kod\veri.json'u açar import.meta.dirname ile terminal nerede olursa olsun dosyanın yanındaki veri.json
Şekil 29.1 Göreli "veri.json" terminalin klasörüne göre çözülür ve yanlış yere gidebilir. import.meta.dirname ile kurulan yol hep dosyanın yanındaki veri.json'u gösterir.

Kural: programın kendi dosyalarını (veri, şablon, sayfa klasörü) her zaman path.join(import.meta.dirname, ...) ile bul. Böylece sunucuyu VS Code'un terminalinden de, başka bir klasörden de, canlı sunucudaki bir başlatıcıdan da çalıştırsan aynı dosyaya ulaşır. Ders 28'deki express.static("public") da bu yüzden kırılgandı; birazdan onu da düzelteceksin.

Sık hataReferenceError: __dirname is not defined in ES module scope

Ne zaman çıkar? İnternetten kopyaladığın eski bir kodda __dirname geçiyorsa. __dirname, CommonJS döneminin import.meta.dirname'iydi; ES modüllerinde yok.

eski-kod.js
import path from "node:path" const dosya = path.join(__dirname, "veri.json")console.log(dosya)
Terminal — PowerShell
PS C:\kod\kalici-api> node eski-kod.jsfile:///C:/kod/kalici-api/eski-kod.js:3const dosya = path.join(__dirname, "veri.json")                        ^ ReferenceError: __dirname is not defined in ES module scopeThis file is being treated as an ES module because it has a '.js' file extension and '\\?\C:\kod\kalici-api\package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.    at file:///C:/kod/kalici-api/eski-kod.js:3:25    ... Node.js v24.20.0

İkinci satır, dosyanın neden ES modülü sayıldığını açıklıyor: package.json'da "type": "module" yazıyor.

Nasıl düzelir? __dirname yerine import.meta.dirname yaz. Bazı kaynaklarda fileURLToPath(import.meta.url) ile başlayan iki üç satırlık bir kalıp görürsün; import.meta.dirname Node 20.11'den önce yoktu, o kalıp onun eski karşılığı.

JSON dosyası: API'nin küçük veritabanı

Şimdi asıl işe geçiyoruz: API'nin verisini bir dosyada tutmak. Plan sade. Bütün veri veri.json adlı tek bir dosyada duracak; her istek dosyayı okuyacak, gerekiyorsa değiştirip geri yazacak:

veri.json
{  "sonId": 2,  "sampiyonlar": [    { "id": 1, "isim": "Ahri" },    { "id": 2, "isim": "Lux" }  ]}

sonId'yi Ders 28'den tanıyorsun: verilen son numara. sampiyonlar.length + 1 silmeden sonra aynı numarayı iki kez veriyordu; sonId bunu önlüyordu. Orada bellekteki bir değişkendi ve yeniden başlatınca 3'e dönüyordu. Şimdi verinin yanında, dosyada duruyor; o da kalıcı olacak.

Okuyan ve yazan iki fonksiyon

Dosya işlerini sunucudan ayrı bir modülde topla. depo.js adında bir dosya aç:

depo.js — kalici-api — Visual Studio Code
depo.js
import { readFile, writeFile } from "node:fs/promises"import path from "node:path" const DOSYA = path.join(import.meta.dirname, "veri.json") export async function oku() {    try {        const metin = await readFile(DOSYA, "utf8")        return JSON.parse(metin)    } catch (hata) {        if (hata.code === "ENOENT") {            return { sonId: 0, sampiyonlar: [] }        }        throw hata    }} export async function yaz(veri) {    await writeFile(DOSYA, JSON.stringify(veri, null, 2), "utf8")}
Ln 1, Col 1Spaces: 4UTF-8JavaScript
  • DOSYA bir önceki bölümün kuralına uyuyor: sunucu nereden başlarsa başlasın aynı veri.json.
  • oku Ders 17'deki kalıp: dosya yoksa (ENOENT) bu bir hata değil, "henüz veri yok" demek; boş bir başlangıç verisi döner. Başka bir hatayı ne yapacağını bilmediği için yeniden fırlatır.
  • yaz objeyi okunaklı JSON'a çevirip dosyanın üstüne yazar.

Şimdi sunucu.js'i yaz. Ders 28'deki API'nin iki uç noktası yeter. app.listen'ı Ders 28'deki gibi hata parametresiyle yazıyoruz:

sunucu.js — kalici-api — Visual Studio Code
sunucu.js
import express from "express"import { oku, yaz } from "./depo.js" const app = express()const port = 3000 app.use(express.json()) app.get("/api/sampiyonlar", async (req, res) => {    const veri = await oku()    res.json(veri.sampiyonlar)}) app.post("/api/sampiyonlar", async (req, res) => {    const isim = req.body?.isim    if (typeof isim !== "string" || isim.trim() === "") {        return res.status(400).json({ hata: "isim alanı gerekli" })    }    const veri = await oku()    veri.sonId = veri.sonId + 1    const yeni = { id: veri.sonId, isim: isim.trim() }    veri.sampiyonlar.push(yeni)    await yaz(veri)    res.status(201).json(yeni)}) app.listen(port, (hata) => {    if (hata) {        throw hata    }    console.log(`Sunucu çalışıyor: http://localhost:${port}`)})
PROBLEMSOUTPUTDEBUG CONSOLETERMINAL
PS C:\kod\kalici-api> npm run dev > kalici-api@1.0.0 dev> node --watch sunucu.js Sunucu çalışıyor: http://localhost:3000
Ln 1, Col 1Spaces: 4UTF-8JavaScript

POST route'u üç adımda çalışıyor: dosyayı oku, bellekteki kopyayı değiştir, dosyaya geri yaz. Dene: ikinci bir terminal aç, ahri.json ({ "isim": "Ahri" }) ve lux.json ({ "isim": "Lux" }) dosyalarını oluştur ve gönder:

İkinci terminal
PS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar[]PS C:\kod\kalici-api> curl.exe --json "@ahri.json" http://localhost:3000/api/sampiyonlar{"id":1,"isim":"Ahri"}PS C:\kod\kalici-api> curl.exe --json "@lux.json" http://localhost:3000/api/sampiyonlar{"id":2,"isim":"Lux"}

İlk istekte dosya yoktu; oku boş listeyi döndürdü. İlk POST'tan sonra VS Code'un dosya listesinde veri.json belirdi; ikinci POST'tan sonra içi bölümün başındaki gibi oldu (yalnız JSON.stringify her alanı ayrı satıra yazar). Şimdi asıl denemeyi yap: birinci terminalde Ctrl+C ile sunucuyu durdur, npm run dev ile yeniden başlat ve listeyi iste:

İkinci terminal
PS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar[{"id":1,"isim":"Ahri"},{"id":2,"isim":"Lux"}]

Ahri ve Lux yerinde. Sunucu yeniden başladı ama veri diskte bekliyordu. API'n artık kalıcı. Yine de bu hâliyle iki ciddi tehlikesi var; ikisini de kendin tetikleyeceksin.

Birinci tehlike: yarım kalan dosya

Tablodaki notu hatırla: writeFile dosyanın içini önce boşaltır, sonra yeni metni parça parça yazar. Bu iki an arasında program ölürse ne olur? Elektrik kesilebilir, bilgisayar donabilir, sunucu bir hatayla çökebilir. Bunu taklit eden küçük bir program yaz. Önce verinin yedeğini al, çünkü bu deneme onu silecek. PowerShell'in Copy-Item kaynak hedef komutu bir dosyayı kopyalar:

İkinci terminal
PS C:\kod\kalici-api> Copy-Item veri.json veri-yedek.json

cokme.js adında bir dosya aç:

cokme.js
import { yaz } from "./depo.js" const sampiyonlar = []for (let i = 1; i <= 200000; i++) {    sampiyonlar.push({ id: i, isim: "Şampiyon " + i })} yaz({ sonId: 200000, sampiyonlar: sampiyonlar })setTimeout(() => {    console.log("Elektrik kesildi!")    process.exit(1)}, 3)

Program 200.000 şampiyonluk büyük bir veriyi yazmaya başlıyor ve 3 milisaniye sonra kendini kapatıyor. process.exit(1) programı o anda, yarım işlerini bitirmeden durdurur; kesintiyi böyle taklit ediyoruz. yaz'ın önüne bilerek await koymadık: yazma sürerken saatin işlemesini istiyoruz. Sunucu çalışırken ikinci terminalde dene:

İkinci terminal
PS C:\kod\kalici-api> node cokme.jsElektrik kesildi!PS C:\kod\kalici-api> (Get-Item veri.json).Length0PS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Error</title></head><body><pre>SyntaxError: Unexpected end of JSON input<br> &nbsp; &nbsp;at JSON.parse (&lt;anonymous&gt;)<br> &nbsp; &nbsp;at oku (file:///C:/kod/kalici-api/depo.js:9:21)<br> &nbsp; &nbsp;at async file:///C:/kod/kalici-api/sunucu.js:10:18</pre></body></html>

(Get-Item veri.json).Length dosyanın bayt cinsinden boyutunu verir: 0. Yeni veri yazılamadan program öldü, eski veri ise çoktan silinmişti; Ahri ve Lux gitti. Sende boyut 0 yerine yarım bir sayı da çıkabilir; hangi anda kesildiğine bağlı. API artık her istekte 500 dönüyor ve birinci terminalde aynı hata yazıyor:

Sunucunun terminali
SyntaxError: Unexpected end of JSON input    at JSON.parse (<anonymous>)    at oku (file:///C:/kod/kalici-api/depo.js:9:21)    at async file:///C:/kod/kalici-api/sunucu.js:10:18

Express hatayı yakaladı, sunucu çökmedi; ama dosya bozuk kaldıkça hiçbir istek başarılı olamaz. Yanıttaki sayfanın yığın izini, dosya yollarını ve satır numaralarını herkese göstermesi Express'in hazır hata sayfasından; bunu dersin sonunda, canlı ortam bölümünde yeniden ele alacağız. Şimdilik yedeği geri koy:

İkinci terminal
PS C:\kod\kalici-api> Copy-Item veri-yedek.json veri.json
Sık hataSyntaxError: Unexpected end of JSON input

Ne zaman çıkar? JSON.parse'a boş bir metin verince: içi boşalmış bir dosya, yarıda kesilmiş bir yazma, elle boşaltılmış bir kayıt. "JSON girdisi beklenmedik yerde bitti" demek.

bos.js
import { writeFile, readFile } from "node:fs/promises" await writeFile("bos.json", "", "utf8")const metin = await readFile("bos.json", "utf8")console.log("Okunan karakter sayısı:", metin.length)const veri = JSON.parse(metin)
Hata çıktısı
Okunan karakter sayısı: 0<anonymous_script>:1  SyntaxError: Unexpected end of JSON input    at JSON.parse (<anonymous>)    at file:///C:/kod/kalici-api/bos.js:6:19 Node.js v24.20.0

Dosya vardı, okundu, ama içi boştu. readFile hata vermedi; hata bir satır sonra, metni çözmeye çalışınca çıktı.

Nasıl düzelir? Önce sebebi bul: dosyayı kim, ne zaman boşalttı? Yedeğin varsa geri koy. Kalıcı çözüm, dosyanın hiçbir zaman yarım kalmayacağı bir yazma yöntemi: hemen aşağıda.

Geçici dosya ve rename

Çözümün fikri şu: asıl dosyaya hiç dokunmadan yeni veriyi başka bir dosyaya yaz, yazma bitince adını değiştir. Yeniden adlandırmak aynı diskte tek hamlede olur: bir an önce veri.json eski veriyi gösterir, bir an sonra yenisini. Arada yarım bir hâli yoktur.

depo.js'te iki yeri değiştir: ilk satırdaki import'a rename'i ekle ve yaz'ı yeniden yaz:

depo.js
import { readFile, writeFile, rename } from "node:fs/promises"import path from "node:path" const DOSYA = path.join(import.meta.dirname, "veri.json") export async function oku() {    try {        const metin = await readFile(DOSYA, "utf8")        return JSON.parse(metin)    } catch (hata) {        if (hata.code === "ENOENT") {            return { sonId: 0, sampiyonlar: [] }        }        throw hata    }} export async function yaz(veri) {    const gecici = DOSYA + ".tmp"    await writeFile(gecici, JSON.stringify(veri, null, 2), "utf8")    await rename(gecici, DOSYA)}

Yeni veri önce veri.json.tmp'ye yazılıyor. Yazma bütünüyle bitince rename onu veri.json'un yerine koyuyor; eski dosya o anda yenisiyle değişiyor. Kesinti yazma sırasında olursa yarım kalan şey .tmp dosyasıdır, asıl veri sağlam kalır. Kaydet (--watch sunucuyu yeniden başlatır, çünkü depo.js onun içe aktardığı bir dosya) ve aynı çöküşü yeniden dene:

İkinci terminal
PS C:\kod\kalici-api> node cokme.jsElektrik kesildi!PS C:\kod\kalici-api> (Get-Item veri.json).Length136PS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar[{"id":1,"isim":"Ahri"},{"id":2,"isim":"Lux"}]

Program yine yarıda öldü, ama veri.json 136 baytlık eski hâliyle yerinde; API çalışmayı sürdürüyor. Klasörde içi boş ya da yarım bir veri.json.tmp kalmış olabilir. Zararsızdır, bir sonraki başarılı yazma onu ezer; Git'e girmemesini dersin ilerisinde ayarlayacağız.

Doğrudan writeFile veri.json eski veri: Ahri, Lux 1. önce boşaltılır veri.json: 0 bayt veri.json boş Ahri ve Lux gitti Geçici dosya + rename veri.json eski veri: Ahri, Lux 1. yeni veri .tmp'ye veri.json olduğu gibi veri.json sağlam eski veri yerinde kesinti Kesinti olmasaydı 2. adım: rename, .tmp'yi tek hamlede veri.json yapardı.
Şekil 29.2 Doğrudan yazmada kesinti anı dosyayı boş bırakır. Geçici dosyaya yazıp rename ile değiştirmede asıl dosya ya eski ya yeni hâlindedir, hiçbir zaman yarım değil.

İkinci tehlike: aynı anda gelen istekler

Bir sunucu istekleri sırayla değil, gelir gelmez karşılar. İki istek aynı anda gelirse ikisi de dosyayı okur, ikisi de kendi kopyasını değiştirir, ikisi de yazar. Bunu da taklit et: kalabalik.js, on şampiyonu aynı anda eklemeye çalışan bir program. Ders 15'teki Promise.all istekleri aynı anda başlatır, Ders 26'daki ayar objesi de POST gönderir:

kalabalik.js
const adres = "http://localhost:3000/api/sampiyonlar"const isimler = ["Jinx", "Garen", "Teemo", "Annie", "Yasuo", "Zed", "Vi", "Sona", "Thresh", "Brand"] function ekle(isim) {    return fetch(adres, {        method: "POST",        headers: { "Content-Type": "application/json" },        body: JSON.stringify({ isim: isim })    })} const yanitlar = await Promise.all(isimler.map(ekle))console.log("Durum kodları:", yanitlar.map((y) => y.status).join(" ")) const listeYaniti = await fetch(adres)const liste = await listeYaniti.json()console.log("Listede", liste.length, "şampiyon var")

Listede Ahri ve Lux var; on ekleme başarılı olursa 12 olmalı:

İkinci terminal
PS C:\kod\kalici-api> node kalabalik.jsDurum kodları: 201 201 201 201 500 500 500 500 500 500Listede 3 şampiyon var

On istekten dördü 201 Created aldı, yani dört kişiye "eklendi" denildi; ama listede yalnız bir yeni şampiyon var. Altısı ise 500 aldı. Sende sayılar her çalıştırmada değişir; bazen son satır bile hata verir, çünkü liste isteği de o anda kaybolmuş ya da bozulmuş bir dosyaya çarpabilir. Sunucunun terminali neler olduğunu söylüyor:

Sunucunun terminali
Error: EPERM: operation not permitted, rename 'C:\kod\kalici-api\veri.json.tmp' -> 'C:\kod\kalici-api\veri.json'    at async rename (node:internal/fs/promises:1490:10)    at async yaz (file:///C:/kod/kalici-api/depo.js:21:5)    at async file:///C:/kod/kalici-api/sunucu.js:23:5 {  errno: -4048,  code: 'EPERM',  syscall: 'rename',  path: 'C:\\kod\\kalici-api\\veri.json.tmp',  dest: 'C:\\kod\\kalici-api\\veri.json'}

İki ayrı sorun üst üste biniyor:

  • Ezilen değişiklik: İstekler dosyayı neredeyse aynı anda okudu ve hepsi aynı eski listeyi gördü. Her biri listeye kendi şampiyonunu ekleyip dosyaya yazdı; sonra yazan, öncekinin eklediğini görmeden üstüne yazdı. 201 alan dört şampiyondan üçü böyle kayboldu, hem de hiçbir hata mesajı olmadan.
  • Aynı geçici dosya: Bütün istekler aynı veri.json.tmp'ye yazıp onu taşımaya çalıştı. Biri taşırken öteki aynı dosyaya yazıyordu; Windows izin vermedi (EPERM). Bazen de dosya çoktan taşınmış olduğu için ENOENT çıktı.

Böyle bir hata, sen tek başına denerken neredeyse hiç görünmez: istekler sırayla gelir, her şey çalışır. Gerçek kullanıcılar gelince ortaya çıkar. Denemeden sonra yedeği geri koy: Copy-Item veri-yedek.json veri.json.

Yazmaları sıraya koymak

Çözüm, "oku, değiştir, yaz" işini bir anda tek bir isteğin yapması. Diğerleri sırada bekler. depo.js'in sonuna şunu ekle:

depo.js
let sira = Promise.resolve() export function degistir(islem) {    const sonuc = sira.then(async () => {        const veri = await oku()        const cevap = islem(veri)        await yaz(veri)        return cevap    })    sira = sonuc.catch(() => {})    return sonuc}
  • sira, kuyruğun sonunu tutan bir Promise. Başta Promise.resolve(): hemen tamamlanmış, "kuyruk boş" demek.
  • Her degistir çağrısı kendi işini sira.then(...) ile kuyruğun sonuna ekler. then öncekinin bitmesini bekler; böylece işler aynı anda değil, birbiri ardına çalışır.
  • İş, verilen islem fonksiyonuna veriyi verir, onun döndürdüğünü (cevap) saklar, dosyayı yazar ve cevabı döndürür.
  • sira = sonuc.catch(() => {}): kuyruğun yeni sonu bu iş olur. catch boş bir fonksiyonla hatayı yutar ki bir iş hata verirse sıradakiler sonsuza dek takılmasın. Hatanın kendisi kaybolmaz: return sonuc onu degistir'i çağıran route'a ulaştırır.

sunucu.js'te ikinci satırı import { oku, degistir } from "./depo.js" yap ve POST route'unun son kısmını değiştir:

sunucu.js
app.post("/api/sampiyonlar", async (req, res) => {    const isim = req.body?.isim    if (typeof isim !== "string" || isim.trim() === "") {        return res.status(400).json({ hata: "isim alanı gerekli" })    }    const yeni = await degistir((veri) => {        veri.sonId = veri.sonId + 1        const sampiyon = { id: veri.sonId, isim: isim.trim() }        veri.sampiyonlar.push(sampiyon)        return sampiyon    })    res.status(201).json(yeni)})

Route artık dosyaya kendisi dokunmuyor; "listeye şunu ekle" diyen küçük bir fonksiyonu kuyruğa veriyor. Kaydet ve kalabalığı yeniden gönder:

İkinci terminal
PS C:\kod\kalici-api> node kalabalik.jsDurum kodları: 201 201 201 201 201 201 201 201 201 201Listede 12 şampiyon var

On istek, on ekleme, eksiksiz. Kaç kez çalıştırırsan çalıştır sonuç aynı; yalnız şampiyonların sırası, isteklerin hangisinin önce vardığına göre değişebilir. Listeyi kısa tutmak için yedeği yine geri koy.

Ayarlar koddan çıksın: ortam değişkenleri

sunucu.js'e bir daha bak: const port = 3000 kodun içinde yazıyor. Kendi bilgisayarında sorun yok; ama Ders 32'de API'ni bir barındırıcıya koyduğunda hangi portu dinleyeceğine barındırıcı karar verecek. Birazdan bir de şifre gerekecek: bütün listeyi silen tehlikeli bir route'u yalnız yöneticinin çağırabilmesi için bir anahtar. O anahtarı koda yazarsan kodla birlikte Git'e, GitHub'a ve kodu gören herkese gider.

Çözüm, kodu her yerde aynı bırakıp değişen değerleri programa dışarıdan vermek. Program başlarken işletim sistemi ona bir ayar listesi verir; bu listedeki her ada ortam değişkeni denir.

process objesini çalışma klasörü bölümünden tanıyorsun. process.env onun içinde; programın gördüğü bütün ortam değişkenlerini tutan bir obje. ortam.js adında bir dosya aç:

ortam.js
console.log("İşletim sistemi:", process.env.OS)console.log("Port:", process.env.PORT)console.log("Anahtar:", process.env.YONETICI_ANAHTARI)
Çıktı
İşletim sistemi: Windows_NTPort: undefinedAnahtar: undefined

OS Windows'un kendi koyduğu bir değişken; her Windows'ta Windows_NT'dir. PORT ve YONETICI_ANAHTARI'nı henüz kimse tanımlamadı; objede olmayan bir anahtar gibi undefined döndü (Ders 4). PowerShell'de bir ortam değişkenini $env:AD = "değer" ile tanımlarsın. Aynı terminalde dene:

Terminal — PowerShell
PS C:\kod\kalici-api> $env:PORT = "4000"PS C:\kod\kalici-api> node ortam.jsİşletim sistemi: Windows_NTPort: 4000Anahtar: undefined

Kodu değiştirmedin, çıktı değişti. $env: ile tanımlanan değişken yalnız o terminalde ve oradan başlatılan programlarda yaşar. Terminali kapatınca gider; VS Code'da açtığın başka bir terminal onu hiç görmez. Denemeden sonra bu terminali kapat (terminal listesindeki çöp kutusu simgesi) ve yenisini aç ki PORT sonraki denemelere karışmasın.

Sık hataTypeError: Cannot read properties of undefined (reading 'slice')

Ne zaman çıkar? Tanımlanmamış bir ortam değişkenini metin sanıp kullanınca. process.env.X yoksa hata vermez, sessizce undefined döner; hata ancak değeri kullanırken çıkar.

anahtar.js
const anahtar = process.env.YONETICI_ANAHTARIconsole.log("Anahtarın ilk 4 harfi:", anahtar.slice(0, 4))
Hata çıktısı
file:///C:/kod/kalici-api/anahtar.js:2console.log("Anahtarın ilk 4 harfi:", anahtar.slice(0, 4))                                              ^ TypeError: Cannot read properties of undefined (reading 'slice')    at file:///C:/kod/kalici-api/anahtar.js:2:47    ... Node.js v24.20.0

Nasıl düzelir? Önce değişken gerçekten tanımlı mı bak: console.log(process.env.YONETICI_ANAHTARI). undefined görüyorsan ya hiç tanımlamadın ya adını farklı yazdın ya da programı .env dosyasını yüklemeden başlattın (birazdan göreceksin). Kalıcı çözüm, eksik ayarı program açılırken yakalayıp açık bir mesajla durmak; bunu ayarlar.js'te yapacaksın.

.env dosyası ve node --env-file

Her seferinde $env: yazmak yorucu; terminali kapatınca da her şey gidiyor. Bir projede genelde beş on ayar olur. Çözüm, ayarları proje klasöründe .env adlı bir metin dosyasına yazmak ve Node'a, programı başlatırken o dosyayı okutmak.

.env birazdan gerçek bir anahtar taşıyacak. Git'e bir an bile girmemesi için sırayı bozma: önce .gitignore, sonra .env. Kurulumda açtığın .gitignore'u şu hâle getir:

.gitignore
node_modules/.envveri*.json*.tmp
  • .env: birazdan açacağın ayar dosyası.
  • veri*.json: veri.json ve veri-yedek.json; * "ne olursa olsun" demek (Ders 7). Veri dosyası kodun parçası değil: her istekte değişir ve gerçek bir uygulamada kullanıcıların bilgilerini taşır.
  • *.tmp: yarıda kalmış geçici dosyalar.

Şimdi VS Code'da yeni bir dosya aç, adını tam olarak .env koy (başında nokta var, uzantısı yok), içine iki satır yaz ve ortam.js'i bu kez --env-file seçeneğiyle çalıştır:

.env — kalici-api — Visual Studio Code
.env
PORT=3000YONETICI_ANAHTARI=gelistirme-anahtari-42
PROBLEMSOUTPUTDEBUG CONSOLETERMINAL
PS C:\kod\kalici-api> node --env-file=.env ortam.jsİşletim sistemi: Windows_NTPort: 3000Anahtar: gelistirme-anahtari-42
Ln 1, Col 1Spaces: 4UTF-8.env

Her satır bir değişken: ad, =, değer. Adlar geleneksel olarak büyük harf ve alt çizgiyle yazılır. Listeye sığsın diye node_modules klasörünü ve önceki bölümlerin deneme dosyalarını göstermedik.

--env-file=.env Node'a "programı başlatmadan önce bu dosyadaki satırları process.env'e koy" der. Node 20.6'dan beri Node'un içinde, Node 24.10'dan beri de deneysel değil. İnternetteki eski örneklerde aynı işi gören dotenv paketini (import "dotenv/config") görürsün; Node 24'te ona gerek yok. Bir kural daha: terminalde aynı adla bir değişken zaten tanımlıysa ($env:PORT gibi) o kazanır; Node .env'deki değeri ortamda zaten olan bir değerin üstüne yazmaz.

.env'in yazım kuralları

Node .env'i kendi kurallarıyla okur ve bazı kurallar seni şaşırtabilir. Hepsini bir kerede gör: VS Code'da kurallar.env adında bir dosya aç ve yaz:

kurallar.env
# Bu satır yorum, Node okumazBOSLUKLU = deneme-123TIRNAKSIZ=Sihirdar VadisiTIRNAKLI="Sihirdar Vadisi"NOTLU=3000 # geliştirme portuKARE=ab#cdTIRNAKTA_KARE="ab#cd"NOKTALI_VIRGUL=3000;KIVRIK=“abc”BOS=

goster.js her değeri JSON.stringify ile yazdırıyor (Ders 12); böylece tırnakları, boş metni ve undefined'ı birbirinden ayırt edebilirsin:

goster.js
const adlar = ["BOSLUKLU", "TIRNAKSIZ", "TIRNAKLI", "NOTLU", "KARE", "TIRNAKTA_KARE", "NOKTALI_VIRGUL", "KIVRIK", "BOS", "YOK"]for (const ad of adlar) {    console.log(ad, JSON.stringify(process.env[ad]))}
Terminal — PowerShell
PS C:\kod\kalici-api> node --env-file=kurallar.env goster.jsBOSLUKLU "deneme-123"TIRNAKSIZ "Sihirdar Vadisi"TIRNAKLI "Sihirdar Vadisi"NOTLU "3000"KARE "ab"TIRNAKTA_KARE "ab#cd"NOKTALI_VIRGUL "3000;"KIVRIK "“abc”"BOS ""YOK undefined
  • = çevresindeki boşluklar atılır. Tırnaksız değerin içinde boşluk olabilir.
  • Değeri saran düz tırnaklar (" ya da ') atılır.
  • #'den sonrası yorumdur, değerin ortasında bile: KARE=ab#cd yalnız ab verdi. İçinde # geçen bir şifreyi mutlaka tırnak içine al.
  • Sondaki ; değerin parçasıdır. JavaScript alışkanlığıyla satır sonuna noktalı virgül koyma.
  • Word ya da WhatsApp'tan gelen kıvrık tırnaklar “ ” tırnak sayılmaz, değerin içinde kalır (Ders 1'deki akıllı tırnak tuzağı).
  • BOS= boş metin ("") verir, undefined değil. Dosyada hiç olmayan değişken undefined olur.

Denemeden sonra kurallar.env'i silebilirsin.

.env.example: şablon Git'e girer

.env Git'e girmiyor. Peki projeyi GitHub'dan indiren bir arkadaşın (ya da yeni bilgisayarındaki sen) hangi ayarların gerektiğini nereden bilecek? Cevap, değerleri boş bırakılmış bir şablon. .env.example adında bir dosya aç:

.env.example
# Bu dosyayı kopyalayıp adını .env yap, değerleri doldur.PORT=3000YONETICI_ANAHTARI=

Bu dosya Git'e girer; içinde gizli bir şey yok, yalnız ayarların adları ve zararsız varsayılanlar. Projeyi yeni kuran kişi şablonu kopyalar ve boşlukları kendi değerleriyle doldurur:

Terminal — PowerShell
PS C:\kod\kalici-api> Copy-Item .env.example .env

Bu komutu şimdi çalıştırma: senin .env'in zaten var ve Copy-Item hiçbir şey sormadan onun üstüne yazar, anahtarın silinir. Komut, projeyi yeni indiren biri içindir.

.gitignore'daki .env satırı .env.example'ı etkilemez: kural yalnız adı tam olarak .env olan dosyaya uyar.

Ayarları tek yerde toplamak: ayarlar.js

Ayarları kodun her yerinden process.env.X diye okumak yerine tek bir modülde topla. Böylece programın hangi ayarlara ihtiyaç duyduğu tek dosyada görünür ve eksik bir ayar ilk istek geldiğinde değil, program açılırken yakalanır. ayarlar.js adında bir dosya aç:

ayarlar.js
function gerekli(ad) {    const deger = process.env[ad]    if (!deger) {        throw new Error(`${ad} ayarı yok. .env dosyasına bak.`)    }    return deger} export const ayarlar = {    port: process.env.PORT ?? 3000,    yoneticiAnahtari: gerekli("YONETICI_ANAHTARI"),    canli: process.env.NODE_ENV === "production"}
  • gerekli(ad), değişkeni process.env[ad] diye köşeli parantezle okur, çünkü ad bir değişkenin içinde (Ders 4). Değer yoksa ya da boş metinse (!deger, Ders 12) açık bir mesajla hata fırlatır (Ders 13).
  • port: PORT yoksa 3000 (Ders 12'deki ??). Port gizli değil ve olmadığında makul bir varsayılanı var.
  • yoneticiAnahtari: gizli bir değerin varsayılanı olamaz. Yoksa program hiç açılmasın.
  • canli: NODE_ENV adlı değişken "production" ise true. Bu kalıbı dersin sonunda kullanacaksın.

Sunucu Ders 28'deki gibi bir de sayfa sunsun. public adında bir klasör aç ve içine kısa bir sayfa koy:

public/index.html
<!doctype html><html lang="tr"><head>    <meta charset="utf-8">    <title>Şampiyon API'si</title></head><body>    <h1>Şampiyon API'si</h1>    <p>Liste: <a href="/api/sampiyonlar">/api/sampiyonlar</a></p></body></html>

Şimdi sunucu.js'i ayarları kullanacak hâle getir. Değişen yerler vurgulu: yeni import'lar, sabit yollu express.static, yönetici ara katmanı, listeyi silen route ve app.listen:

sunucu.js — kalici-api — Visual Studio Code
sunucu.js
import express from "express"import path from "node:path"import { ayarlar } from "./ayarlar.js"import { oku, degistir } from "./depo.js" const app = express() app.use(express.json())app.use(express.static(path.join(import.meta.dirname, "public"))) function yoneticiMi(req, res, next) {    if (req.get("Authorization") !== `Bearer ${ayarlar.yoneticiAnahtari}`) {        return res.status(401).json({ hata: "Yönetici anahtarı eksik ya da yanlış" })    }    next()} app.get("/api/sampiyonlar", async (req, res) => {    const veri = await oku()    res.json(veri.sampiyonlar)}) app.post("/api/sampiyonlar", async (req, res) => {    const isim = req.body?.isim    if (typeof isim !== "string" || isim.trim() === "") {        return res.status(400).json({ hata: "isim alanı gerekli" })    }    const yeni = await degistir((veri) => {        veri.sonId = veri.sonId + 1        const sampiyon = { id: veri.sonId, isim: isim.trim() }        veri.sampiyonlar.push(sampiyon)        return sampiyon    })    res.status(201).json(yeni)}) app.delete("/api/sampiyonlar", yoneticiMi, async (req, res) => {    await degistir((veri) => {        veri.sampiyonlar = []    })    res.status(204).end()}) app.listen(ayarlar.port, (hata) => {    if (hata) {        throw hata    }    const mod = ayarlar.canli ? "canlı" : "geliştirme"    console.log(`Sunucu çalışıyor (${mod}): http://localhost:${ayarlar.port}`)})
Ln 1, Col 1Spaces: 4UTF-8JavaScript
  • 9. satır: Ders 28'deki express.static("public") artık çalışma klasörüne bağlı değil; public hep sunucu.js'in yanındaki klasör.
  • yoneticiMi bir ara katman. req.get("Authorization") isteğin Authorization başlığını okur; başlık adında büyük-küçük harf fark etmez. Başlık tam olarak Bearer + boşluk + anahtar değilse istek 401 ile geri döner ve route'a hiç ulaşmaz.
  • app.delete("/api/sampiyonlar", yoneticiMi, ...) bütün listeyi siler. sonId'ye dokunmuyor; silinen numaralar bir daha verilmez.
  • app.listen portu ayarlardan alıyor ve hangi ortamda çalıştığını söylüyor.

sunucu.js'i kaydettiğin anda --watch sunucuyu yeniden başlatır ve birinci terminalde Error: YONETICI_ANAHTARI ayarı yok. .env dosyasına bak. yazıp durur. Şaşırma: dev betiği .env'i henüz okumuyor, ayarlar.js de tam bunu yakaladı. Şimdi package.json'daki dev betiğine --env-file ekle:

package.json
  "scripts": {    "start": "node sunucu.js",    "dev": "node --watch --env-file=.env sunucu.js"  },

start olduğu gibi kaldı; nedenini dersin sonundaki canlı ortam bölümünde göreceksin. --watch package.json'ı izlemez; birinci terminalde sunucuyu Ctrl+C ile durdur ve yeniden başlat:

Sunucunun terminali
PS C:\kod\kalici-api> npm run dev > kalici-api@1.0.0 dev> node --watch --env-file=.env sunucu.js Sunucu çalışıyor (geliştirme): http://localhost:3000

İkinci terminalden korumalı route'u dene. Bu bir geliştirme anahtarı, komutun içine yazmanın zararı yok; canlıdaki anahtarı ise komutlara yazma (Ders 26):

İkinci terminal
PS C:\kod\kalici-api> curl.exe -i -X DELETE http://localhost:3000/api/sampiyonlarHTTP/1.1 401 UnauthorizedX-Powered-By: ExpressContent-Type: application/json; charset=utf-8Content-Length: 51ETag: W/"33-JUk/9UMX2vw4/eg1cpYlxj+GxSc"Date: Mon, 12 Oct 2026 00:14:03 GMTConnection: keep-aliveKeep-Alive: timeout=5 {"hata":"Yönetici anahtarı eksik ya da yanlış"}PS C:\kod\kalici-api> curl.exe -X DELETE http://localhost:3000/api/sampiyonlar -H "Authorization: Bearer yanlis"{"hata":"Yönetici anahtarı eksik ya da yanlış"}PS C:\kod\kalici-api> curl.exe -i -X DELETE http://localhost:3000/api/sampiyonlar -H "Authorization: Bearer gelistirme-anahtari-42"HTTP/1.1 204 No ContentX-Powered-By: ExpressDate: Mon, 12 Oct 2026 00:14:03 GMTConnection: keep-aliveKeep-Alive: timeout=5 PS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar[]

Anahtarsız ve yanlış anahtarlı istek 401 aldı; doğru anahtarla liste silindi (204, gövdesiz). Listeyi geri getir: Copy-Item veri-yedek.json veri.json. --watch artık .env'i de izliyor. .env'de anahtarın sonundaki 42'yi 43 yapıp kaydet ve sunucunun terminaline bak:

Sunucunun terminali
Change detected in 'C:\\kod\\kalici-api\\.env'Restarting 'sunucu.js'Sunucu çalışıyor (geliştirme): http://localhost:3000

.env yalnız program açılırken okunur; --watch olmasaydı değişikliği görmek için sunucuyu elle yeniden başlatman gerekirdi. Anahtarı 42'ye geri al.

Sık hatanode: .env: not found

Ne zaman çıkar? --env-file ile verdiğin dosya yoksa: adı farklıysa (env, .env.txt), başka bir klasördeyse ya da hiç oluşturulmadıysa. Node programı hiç başlatmaz. Dosya, terminalin çalışma klasörüne göre aranır.

Terminal — PowerShell
PS C:\kod\kalici-api> npm run dev > kalici-api@1.0.0 dev> node --watch --env-file=.env sunucu.js node: .env: not foundPS C:\kod\kalici-api> node --env-file=.env ortam.jsC:\Program Files\nodejs\node.exe: .env: not found

npm run dev ile başlatınca satırın başında node, Node'u kendin çalıştırınca tam yolu yazar; mesaj aynı.

Nasıl düzelir? Terminal proje klasöründe mi, istemde C:\kod\kalici-api yazıyor mu, bak. VS Code'un dosya listesinde adın tam olarak .env olduğunu denetle; Not Defteri'yle oluşturduğun dosya, uzantılar gizliyken aslında .env.txt olabilir. Projeyi yeni indirdiysen .env hiç yoktur: Copy-Item .env.example .env ile oluştur ve doldur. Dosya olsa da olmasa da çalışması gereken bir komut için Node'da --env-file-if-exists=.env seçeneği de var; dosya yoksa .env not found. Continuing without it. ("bulunamadı, onsuz devam ediyorum") yazar ve programı yine başlatır.

Sık hataError: YONETICI_ANAHTARI ayarı yok. .env dosyasına bak.

Ne zaman çıkar? Bu mesajı ayarlar.js'te sen yazdın. .env'de anahtar yoksa, boşsa ya da sunucuyu .env'i okumayan bir komutla başlattıysan çıkar. En sık sebep npm run dev yerine npm start yazmak:

Terminal — PowerShell
PS C:\kod\kalici-api> npm start > kalici-api@1.0.0 start> node sunucu.js file:///C:/kod/kalici-api/ayarlar.js:4        throw new Error(`${ad} ayarı yok. .env dosyasına bak.`)              ^ Error: YONETICI_ANAHTARI ayarı yok. .env dosyasına bak.    at gerekli (file:///C:/kod/kalici-api/ayarlar.js:4:15)    at file:///C:/kod/kalici-api/ayarlar.js:11:23    ... Node.js v24.20.0

Program daha portu açmadan durdu. İyi bir şey: anahtarsız bir sunucunun çalışıp ilk yönetici isteğinde garip davranmasından çok daha kolay anlaşılır.

Nasıl düzelir? Geliştirirken npm run dev kullan. .env'de YONETICI_ANAHTARI= satırının dolu olduğunu, adın harfi harfine aynı yazıldığını denetle.

Sık hataError: listen EACCES: permission denied 3000;

Ne zaman çıkar? .env'de port satırının sonuna noktalı virgül koyunca: PORT=3000;. Değer "3000;" olur. Node sayıya çevrilemeyen bir metni port numarası saymaz; onu, Windows'ta iki programın konuştuğu adlandırılmış bir hattın (named pipe) adı sanır. 3000; adıyla böyle bir hat açmaya kalkar ve Windows izin vermez. Mesajın portla ilgisi yokmuş gibi görünmesi bu yüzden.

Terminal — PowerShell
PS C:\kod\kalici-api> npm run dev > kalici-api@1.0.0 dev> node --watch --env-file=.env sunucu.js file:///C:/kod/kalici-api/sunucu.js:46        throw hata        ^ Error: listen EACCES: permission denied 3000;    at Server.setupListenHandle [as _listen2] (node:net:2302:21)    at listenInCluster (node:net:2433:12)    at Server.listen (node:net:2575:5)    at app.listen (C:\kod\kalici-api\node_modules\express\lib\application.js:605:24)    at file:///C:/kod/kalici-api/sunucu.js:44:5    ... {  code: 'EACCES',  errno: -4092,  syscall: 'listen',  address: '3000;',  port: -1} Node.js v24.20.0Failed running 'sunucu.js'. Waiting for file changes before restarting...

Alttaki address: '3000;' satırı ipucunu veriyor: noktalı virgül değerin içinde. Son satır --watch'tan: program çöktü, bir dosya değişene kadar bekliyor.

Nasıl düzelir? .env'deki satırı PORT=3000 yap ve kaydet; --watch sunucuyu kendisi yeniden başlatır.

Sık hataRangeError [ERR_SOCKET_BAD_PORT]: options.port should be >= 0 and < 65536. Received type string ('').

Ne zaman çıkar? .env'de PORT= satırı var ama değeri boşsa. process.env.PORT undefined değil, boş metin olur; ?? yalnız null ve undefined'da devreye girdiği için 3000'e geçmez ve app.listen("") çağrılır.

Terminal — PowerShell
PS C:\kod\kalici-api> npm run dev > kalici-api@1.0.0 dev> node --watch --env-file=.env sunucu.js node:net:2547    validatePort(options.port, 'options.port');    ^ RangeError [ERR_SOCKET_BAD_PORT]: options.port should be >= 0 and < 65536. Received type string ('').    at Server.listen (node:net:2547:5)    at app.listen (C:\kod\kalici-api\node_modules\express\lib\application.js:605:24)    at file:///C:/kod/kalici-api/sunucu.js:44:5    ... {  code: 'ERR_SOCKET_BAD_PORT'} Node.js v24.20.0Failed running 'sunucu.js'. Waiting for file changes before restarting...

Nasıl düzelir? Ya satıra bir değer yaz (PORT=3000) ya da satırı tamamen sil; satır yoksa ?? 3000'i seçer. Görevlerde boş metni de, saçma değerleri de yakalayan bir port okuyucu yazacaksın.

Gizli bilgiler: koda ve Git'e girmez

.env'in Git'e neden hiç girmemesi gerektiğine yakından bakalım. İçindeki her şey gizli değil: PORT'u herkes bilse zararsız. Ama yönetici anahtarı aynı dosyada duruyor; bu yüzden dosyanın tamamını gizli say.

Dört kural:

  • Gizli bilgi koda yazılmaz; program onu process.env'den okur.
  • .env .gitignore'da durur; Git'e onun yerine .env.example girer.
  • Her commit'ten önce git status'a bak: listede .env görüyorsan dur ve .gitignore'u düzelt.
  • Ekran görüntüsü alırken, yayın açarken, kodunu birine gösterirken .env açık bir sekmede durmasın.

Silmek yetmez: Git hatırlar

Kurallara rağmen bir gün .env commit'e girebilir. İlk akla gelen, dosyayı silip yeni bir commit atmak. Bunun neden yetmediğini ayrı bir deneme klasöründe gör. Sunucuyu Ctrl+C ile durdur; birazdan VS Code'da başka bir klasör açacaksın ve terminaller kapanacak. Sonra C:\kod'a geç, sir-deneme adında bir klasör aç, içine gir ve Git'i başlat:

Terminal — PowerShell
PS C:\kod\kalici-api> cd C:\kodPS C:\kod> mkdir sir-denemePS C:\kod> cd sir-denemePS C:\kod\sir-deneme> git initInitialized empty Git repository in C:/kod/sir-deneme/.git/

git init sende hint: ile başlayan satırlar da yazarsa Ders 18'deki gibi geçebilirsin; varsayılan dal adıyla ilgili bir öneri. VS Code'da File → Open Folder ile bu klasörü aç, yeni bir terminal aç (Terminal → New Terminal) ve iki dosya oluştur: içinde console.log("Sunucu") yazan sunucu.js ve içinde YONETICI_ANAHTARI=c6b3876c-4724-44d6-8f6b-9c56a3dea415 yazan .env. Bu klasörde .gitignore yok; yani hata yapan birinin klasörü. Sonra:

Terminal — PowerShell
PS C:\kod\sir-deneme> git add .PS C:\kod\sir-deneme> git commit -m "İlk kayıt"[master (root-commit) 9ca960e] İlk kayıt 2 files changed, 2 insertions(+) create mode 100644 .env create mode 100644 sunucu.js

create mode 100644 .env: anahtar kayda girdi. Şimdi "hatayı düzelt": VS Code'da .env'i sil ve silmeyi kaydet:

Terminal — PowerShell
PS C:\kod\sir-deneme> git add .PS C:\kod\sir-deneme> git commit -m ".env dosyasını sil"[master c0c4e9c] .env dosyasını sil 1 file changed, 1 deletion(-) delete mode 100644 .envPS C:\kod\sir-deneme> git log --onelinec0c4e9c .env dosyasını sil9ca960e İlk kayıtPS C:\kod\sir-deneme> git show 9ca960e:.envYONETICI_ANAHTARI=c6b3876c-4724-44d6-8f6b-9c56a3dea415

git show KOD:dosya bir commit'teki dosyanın o anki içini gösterir. Dosya artık klasörde yok, son commit'te de yok; ama ilk kayıt onu olduğu gibi saklıyor. Commit dosyaların o anki kopyasıdır; silmek yeni bir kayıt ekler, eskisini değiştirmez. Senin commit kodların farklı olacak; git show'a git log --oneline listesinin en altındaki, İlk kayıt satırının kodunu yaz.

Depo GitHub'a gittiyse durum daha kötü: depoyu görebilen herkes eski commit'lere tıklayıp dosyayı okuyabilir ve herkese açık depoları anahtar arayarak sürekli tarayan otomatik programlar var. GitHub bazı tanınmış servislerin anahtar biçimlerini herkese açık depolara gönderilirken yakalayıp engelleyebilir (push protection); ama yalnız tanıdığı biçimleri yakalar, senin uydurduğun bir yönetici anahtarını tanımaz. Git geçmişini yeniden yazan araçlar da var (git filter-repo gibi), ama bir anahtar bir kez dışarı çıktıysa onu kopyalanmış say.

Deneme bitti; sir-deneme klasörünü silebilirsin. VS Code'da File → Open Folder ile C:\kod\kalici-api'ye dön, bir terminal aç ve sunucuyu npm run dev ile yeniden başlat. İkinci terminali de aç; bir sonraki bölümde ikisi de gerekecek.

Sızınca ne yapılır?

Bir anahtarın sızdığını fark ettiğin anda, sırasıyla:

  1. Anahtarı hemen iptal et, yenisini al. Servisin panelinde anahtarı sil ya da yenile; düğmenin adı çoğu yerde "Revoke", "Regenerate" ya da "Roll" gibidir (arayüz değişmiş olabilir). Kendi yönetici anahtarınsa yeni bir değer üret. Geçmişi temizlemekle vakit kaybetme; önce bu.
  2. Erişim kayıtlarına bak. Servisin kullanım, kayıt (log) ya da fatura sayfasında sızdığı andan beri tanımadığın istekler, beklenmedik harcamalar var mı? Kendi sunucunsa onun kayıtlarına bak. Bir şey bulursan ne zaman ve ne yapıldığını not et.
  3. Yeni anahtarı yerine koy. Bilgisayarında .env'e, canlıda barındırıcının paneline yaz ve programı yeniden başlat; .env yalnız açılışta okunur.
  4. Sızıntının yolunu kapat. .env .gitignore'da mı? Dosya zaten Git'in takibindeyse .gitignore'a yazmak onu gizlemez (Ders 7); aşağıdaki komutla takipten çıkar ve commit'le.

Örneğin .env'i daha önce commit'lediğin bir projede .gitignore'u yeni oluşturup içine .env yazdıysan:

Terminal — PowerShell
PS C:\kod\bir-proje> git rm --cached .envrm '.env'PS C:\kod\bir-proje> git status --shortD  .env?? .gitignore

--cached "yalnız Git'in kaydından sil" demek: .env klasöründe durmaya devam eder, Git ise artık onu izlemez. D silindi, ?? henüz izlenmeyen yeni dosya (burada yeni açılan .gitignore) demek. Geçmişteki kopyası yine durur; bu yüzden 4. adım 1. adımın yerini tutmaz.

Yayına giden klasörde gizli dosya kalmasın

Bir ders de gerçek bir olaydan. Kişisel bir site, bir yayın aracıyla tek komutla internete konuyordu. Aracın yüklediği klasörün içinde, sitenin parçası olmayan başka bir projenin alt klasörü ve onun .env dosyası da duruyordu. .env Git'e hiç girmemişti; .gitignore doğruydu. Ama yayın aracı Git'e değil, klasöre bakıyordu: .env de yüklendi ve adres çubuğuna site-adresi/alt-klasor/.env yazan herkes onu indirebiliyordu. Fark edilince dosya yayından kaldırıldı; böyle bir durumda doğru sıra yukarıdaki adımlardır.

Ders: .gitignore Git'in kuralıdır, yayın araçları her zaman Git'e bakmaz. GitHub Pages (Ders 25) deponu yayınlar; Git'e girmeyen dosya oraya da gitmez. Vercel'in komut satırı aracı gibi araçlar ise bilgisayarındaki klasörü yükler. Vercel'in belgesine göre kendiliğinden atladığı listede .env.local var ama düz .env yok; yüklenmesini istemediğin dosyaları .vercelignore adlı ayrı bir dosyaya yazman gerekir (Ekim 2026'daki belge; liste değişmiş olabilir). Her yayın aracının yok sayma kuralı farklıdır; kullandığın aracın belgesine bak.

Kendi sunucunda dene

Aynı hatayı Express'le yap. Diyelim ki biri, sayfa dosyaları proje klasöründe dursun diye express.static'e klasörün kendisini verdi. sunucu.js'te 9. satırı geçici olarak değiştir:

sunucu.js✗ Yanlış
app.use(express.static(import.meta.dirname))
İkinci terminal
PS C:\kod\kalici-api> curl.exe http://localhost:3000/ayarlar.jsfunction gerekli(ad) {    const deger = process.env[ad]    if (!deger) {        throw new Error(`${ad} ayarı yok. .env dosyasına bak.`)    }    return deger} export const ayarlar = {    port: process.env.PORT ?? 3000,    yoneticiAnahtari: gerekli("YONETICI_ANAHTARI"),    canli: process.env.NODE_ENV === "production"}PS C:\kod\kalici-api> curl.exe http://localhost:3000/.env<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Error</title></head><body><pre>Cannot GET /.env</pre></body></html>

Sunucunun kodu herkese açıldı; /veri.json, /sunucu.js ve /package.json da aynı şekilde iner. Express seni bir kez kurtardı: express.static adı noktayla başlayan dosyaları (.env, .gitignore) varsayılan olarak göndermez. Her araç bu kadar dikkatli değil. Aynı klasörü Ders 18'deki npx serve ile aç: birinci terminalde sunucuyu Ctrl+C ile durdur ve aynı yerde npx serve yaz. 3000 boşaldığı için serve de 3000'i alır; kutudaki adresin http://localhost:3000 olduğuna bak. Sonra ikinci terminalden .env'i iste:

İkinci terminal
PS C:\kod\kalici-api> curl.exe -i http://localhost:3000/.envHTTP/1.1 200 OKContent-Length: 53Content-Disposition: inline; filename=".env"Accept-Ranges: bytesETag: "5575c6da17169cb20ab81bb8262530deb923a767"Date: Mon, 12 Oct 2026 00:26:42 GMTConnection: keep-aliveKeep-Alive: timeout=5 PORT=3000YONETICI_ANAHTARI=gelistirme-anahtari-42

200 OK ve anahtar ekranda (biz serve 14.2.6 ile denedik). Bu, olaydaki sitenin başına gelenin aynısı. Birinci terminalde serve'ü Ctrl+C ile durdur, sunucu.js'te 9. satırı geri al (app.use(express.static(path.join(import.meta.dirname, "public")))) ve sunucuyu npm run dev ile yeniden başlat.

Diskinde .env node_modules/ veri.json veri.json.tmp .gitignore'da: Git görmez Git'te ve GitHub'da sunucu.js ayarlar.js depo.js package.json .gitignore .env.example İnternette public/ index.html adresi bilen herkes indirir
Şekil 29.3 Proje klasöründeki dosyalar üç bölgede yaşar. .gitignore diskteki dosyaları Git'ten, public klasörü Git'teki dosyaları internetten ayırır.

Kural: yayın klasörüne yalnız yayınlanacak dosyalar girer.

  • Sunucu kodu, .env ve veri dosyası public'in dışında durur. İçeride yalnız tarayıcıya gidecekler var: HTML, CSS, tarayıcıda çalışan JavaScript, resimler.
  • Statik bir siteyi yayınlarken de aynı kural geçerli: yayınlanan klasörde not, yedek, başka bir projenin klasörü durmaz.
  • Yayın aracının yok sayma dosyasını bil: depodan yayınlayan araçlarda .gitignore, klasör yükleyen araçlarda aracın kendi dosyası (.vercelignore gibi).
  • Yayından sonra kendin dene: curl.exe -I https://siten/.env 404 dönmeli. Kendi sunucunda şimdi öyle:
İkinci terminal
PS C:\kod\kalici-api> curl.exe -I http://localhost:3000/.envHTTP/1.1 404 Not FoundX-Powered-By: ExpressContent-Security-Policy: default-src 'none'X-Content-Type-Options: nosniffContent-Type: text/html; charset=utf-8Content-Length: 144Date: Mon, 12 Oct 2026 00:20:06 GMTConnection: keep-aliveKeep-Alive: timeout=5

-I yalnız yanıtın başlıklarını ister; gövdeye bakmadan durum koduna bakmanın kısa yolu.

Geliştirme ve canlı ortam

Şimdiye kadar her şey senin bilgisayarında, yani geliştirme ortamında çalıştı. Ders 32'de aynı kod bir barındırıcıda, canlı ortamda çalışacak. Kod aynı kalır; değişen ayarlardır.

Geliştirme (bilgisayarın)Canlı (barındırıcı)
Ayarlar nereden gelir?.env ve --env-filebarındırıcının panelinde girilen ortam değişkenleri
PORT3000barındırıcı verir
NODE_ENVyokproduction
YONETICI_ANAHTARIgelistirme-anahtari-42 gibi kolay bir değeruzun, rastgele, yalnız canlıya özel
Verideneme verisigerçek kullanıcıların verisi
Hata sayfasıayrıntılı, yığın izligenel bir cümle
Bilgisayarın geliştirme ortamı .env dosyası PORT=3000 YONETICI_ANAHTARI=… Barındırıcı canlı ortam panelden girilen değerler NODE_ENV=production YONETICI_ANAHTARI=… --env-file=.env barındırıcı kendisi verir process.env ayarlar.js okur; kod iki yerde aynı
Şekil 29.4 Kod iki ortamda da ayarlarını process.env'den okur. Değerleri bilgisayarında .env dosyası, canlıda barındırıcının paneli verir.

İki ortamda ayrı anahtar kullanmanın iki faydası var: bilgisayarındaki .env bir gün sızarsa canlı etkilenmez; canlıdaki anahtarı da yalnız gereken kişi bilir. Canlı için tahmin edilemez bir anahtar üretmenin kısa yolu:

Terminal — PowerShell
PS C:\kod\kalici-api> node -p "crypto.randomUUID()"d155edef-8768-473a-a9b5-a2c8ad7c8332

node -p tırnak içindeki kodu çalıştırıp sonucunu yazar. crypto'yu Ders 31'de tanıyacaksın, şimdilik kalıp olarak kullan. Her çalıştırmada başka bir 36 karakterlik değer çıkar; bu anahtarı tahmin etmek pratikte imkânsızdır.

NODE_ENV ve Express

NODE_ENV bir gelenek: Node onu kendisi koymaz, barındırıcılar ve sen koyarsın. Değeri production ise birçok paket kendini canlıya göre ayarlar; Express de öyle. Dersin başındaki bozuk veri.json denemesini hatırla: Express'in hata sayfası SyntaxError'ı, dosya yollarını ve satır numaralarını herkese gösteriyordu. Şimdi canlıyı taklit et. Birinci terminalde sunucuyu Ctrl+C ile durdur, VS Code'da yeni bir terminal aç ve ayarları barındırıcının yerine sen ver:

Yeni terminal
PS C:\kod\kalici-api> $env:NODE_ENV = "production"PS C:\kod\kalici-api> $env:YONETICI_ANAHTARI = "canli-deneme-anahtari"PS C:\kod\kalici-api> npm start > kalici-api@1.0.0 start> node sunucu.js Sunucu çalışıyor (canlı): http://localhost:3000

npm start .env'i okumadı; ayarlar ortamdan geldi, tıpkı barındırıcıda olacağı gibi. "canlı" sözcüğü ayarlar.canli'den geliyor. İkinci terminalde veri dosyasını boşalt ve listeyi iste:

İkinci terminal
PS C:\kod\kalici-api> Clear-Content veri.jsonPS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Error</title></head><body><pre>Internal Server Error</pre></body></html>PS C:\kod\kalici-api> Copy-Item veri-yedek.json veri.json

Clear-Content dosyanın içini boşaltır. Ziyaretçi yalnız Internal Server Error gördü; ayrıntı, yani SyntaxError: Unexpected end of JSON input ve yığın izi, yine sunucunun terminalinde. Geliştirmede ayrıntı işine yarar, canlıda saldırgana sunucunun haritasını verir (Ders 28'deki kural). Kendi hata yakalayıcını yazdığında aynı ayrımı sen yaparsın: ayarlar.canli ise genel bir cümle, değilse ayrıntı. Kimlik doğrulamada da sık kullanılan bir kalıp bu; Ders 31'de çerezlerin bir ayarını onunla vereceksin.

Bitince sunucuyu durdur ve bu terminali kapat; $env:NODE_ENV ve $env:YONETICI_ANAHTARI onunla birlikte gider. Açık kalırsa aynı terminaldeki npm run dev de kendini canlı sanır, çünkü ortamdaki değer .env'den önce gelir.

Görevler

Görev dosyalarını proje klasöründe gorevler adında bir alt klasöre koy ve proje klasöründen çalıştır: node gorevler\esyalar.js. package.json'daki "type": "module" onlar için de geçerli. Ortamda $env: ile tanımladığın bir değişken kalmasın diye yeni bir terminal aç. Önce kendin dene, takılırsan ipuçlarını sırayla aç.

Görev 1

Eşya klasörü

gorevler\esyalar.js adında bir program yaz. Program kendi yanında esyalar adında bir klasör açsın (klasör zaten varsa hata vermesin) ve içine dört dosya yazsın: kilic.json ({ ad: "Uzun Kılıç", fiyat: 350 }), kalkan.json ({ ad: "Doran Kalkanı", fiyat: 450 }), iksir.json ({ ad: "Can İksiri", fiyat: 50 }) ve içinde düz bir cümle olan not.txt. Sonra klasördeki yalnız .json dosyalarını okuyup her eşyayı ve toplam fiyatı yazdırsın:

Metin
Can İksiri: 50 altınDoran Kalkanı: 450 altınUzun Kılıç: 350 altınToplam: 850 altın

Program C:\kod'dan node kalici-api\gorevler\esyalar.js diye başlatılsa bile klasörü esyalar.js'in yanında açmalı.

İpucu 1

Klasörün yolu path.join(import.meta.dirname, "esyalar"). Her dosya yolunu da bu klasörle path.join ederek kur.

İpucu 2

readdir yalnız adları verir ve not.txt de aralarında. path.extname(ad) ".json" değilse o turu continue ile atla (Ders 5).

İpucu 3

Döngüden önce let toplam = 0, her eşyada toplam = toplam + esya.fiyat. Dosyaları Windows alfabetik sırayla verdiği için çıktı iksir, kalkan, kilic sırasıyla gelir.

Çözümü göster
esyalar.js
import { mkdir, writeFile, readdir, readFile } from "node:fs/promises"import path from "node:path" const klasor = path.join(import.meta.dirname, "esyalar")await mkdir(klasor, { recursive: true }) await writeFile(path.join(klasor, "kilic.json"), JSON.stringify({ ad: "Uzun Kılıç", fiyat: 350 }), "utf8")await writeFile(path.join(klasor, "kalkan.json"), JSON.stringify({ ad: "Doran Kalkanı", fiyat: 450 }), "utf8")await writeFile(path.join(klasor, "iksir.json"), JSON.stringify({ ad: "Can İksiri", fiyat: 50 }), "utf8")await writeFile(path.join(klasor, "not.txt"), "Bu bir eşya değil", "utf8") let toplam = 0for (const ad of await readdir(klasor)) {    if (path.extname(ad) !== ".json") {        continue    }    const metin = await readFile(path.join(klasor, ad), "utf8")    const esya = JSON.parse(metin)    console.log(`${esya.ad}: ${esya.fiyat} altın`)    toplam = toplam + esya.fiyat}console.log(`Toplam: ${toplam} altın`)
Çıktı
Can İksiri: 50 altınDoran Kalkanı: 450 altınUzun Kılıç: 350 altınToplam: 850 altın

Sınır denemeleri: programı ikinci kez çalıştır; recursive sayesinde hata yok, çıktı aynı. C:\kod'dan node kalici-api\gorevler\esyalar.js diye çalıştır; C:\kod\esyalar diye bir klasör açılmamalı. Dört writeFile satırının başına // koyup esyalar klasörünü boşalt: Toplam: 0 altın çıkmalı.

Görev 2

Port metnini sayıya çevir

process.env.PORT'tan gelen değeri denetleyen portOku(metin) fonksiyonunu gorevler\port.js'te yaz:

  • undefined ya da boş metinse 3000 döndürsün.
  • 1 ile 65535 arasında (ikisi dahil) bir tam sayıysa o sayıyı sayı olarak döndürsün.
  • Değilse PORT geçersiz: "..." mesajlı bir hata fırlatsın.

Fonksiyonun altına şu deneme döngüsünü yaz:

JavaScript
const denemeler = [undefined, "", "4000", "1", "65535", "0", "65536", "3000;", "3.5", "abc"]for (const metin of denemeler) {    try {        console.log(JSON.stringify(metin), "->", portOku(metin))    } catch (hata) {        console.log(JSON.stringify(metin), "->", hata.message)    }}

Beklenen çıktı:

Metin
undefined -> 3000"" -> 3000"4000" -> 4000"1" -> 1"65535" -> 65535"0" -> PORT geçersiz: "0""65536" -> PORT geçersiz: "65536""3000;" -> PORT geçersiz: "3000;""3.5" -> PORT geçersiz: "3.5""abc" -> PORT geçersiz: "abc"
İpucu 1

Önce boş durumu ele al ve erken dön (Ders 6): metin === undefined || metin === "".

İpucu 2

Number(metin) "3000;" ve "abc" için NaN, "3.5" için 3.5 verir. Number.isInteger(...) ikisini de yakalar (Ders 17).

Çözümü göster
port.js
function portOku(metin) {    if (metin === undefined || metin === "") {        return 3000    }    const port = Number(metin)    if (!Number.isInteger(port) || port < 1 || port > 65535) {        throw new Error(`PORT geçersiz: "${metin}"`)    }    return port} const denemeler = [undefined, "", "4000", "1", "65535", "0", "65536", "3000;", "3.5", "abc"]for (const metin of denemeler) {    try {        console.log(JSON.stringify(metin), "->", portOku(metin))    } catch (hata) {        console.log(JSON.stringify(metin), "->", hata.message)    }}
Çıktı
undefined -> 3000"" -> 3000"4000" -> 4000"1" -> 1"65535" -> 65535"0" -> PORT geçersiz: "0""65536" -> PORT geçersiz: "65536""3000;" -> PORT geçersiz: "3000;""3.5" -> PORT geçersiz: "3.5""abc" -> PORT geçersiz: "abc"

Sınır değerleri 1, 65535 geçti; bir eksiği ve bir fazlası (0, 65536) geçmedi. 0'ı neden reddettik? app.listen(0) hata vermez, işletim sisteminden rastgele boş bir port ister; sen 3000'de ararken sunucu başka bir kapıda açılır. Bu fonksiyonu ayarlar.js'te port: portOku(process.env.PORT) diye kullanırsan hata kartlarındaki iki tuzak da sunucu açılırken anlaşılır bir mesajla yakalanır.

Görev 3

Eksik ayarların hepsini söyle

ayarlar.js'teki gerekli ilk eksik ayarda durur: üç ayar eksikse kullanıcı programı üç kez başlatıp üç ayrı hata okur. gorevler\denetim.js'te ayarlariDenetle(adlar) fonksiyonunu yaz:

  • adlar dizisindeki ayarlardan process.env'de olmayan ya da boş olan bütün adları bulsun; en az biri varsa Eksik ayar: AD1, AD2 mesajlı tek bir hata fırlatsın.
  • NODE_ENV "production" ise YONETICI_ANAHTARI en az 32 karakter olmalı; değilse Canlıda YONETICI_ANAHTARI en az 32 karakter olmalı hatası.
  • Sorun yoksa Ayarlar tamam yazsın.

Altına şu denemeleri yaz. Denemek için process.env'e kodun içinden değer verebilirsin; değer yalnız bu programın süresince yaşar:

JavaScript
function dene(adlar) {    try {        ayarlariDenetle(adlar)    } catch (hata) {        console.log(hata.message)    }} process.env.YONETICI_ANAHTARI = "gelistirme-anahtari-42"process.env.BOS_AYAR = "" dene(["YONETICI_ANAHTARI"])dene(["YONETICI_ANAHTARI", "RIOT_ANAHTARI", "BOS_AYAR"])dene([]) process.env.NODE_ENV = "production"dene(["YONETICI_ANAHTARI"])process.env.YONETICI_ANAHTARI = "abcdefghijklmnopqrstuvwxyz12345"dene(["YONETICI_ANAHTARI"])process.env.YONETICI_ANAHTARI = "abcdefghijklmnopqrstuvwxyz123456"dene(["YONETICI_ANAHTARI"])

Son iki anahtar 31 ve 32 karakter. Beklenen çıktı:

Metin
Ayarlar tamamEksik ayar: RIOT_ANAHTARI, BOS_AYARAyarlar tamamCanlıda YONETICI_ANAHTARI en az 32 karakter olmalıCanlıda YONETICI_ANAHTARI en az 32 karakter olmalıAyarlar tamam
İpucu 1

Eksikleri filter ile bul (Ders 11): adlar.filter((ad) => !process.env[ad]). ! hem undefined'ı hem boş metni yakalar.

İpucu 2

Mesajı "Eksik ayar: " + eksikler.join(", ") ile kur (Ders 17). Boş dizide filter boş dizi döndürür; hata fırlatmamalısın.

İpucu 3

Canlı kuralında anahtar hiç olmayabilir; .length'i undefined'da çağırırsan program çöker. const anahtar = process.env.YONETICI_ANAHTARI ?? "" ile başla.

Çözümü göster
denetim.js
function ayarlariDenetle(adlar) {    const eksikler = adlar.filter((ad) => !process.env[ad])    if (eksikler.length > 0) {        throw new Error("Eksik ayar: " + eksikler.join(", "))    }    const anahtar = process.env.YONETICI_ANAHTARI ?? ""    if (process.env.NODE_ENV === "production" && anahtar.length < 32) {        throw new Error("Canlıda YONETICI_ANAHTARI en az 32 karakter olmalı")    }    console.log("Ayarlar tamam")} function dene(adlar) {    try {        ayarlariDenetle(adlar)    } catch (hata) {        console.log(hata.message)    }} process.env.YONETICI_ANAHTARI = "gelistirme-anahtari-42"process.env.BOS_AYAR = "" dene(["YONETICI_ANAHTARI"])dene(["YONETICI_ANAHTARI", "RIOT_ANAHTARI", "BOS_AYAR"])dene([]) process.env.NODE_ENV = "production"dene(["YONETICI_ANAHTARI"])process.env.YONETICI_ANAHTARI = "abcdefghijklmnopqrstuvwxyz12345"dene(["YONETICI_ANAHTARI"])process.env.YONETICI_ANAHTARI = "abcdefghijklmnopqrstuvwxyz123456"dene(["YONETICI_ANAHTARI"])
Çıktı
Ayarlar tamamEksik ayar: RIOT_ANAHTARI, BOS_AYARAyarlar tamamCanlıda YONETICI_ANAHTARI en az 32 karakter olmalıCanlıda YONETICI_ANAHTARI en az 32 karakter olmalıAyarlar tamam

BOS_AYAR tanımlı ama boş; yine de eksik sayıldı, çünkü boş bir ayar çoğu zaman unutulmuş bir ayardır. 31 karakter reddedildi, 32 kabul edildi. Geliştirme anahtarı (22 karakter) geliştirmede sorun değil, canlıda programı açtırmıyor: biri .env'i olduğu gibi canlıya taşırsa bu kural onu durdurur. crypto.randomUUID()'nin ürettiği 36 karakterlik anahtar kuralı geçer.

Görev 4

Bozuk kaydı açıkça söyle

TFT'deki ilerlemeni bir dosyada tutan iki fonksiyon yaz (gorevler\kayit.js):

  • guvenliYaz(dosya, veri): veriyi okunaklı JSON olarak atomik yazsın (geçici dosya + rename).
  • guvenliOku(dosya, varsayilan): dosya yoksa varsayilan'ı döndürsün. Dosya var ama JSON olarak çözülemiyorsa tft-kayit.json bozuk, elle bak (...) gibi, dosyanın adını ve asıl hatanın mesajını içeren yeni bir hata fırlatsın. Başka hataları olduğu gibi yeniden fırlatsın.

Dosya yolunu path.join(import.meta.dirname, "tft-kayit.json") ile kur ve şu dört durumu dene: olmayan bir dosyayı oku; { altin: 50, seviye: 7 } yazıp geri oku; dosyayı boş metinle ez ve oku; dosyaya JSON.stringify({ altin: 50, seviye: 7 }).slice(0, 12) (yarıda kesilmiş bir yazma) koyup oku. Varsayılan { altin: 0, seviye: 1 } olsun. Beklenen çıktı:

Metin
Dosya yok: { altin: 0, seviye: 1 }Yazıp okudum: { altin: 50, seviye: 7 }Hata: tft-kayit.json bozuk, elle bak (Unexpected end of JSON input)Hata: tft-kayit.json bozuk, elle bak (Expected double-quoted property name in JSON at position 12 (line 1 column 13))
İpucu 1

guvenliYaz, depo.js'teki yaz'ın aynısı; tek farkı dosyayı parametre olarak alması.

İpucu 2

guvenliOku'da iki ayrı try kullan: biri readFile için (yalnız ENOENT'te varsayılan), biri JSON.parse için. Metni ilk try'ın dışında kullanabilmek için let metin ile önce tanımla (Ders 10).

İpucu 3

Dosyanın adı için path.basename(dosya), asıl hatanın mesajı için hata.message.

Çözümü göster
kayit.js
import { readFile, writeFile, rename } from "node:fs/promises"import path from "node:path" async function guvenliOku(dosya, varsayilan) {    let metin    try {        metin = await readFile(dosya, "utf8")    } catch (hata) {        if (hata.code === "ENOENT") {            return varsayilan        }        throw hata    }    try {        return JSON.parse(metin)    } catch (hata) {        throw new Error(`${path.basename(dosya)} bozuk, elle bak (${hata.message})`)    }} async function guvenliYaz(dosya, veri) {    const gecici = dosya + ".tmp"    await writeFile(gecici, JSON.stringify(veri, null, 2), "utf8")    await rename(gecici, dosya)} const yeniOyuncu = { altin: 0, seviye: 1 }const kayit = path.join(import.meta.dirname, "tft-kayit.json") console.log("Dosya yok:", await guvenliOku(path.join(import.meta.dirname, "yok.json"), yeniOyuncu)) await guvenliYaz(kayit, { altin: 50, seviye: 7 })console.log("Yazıp okudum:", await guvenliOku(kayit, yeniOyuncu)) await writeFile(kayit, "", "utf8")try {    await guvenliOku(kayit, yeniOyuncu)} catch (hata) {    console.log("Hata:", hata.message)} const yarim = JSON.stringify({ altin: 50, seviye: 7 }).slice(0, 12)await writeFile(kayit, yarim, "utf8")try {    await guvenliOku(kayit, yeniOyuncu)} catch (hata) {    console.log("Hata:", hata.message)}
Çıktı
Dosya yok: { altin: 0, seviye: 1 }Yazıp okudum: { altin: 50, seviye: 7 }Hata: tft-kayit.json bozuk, elle bak (Unexpected end of JSON input)Hata: tft-kayit.json bozuk, elle bak (Expected double-quoted property name in JSON at position 12 (line 1 column 13))

Neden bozuk dosyada varsayılanı döndürmüyoruz? Çünkü o zaman bir sonraki guvenliYaz sıfırlanmış kaydı diske yazar ve oyuncunun gerçek ilerlemesi kalıcı olarak gider. Yok olan dosya "yeni oyuncu" demek; bozuk dosya ise "burada bir şey ters gitti, dokunmadan önce bak" demek. Mesajdaki dosya adı da, bakılacak yeri gösterir.

Görev 5

Yayın öncesi denetim

Bir klasörü yayına göndermeden önce içinde gizli dosya kalmış mı diye bakan bir program yaz (gorevler\denetle.js). Denemek için program önce kendi yanında yayin adında bir klasör kursun ve içine şu dosyaları yazsın (içerikleri önemsiz, "deneme" olabilir): index.html, stil.css, .env, .env.example, .env.production ve alt klasörde sertifika/sunucu.key. Sonra klasörü alt klasörleriyle birlikte tarasın ve şu kurallara uyan dosyaları listelesin:

  • Adı tam olarak .env olan ya da .env. ile başlayan her dosya tehlikeli; .env.example hariç.
  • Uzantısı .key ya da .pem olan dosyalar (sunucu sertifikalarının gizli anahtarları) tehlikeli.

Beklenen çıktı:

Metin
Yayına gitmemesi gereken 3 dosya var:- .env- .env.production- sertifika\sunucu.key

Tehlikeli dosya yoksa Temiz: yayına hazır yazmalı.

İpucu 1

readdir'e { recursive: true } verirsen alt klasörlerin içini de listeler; adlar sertifika\sunucu.key gibi yolla gelir. Kuralı uygularken yolun son parçasına, path.basename(yol)'a bak.

İpucu 2

Kuralı tehlikeliMi(ad) adında bir fonksiyona koy. İlk satırda .env.example'ı ele: if (ad === ".env.example") return false. Sonra ad === ".env" || ad.startsWith(".env.") (Ders 9).

İpucu 3

path.extname(".env") boş metin verir; Node, adı noktayla başlayan dosyada o noktayı uzantı saymaz. Bu yüzden .env'i uzantıyla değil, adla yakala. .key ve .pem için ise path.extname doğru araç.

Çözümü göster
denetle.js
import { mkdir, writeFile, readdir } from "node:fs/promises"import path from "node:path" const klasor = path.join(import.meta.dirname, "yayin")await mkdir(path.join(klasor, "sertifika"), { recursive: true })const ornekler = ["index.html", "stil.css", ".env", ".env.example", ".env.production", "sertifika/sunucu.key"]for (const ad of ornekler) {    await writeFile(path.join(klasor, ad), "deneme", "utf8")} function tehlikeliMi(ad) {    if (ad === ".env.example") {        return false    }    if (ad === ".env" || ad.startsWith(".env.")) {        return true    }    const uzanti = path.extname(ad)    return uzanti === ".pem" || uzanti === ".key"} const yollar = await readdir(klasor, { recursive: true })const tehlikeliler = yollar.filter((yol) => tehlikeliMi(path.basename(yol))) if (tehlikeliler.length === 0) {    console.log("Temiz: yayına hazır")} else {    console.log(`Yayına gitmemesi gereken ${tehlikeliler.length} dosya var:`)    for (const yol of tehlikeliler) {        console.log("- " + yol)    }}
Çıktı
Yayına gitmemesi gereken 3 dosya var:- .env- .env.production- sertifika\sunucu.key

Sınır denemesi: yayin klasörünü sil, ornekler'i ["index.html", "stil.css", ".env.example", "sertifika/okuBeni.txt"] yap ve yeniden çalıştır: Temiz: yayına hazır çıkmalı. .env.example ilk kuralın tam kenarında duruyor: o da .env. ile başlıyor; fonksiyonun ilk satırındaki istisna olmasa haksız yere listelenirdi. Görevi bitirince yayin klasörünü sil: içindeki .env.production gibi dosyaların Git'e girmesini istemezsin.

Görev 6

Tek şampiyonu sil

API'ye tek bir şampiyonu silen bir route ekle: DELETE /api/sampiyonlar/:id.

  • Yalnız yönetici anahtarıyla çalışsın (yoneticiMi).
  • id tam sayı değilse 400 ve {"hata":"id bir tam sayı olmalı"}.
  • O numarada şampiyon yoksa 404 ve {"hata":"2 numaralı şampiyon yok"} gibi bir mesaj.
  • Silerse 204, gövdesiz.
  • Dosyaya degistir üzerinden yazsın; aynı anda gelen silmeler birbirini ezmesin.

Listede Ahri (1) ve Lux (2) varken beklenen davranış:

İkinci terminal
PS C:\kod\kalici-api> curl.exe -X DELETE http://localhost:3000/api/sampiyonlar/2{"hata":"Yönetici anahtarı eksik ya da yanlış"}PS C:\kod\kalici-api> curl.exe -i -X DELETE http://localhost:3000/api/sampiyonlar/2 -H "Authorization: Bearer gelistirme-anahtari-42"HTTP/1.1 204 No Content...PS C:\kod\kalici-api> curl.exe -X DELETE http://localhost:3000/api/sampiyonlar/2 -H "Authorization: Bearer gelistirme-anahtari-42"{"hata":"2 numaralı şampiyon yok"}PS C:\kod\kalici-api> curl.exe -X DELETE http://localhost:3000/api/sampiyonlar/abc -H "Authorization: Bearer gelistirme-anahtari-42"{"hata":"id bir tam sayı olmalı"}PS C:\kod\kalici-api> curl.exe http://localhost:3000/api/sampiyonlar[{"id":1,"isim":"Ahri"}]
İpucu 1

Sırayı Ders 28'deki sampiyonuBul'dan hatırla: önce Number(req.params.id) ve Number.isInteger, sonra arama. Route'un ikinci parametresi yoneticiMi, üçüncüsü asıl iş.

İpucu 2

degistir'e verdiğin fonksiyon bir şey döndürebilir; degistir onu sana geri verir. Silinip silinmediğini döndür: filtrelemeden önceki uzunluğu sakla, veri.sampiyonlar = veri.sampiyonlar.filter((s) => s.id !== id), sonra iki uzunluğu karşılaştır.

İpucu 3

404 kararını degistir'in dışında ver: içeride yanıt göndermeye kalkma, içerideki fonksiyonun tek işi veriyi değiştirmek.

Çözümü göster

sunucu.js'te listeyi silen route'un altına (app.listen'dan önce) ekle:

sunucu.js
app.delete("/api/sampiyonlar/:id", yoneticiMi, async (req, res) => {    const id = Number(req.params.id)    if (!Number.isInteger(id)) {        return res.status(400).json({ hata: "id bir tam sayı olmalı" })    }    const silindi = await degistir((veri) => {        const onceki = veri.sampiyonlar.length        veri.sampiyonlar = veri.sampiyonlar.filter((s) => s.id !== id)        return veri.sampiyonlar.length < onceki    })    if (!silindi) {        return res.status(404).json({ hata: `${id} numaralı şampiyon yok` })    }    res.status(204).end()})

Yukarıdaki çıktılar bu kodla alındı. Sınır denemeleri: aynı silmeyi ikinci kez yapınca 404; anahtarsız abc isteği 400 değil 401 alır, çünkü yoneticiMi route'tan önce çalışır ve anahtarı olmayana adresin geçerli olup olmadığını bile söylemez. veri.json'a bak: sonId hâlâ 2; sıradaki yeni şampiyon 3 numarayı alır, Lux'ın eski numarası bir daha verilmez. 404 yolunda da dosya yeniden yazılır ama içeriği değişmez; zararsız.

Kendini sına

Soru 1

Bu satır Windows'ta ne yazar?

JavaScript
console.log(path.join("kayitlar", "2026", "..", "ekim.json"))
Cevabı göster

kayitlar\ekim.json. .. bir üst klasör demek: 2026'ya girip geri çıktı. path.join parçaları Windows'un ayırıcısı \ ile birleştirir; aynı kod Linux'ta kayitlar/ekim.json yazar.

Soru 2

.env'de YONETICI_ANAHTARI=Gizli#Anahtar-42 yazıyor. process.env.YONETICI_ANAHTARI ne olur? Nasıl düzeltirsin?

Cevabı göster

"Gizli". Node .env'de #'den sonrasını yorum sayar, değerin ortasında bile. Programın beklediği anahtar Gizli#Anahtar-42 olduğu için her yönetici isteği 401 alır ve sebebi hiç görünmez. Düzeltme: değeri tırnak içine al, YONETICI_ANAHTARI="Gizli#Anahtar-42".

Soru 3

Arkadaşın projeni GitHub'dan indirdi, npm install ve npm run dev yazdı, node: .env: not found aldı. Neden? Ne yapmalı?

Cevabı göster

.env .gitignore'da olduğu için GitHub'a hiç gitmedi; arkadaşının klonunda yok, --env-file=.env de bulamıyor. Arkadaşın Copy-Item .env.example .env ile şablonu kopyalamalı ve boş değerleri kendi değerleriyle doldurmalı. Senin gerçek anahtarını ona mesajla göndermek doğru yol değil; kendi geliştirme anahtarını üretsin.

Soru 4

yaz fonksiyonu writeFile(DOSYA, ...) ile doğrudan yazıyor ve tam yazarken elektrik kesiliyor. Ne olabilir? Geçici dosya ve rename bunu nasıl önler?

Cevabı göster

writeFile dosyayı önce boşaltır, sonra yazar. Kesinti arada olursa veri.json boş ya da yarım kalır, eski veri de gitmiştir; sonraki her okuma SyntaxError: Unexpected end of JSON input verir. Geçici dosyayla yeni veri önce veri.json.tmp'ye yazılır; asıl dosyaya ancak yazma bitince, tek hamlelik (atomik) bir rename ile dokunulur. Kesinti hangi anda olursa olsun veri.json ya eski ya yeni hâliyle sağlam kalır.

Soru 5

.env dosyanı yanlışlıkla GitHub'daki herkese açık deponuza gönderdin ve on dakika sonra fark ettin. Dosyayı silip yeni bir commit attın, depoyu da gizli (Private) yaptın. Yeter mi? Ne yaparsın?

Cevabı göster

Yetmez. On dakika, anahtarı arayan otomatik programların onu bulması için yeterli olabilir; silinen dosya da eski commit'te duruyor. Sıra: (1) içindeki her anahtarı hemen iptal et ve yenisini al, (2) servisin kayıtlarında o andan beri tanımadığın kullanım var mı bak, (3) yeni anahtarları .env'e ve canlıdaki panele yaz, (4) .env'i .gitignore'a ekle ve gerekiyorsa git rm --cached .env ile takipten çıkar. Geçmişi temizlemek isteğe bağlı; anahtarı yenilemek zorunlu.

Soru 6

Express API'nin public klasörüne hangileri girer: index.html, stil.css, uygulama.js (tarayıcıda çalışan), sunucu.js, veri.json, .env, logo.png?

Cevabı göster

index.html, stil.css, uygulama.js ve logo.png: tarayıcıya gitmesi gereken dosyalar. sunucu.js sunucunun kodu, veri.json kullanıcıların verisi, .env gizli ayarlar; üçü de public'in dışında kalır. public'teki her dosyanın bir adresi vardır ve o adresi bilen herkes onu indirir.

Soru 7

$env:PORT = "8080" yazılmış bir terminalde node -e "console.log(process.env.PORT + 1, typeof process.env.PORT)" ne yazar? (node -e, node -p gibi tırnak içindeki kodu çalıştırır, ama sonucu kendisi yazdırmaz; yazdıran console.log.)

Cevabı göster

80801 string. Ortam değişkenleri her zaman metindir; "8080" + 1 metin birleştirmesidir. Sayı gerekiyorsa Number(process.env.PORT) + 1 yazmalısın (8081).

Kayıt noktası

Projeni Git'e kaydet. kalici-api yeni bir proje olduğu için önce git init gerekiyor. Commit'ten önce derste açtığın deneme dosyalarını (dosya-deneme.js, yol-deneme.js, nerede.js, eski-kod.js, bos.js, bos.json, ortam.js, port-hesap.js, anahtar.js, goster.js, maclar klasörü, varsa kurallar.env ve ps-deneme.env) silebilirsin; cokme.js, kalabalik.js, ahri.json ve lux.json API'yi denemenin araçları, kalsınlar. git add .'ten sonra listeye bir kez bak:

Bir sonraki derste sunucun başka servislerle konuşacak: Riot'un API'si gibi anahtar isteyen bir servise .env'deki anahtarla istek atacak, cevap gecikince vazgeçmeyi, hız sınırına uymayı ve anahtarı tarayıcıdan saklayan bir ara sunucu kurmayı öğreneceksin.

İşaret yalnızca bu tarayıcıda saklanır, kimseye gönderilmez.