Gönderdiğiniz
müfredat dosyasını satır satır analiz ederek,
anlamsız ifadeleri, süreleri (dakika/saniye), "Önizleme"
ve "ders" ibrelerini temizledim. İstediğiniz doğrultuda özet
yapmadan, her konuyu derinlemesine açıklayarak, gerçek dünya senaryoları,
pratik örnekler ve uygulamalarla zenginleştirilmiş detaylı bir eğitim rehberi
tasarladım.
Dosya
oldukça uzun ve kapsamlı olduğu için içeriği tam da istediğiniz gibi parça
parça ele alacağız. Bu ilk bölümde, kursun temel vaatlerini içeren "Öğrenecekleriniz"
kısmının detaylı açıklaması ile Bölüm 1 ve Bölüm 2'nin eksiksiz,
derinlemesine konu anlatımlarını ve uygulamalarını bulacaksınız.
🌟
"Öğrenecekleriniz" Bölümünün Detaylı Açıklaması
Bu
paragraf, modern bir arka plan (backend)
geliştiricisinin sıfırdan profesyonel seviyeye (Pro Dev) ulaşırken kazanacağı
tüm sektörel yetkinlikleri haritalandırır. Sadece teorik JavaScript bilgisi
sunmak yerine; veri tabanı yönetimi, güvenlik, gerçek zamanlı (real-time) iletişim, harici API entegrasyonları (ödeme ve
e-posta) ve modern mimari yaklaşımları bir arada sunarak sektöre hazır projeler
üretmeyi amaçlar.
🛠 BÖLÜM 1:
Node.js Temelleri ve Giriş
➡️ Node.js Gerçekte
Nedir ve Neden Popülerdir?
Node.js
bir programlama dili veya bir framework (çatı)
değildir. Node.js, JavaScript kodlarının tarayıcı dışında (sunucu tarafında)
çalışmasını sağlayan açık kaynaklı bir çalışma ortamıdır (Runtime Environment).
Geleneksel
olarak JavaScript yalnızca Google Chrome, Firefox gibi tarayıcılarda web
sayfalarına etkileşim katmak için kullanılırdı. Node.js, Chrome'un kalbi olan V8
Engine motorunu alarak bilgisayarınızın işletim sistemine doğrudan
erişebilen bir yapıya dönüştürdü.
📈 Popülerlik Sebepleri:
➡️ Node.js Nasıl
Çalışır? (Arka Plan Mekanizması)
Node.js'in gücü Asenkron (Asynchronous),
Bloke Olmayan (Non-blocking) ve Olay
Güdümlü (Event-driven) mimarisinden gelir.
➡️ Tarayıcı
(Browser) JavaScript ile Node.js Arasındaki Farklar
|
Özellik |
Tarayıcı
(Browser JS) |
Node.js |
|
Çalışma
Alanı |
Web
Tarayıcıları (Chrome, Safari vb.) |
Sunucu,
Bilgisayarın İşletim Sistemi |
|
Küresel
Nesne |
window, document |
global, process |
|
DOM
Erişimi |
Var
(Butonlar, Formlar manipüle edilebilir) |
Yok
(HTML elemanlarına erişemez) |
|
Dosya
Sistemi |
Yok
(Güvenlik nedeniyle bilgisayar dosyalarına erişemez) |
Var
(fs modülü ile dosya okur/yazar) |
💻 Uygulama 1:
İlk Node.js Kodunu Yazma ve Çalıştırma
Adım
1:
Bilgisayarınızda app.js
adında bir dosya oluşturun.
Adım 2: İçerisine işletim sistemi ve global nesneleri test etmek için şu
kodları yazın:
// Tarayıcıda
çalışmayan, Node.js'e özel global nesne
console.log("Modern
Node.js Kursuna Hoş Geldiniz!");
// Çalışma ortamı
hakkında bilgi alma
console.log("Çalışan
Node Sürümü: " + process.version);
console.log("Mevcut
Dosya Yolu: "
+ __filename);
Adım
3: Terminali
açın, dosyanın olduğu dizine gelin ve şu komutla çalıştırın:
node app.js
Terminal
çıktısında tarayıcıya ihtiyaç duymadan JavaScript kodunuzun bilgisayarınızda
doğrudan çalıştığını göreceksiniz.
📦 BÖLÜM 2:
Node.js Modül Sistemi (Module System)
Node.js
projelerinde kodlarımızı parçalara ayırarak yönetiriz. Her bir JavaScript
dosyası kendi içinde izole bir Modüldür. Kodların birbirine karışmasını
engeller ve tekrar kullanılabilirliği (reusability)
sağlar.
➡️ Kendi Modülünü
Oluşturma, İhraç (Export) ve İthal (Import) Etme
Bir
dosyada yazdığımız fonksiyonu veya değişkeni başka bir dosyada kullanmak için module.exports ile dışarı aktarmalı, kullanacağımız dosyada ise require() ile içeri çekmeliyiz.
💻 Uygulama 2: Özel Modül
Tasarımı
Adım
1: matematik.js adında yardımcı bir modül
dosyası oluşturalım:
const topla = (sayi1, sayi2) => sayi1 + sayi2;
const carp = (sayi1, sayi2) => sayi1 * sayi2;
// Fonksiyonları dış
dünyaya ihraç ediyoruz
module.exports = {
topla,
carp
};
Adım
2: Ana
dosyamız olan index.js
içerisinden bu modüle erişelim:
// require
ile yerel modülümüzü import ediyoruz (./ kullanmayı unutmayın)
const mat = require('./matematik');
const sonuc1 = mat.topla(15,
25);
const sonuc2 = mat.carp(5,
10);
console.log("Toplama
Sonucu:",
sonuc1); // Çıktı: 40
console.log("Çarpma
Sonucu:",
sonuc2); // Çıktı: 50
➡️ Node.js
Yerleşik (Built-in) Çirdek
Modülleri
Node.js,
bilgisayar kaynaklarına erişebilmemiz için kuruluma ihtiyaç duymayan güçlü
çekirdek modüllerle birlikte gelir.
1.
Path Modülü (Dosya Yolları Yönetimi)
Farklı
işletim sistemlerinde (Windows \ kullanırken, Linux/Mac / kullanır) dosya yollarının çökmeden, çapraz platform uyumlu
çalışmasını sağlar.
const path = require('path');
// Bir dosya yolunun
parçalarını analiz etme
const dosyaBilgisi = path.parse(__filename);
console.log("Dosya
Adı:",
dosyaBilgisi.name);
console.log("Uzantısı:", dosyaBilgisi.ext);
// Güvenli şekilde
dosya yolu birleştirme
const tamYol = path.join(__dirname,
'veriler', 'kullanici.json');
console.log("Oluşturulan
Güvenli Yol:",
tamYol);
2.
OS Modülü (İşletim Sistemi Bilgileri)
Uygulamanın
çalıştığı bilgisayarın donanımsal ve sistemsel verilerine erişir.
const os
= require('os');
const toplamHafiza = os.totalmem() / (1024
* 1024 * 1024); //
GB cinsine çevirme
const bosHafiza = os.freemem() / (1024
* 1024 * 1024);
console.log("İşletim
Sistemi Platformu:",
os.platform());
console.log(`Toplam
Bellek: ${toplamHafiza.toFixed(2)}
GB`);
console.log(`Boş
Bellek: ${bosHafiza.toFixed(2)}
GB`);
console.log("İşlemci
Mimarisi:",
os.arch());
3.
FS Modülü (File System - Dosya İşlemleri)
Bilgisayarda
dosya oluşturma, okuma, güncelleme ve silme işlemlerini yürütür.
const fs
= require('fs');
// 1. Dosya Yazma
(Senkron Örnek)
fs.writeFileSync('gunluk.txt', 'Bugün
Node.js modül sistemini öğrendim.\n');
console.log("Dosya
başarıyla oluşturuldu.");
// 2. Dosyaya Yeni
Veri Ekleme
fs.appendFileSync('gunluk.txt', 'Bu
satır sonradan eklendi.');
// 3. Dosya Okuma
const icerik = fs.readFileSync('gunluk.txt', 'utf-8');
console.log("---
Dosya İçeriği ---");
console.log(icerik);
🌐 HTTP Modülü
ile İlk Web Sunucusunu Kurmak ve Rotalama (Routing)
Express
gibi framework'lerin temelini oluşturan bu modül,
bilgisayarınızı internet üzerinden istek kabul edebilen bir web sunucusuna
dönüştürür.
💻 Uygulama 3: Rotalı HTTP
Sunucusu
Aşağıdaki
kodları server.js
adıyla kaydedip çalıştırın ve tarayıcınızdan http://localhost:5000 adresine gidin:
const http = require('http');
const sunucu = http.createServer((istek,
cevap) => {
//
Tarayıcıya Türkçe karakterleri doğru okuyabilmesi için header
gönderiyoruz
cevap.writeHead(200,
{ 'Content-Type': 'text/html; charset=utf-8' });
//
Temel Rotalama (Routing) Mekanizması
if (istek.url === '/'
|| istek.url === '/anasayfa') {
cevap.write('<h1>Ana Sayfaya Hoş Geldiniz</h1><p>Node.js
çekirdek sunucusu devrede.</p>');
}
else if (istek.url === '/hakkimizda') {
cevap.write('<h1>Hakkımızda Sayfası</h1><p>Biz modern backend geliştiricileriyiz.</p>');
}
else if (istek.url === '/api/kullanici') {
//
API gibi JSON veri dönme örneği
cevap.writeHead(200,
{ 'Content-Type': 'application/json' });
const kullanici = { id: 1, isim: "Ahmet", rol: "Admin" };
cevap.write(JSON.stringify(kullanici));
}
else {
//
Hatalı rota yönetimi (404)
cevap.writeHead(404,
{ 'Content-Type': 'text/html; charset=utf-8' });
cevap.write('<h1>404 Sayfa Bulunamadı!</h1>');
}
cevap.end(); //
İsteği sonlandır ve cevabı gönder
});
// Sunucunun
dinleyeceği portu belirliyoruz
sunucu.listen(5000, () => {
console.log("Sunucu 5000 portu üzerinde yerel olarak
başlatıldı...");
});
Harika,
hız kesmeden ve yapıyı bozmadan devam ediyoruz. Sırada projelerimizin yönetim
merkezi olan Bölüm 3: NPM (Node Package Manager) ve backend
geliştirmenin kalbi sayılan Bölüm 4: Express.js ile RESTful
API Geliştirme konuları var. Temizlik kurallarına sadık kalarak, her konuyu
derinlemesine, pratik uygulamalar ve canlı kod örnekleriyle ele alıyorum.
📦 BÖLÜM 3: Node Package Manager (NPM)
NPM,
dünya genelindeki geliştiriciler tarafından yazılmış milyonlarca hazır kod
kütüphanesini (paketini) projemize tek tıkla dahil etmemizi sağlayan, dünyanın
en büyük yazılım kayıt defteri ve paket yönetim sistemidir.
➡️ Projenin
Kimlik Kartı: package.json
Bir
Node.js projesine başlarken ilk yapılması gereken işlem projeyi initialize etmek, yani bir package.json dosyası oluşturmaktır. Bu
dosya projenin adını, versiyonunu, çalıştırılma komutlarını (scripts) ve projede kullanılan tüm dış bağımlılıkları
(kütüphaneleri) listeler.
💻 Uygulama 1: Proje Başlatma
ve Paket Yönetimi
Adım
1:
Terminalinizde boş bir klasör oluşturup içine girin ve şu komutu çalıştırın:
npm init -y
(Bu
komut, soru sormadan varsayılan ayarlarla hızlıca bir package.json dosyası üretir).
Adım
2 (Kütüphane Yükleme - Production
Dependency): Uygulamanın çalışırken ihtiyaç duyacağı harici bir
kütüphaneyi yükleyelim. Örneğin tarih yönetimi için çok popüler olan moment kütüphanesini kuralım:
npm install moment
Bu
işlemden sonra klasörünüzde node_modules adında bir klasör oluşur
(paketlerin fiziksel kodları buradadır) ve package.json içerisindeki "dependencies" alanına moment otomatik olarak eklenir.
Adım
3 (Geliştirici Paketi Yükleme - Developer Dependency): Sadece kodu yazarken (geliştirme aşamasında) işimize
yarayacak, ancak proje canlı sunucuya (production)
taşındığında çalışmasına gerek olmayan paketleri --save-dev veya -D bayrağı ile kurarız:
npm install eslint --save-dev
Bu
paket package.json içinde "devDependencies" kısmına yazılır ve sunucu
maliyetlerini/canlı ortam boyutunu hafifletmek için ayrıştırılır.
➡️ Temizlik ve
Paket Güncelleme Komutları
·
npm uninstall
moment
·
npx depcheck
·
npx npm-check-updates
-u
·
npm install
🌐 BÖLÜM 4:
Express.js ile RESTful API İnşa Etmek
Node.js'in yerleşik http modülü ile büyük projeler yönetmek çok zor ve karmaşıktır. Express.js,
Node.js üzerinde web sunucuları ve API'ler inşa etmek
için kullanılan en popüler, minimalist ve hızlı web framework
(çatı) yapısıdır.
➡️ API vs REST API Kavramları
➡️ Çevresel Değişkenler
(Environment Variables - .env)
Veri
tabanı şifreleri, gizli API anahtarları veya port numaraları gibi hassas
veriler asla kaynak kodun içine doğrudan yazılmamalıdır (Hardcoded).
Bunun yerine projenin kök dizininde gizli bir .env
dosyası oluşturulur ve dotenv paketiyle okunur. Bu sayede kod güvenliği sağlanır ve yerel
bilgisayar ile canlı sunucu ayarları kolayca ayrışır.
💻 Uygulama 2:
Express.js ile Tam Donanımlı Todo CRUD API Projesi
Şimdi
sıfırdan; otomatik yeniden başlatma (nodemon), gizli ayarlar (.env), rota parametreleri, veri
doğrulama (Joi) ve tam CRUD (Create, Read,
Update, Delete) işlevlerine sahip profesyonel bir API
tasarlayacağız.
Adım
1: Gerekli Paketlerin Kurulumu
npm install express dotenv joi
npm install nodemon --save-dev
Adım
2: Konfigürasyon Dosyalarının Hazırlanması
Projenizin
kök dizinine .env
dosyası açın:
PORT=4000
NODE_ENV=development
package.json dosyasındaki "scripts" alanını düzenleyin (böylece
kod değiştikçe sunucu otomatik yenilenecek):
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
}
Adım
3: app.js API Kodlarının Yazılması
// Gerekli modülleri
dahil ediyoruz
const express = require('express');
const joi
= require('joi');
require('dotenv').config(); // .env dosyasını okur
const app
= express();
// Middleware:
Gelen isteklerdeki JSON gövdesini (body) okuyabilmek için şarttır
app.use(express.json());
// Bellekte geçici
veri tabanı (Hafıza simülasyonu)
let todolar = [
{ id: 1, baslik: "Node.js
Modüllerini Çalış",
tamamlandi: true },
{ id: 2, baslik: "Express CRUD API
Tasarla",
tamamlandi: false }
];
// --- DATA VALIDATION
(JOI ŞEMASI) ---
// İsteklerin güvenli
ve doğru formatta gelmesini sağlar
const todoSemasi Kontrol = (todo) => {
const sema = joi.object({
baslik:
joi.string().min(3).required(), // En az 3 karakter ve zorunlu
tamamlandi:
joi.boolean()
});
return sema.validate(todo);
};
// 1. GET: Tüm Todoları Getir (Fetch All)
app.get('/api/todos', (req,
res) => {
//
res.json() performans ve
JSON standartları açısından res.send'den daha
uygundur
res.status(200).json(todolar);
});
// 2. GET: ID ile Tek
Bir Todo Getir (Route Parameter)
app.get('/api/todos/:id', (req,
res) => {
const todo = todolar.find(t => t.id === parseInt(req.params.id));
if (!todo) {
return res.status(404).json({ mesaj: "Aranan
görev bulunamadı!"
});
}
res.status(200).json(todo);
});
// 3. POST: Yeni Todo Ekle (Create)
app.post('/api/todos', (req,
res) => {
//
Veri doğrulama kontrolü
const { error } = todoSemasi Kontrol(req.body);
if (error) {
return res.status(400).json({ hata: error.details[0].message });
}
const yeniTodo = {
id:
todolar.length + 1,
baslik: req.body.baslik,
tamamlandi: req.body.tamamlandi
|| false
};
todolar.push(yeniTodo);
res.status(201).json({ mesaj: "Todo başarıyla eklendi", veri: yeniTodo });
});
// 4. PUT: Todo Güncelle (Update)
app.put('/api/todos/:id', (req,
res) => {
const todo = todolar.find(t => t.id === parseInt(req.params.id));
if (!todo) {
return res.status(404).json({ mesaj: "Güncellenmek
istenen görev mevcut değil." });
}
//
Gelen yeni verinin doğrulanması
const { error } = todoSemasi Kontrol(req.body);
if (error) {
return res.status(400).json({ hata: error.details[0].message });
}
todo.baslik
= req.body.baslik;
todo.tamamlandi
= req.body.tamamlandi;
res.status(200).json({ mesaj: "Görev
güncellendi",
veri: todo });
});
// 5. DELETE: Todo Sil (Delete)
app.delete('/api/todos/:id', (req,
res) => {
const todoIndex = todolar.findIndex(t => t.id === parseInt(req.params.id));
if (todoIndex ===
-1) {
return res.status(404).json({ mesaj: "Silinmek
istenen görev bulunamadı." });
}
//
Diziden elemanı çıkartıyoruz
const silinenTodo = todolar.splice(todoIndex, 1);
res.status(200).json({ mesaj: "Görev
başarıyla silindi",
veri: silinenTodo[0]
});
});
// Sunucuyu Çevresel
Değişkenden Gelen Port ile Başlatma
const PORT = process.env.PORT
|| 3000;
app.listen(PORT, () => {
console.log(`Sunucu ${process.env.NODE_ENV} modunda, ${PORT} portunda
çalışıyor...`);
});
Adım
4: Test Etme
Terminalinizden
npm run dev komutunu yazarak API
sunucunuzu ayağa kaldırın. Postman veya benzeri bir
API test aracı ile http://localhost:4000/api/todos adresine istekler atarak
koda verdiğimiz tepkileri (Status kodları: 200, 201,
400, 404) canlı inceleyebilirsiniz.
Hız
kesmeden ve derinliği koruyarak devam ediyoruz. Sırada Express.js mimarisinin
can damarı olan Bölüm 5: İleri Düzey Express Kavramları (Middleware
& Güvenlik) ve Node.js mülakatlarının en popüler konusu olan Bölüm
6: Asenkron JavaScript Mekanizmaları var.
Müfredatın
orijinal sırasına sadık kalarak, her konuyu sektörel pratikler, gerçek dünya
riskleri ve somut projelerle açıklıyorum.
🛡️ BÖLÜM 5:
İleri Düzey Express Kavramları (Master Express)
Express.js
uygulamanızın esnekliği ve gücü Middleware
(Ara Yazılım) yapısından gelir. İstek (Request)
sunucuya ulaştığı an ile cevap (Response) üretildiği
an arasında çalışan tüm fonksiyonlara middleware
denir.
➡️ Custom (Özel) Middleware Tasarımı
ve Mantığı
Her middleware fonksiyonu üç parametre alır: req,
res
ve next. Eğer fonksiyon içinde next() çağrılmazsa, istek sunucuda
asılı kalır ve tarayıcı sonsuza kadar döner.
// Özel bir Loglama (Logger) Middleware Tasarımı
const istekLoglayici = (req,
res, next) => {
const zaman = new
Date().toISOString();
console.log(`[${zaman}] Yöntem: ${req.method} | Rota: ${req.url} | IP: ${req.ip}`);
next(); //
İsteği bir sonraki fonksiyona veya rotaya aktarır
};
➡️ Kurumsal Üçüncü
Parti Kütüphaneler (Third-Party Middlewares)
Profesyonel
Node.js projelerinde güvenlik ve izlenebilirlik için şu iki kütüphane standart
olarak kurulur:
💻 Uygulama 1:
Kurumsal Seviye İleri Düzey Express Sunucusu
Bu
uygulamada özel middleware'ler, güvenlik paketleri,
statik dosya paylaşımı (resim/CSS barındırma) ve ortama göre kodlama
(Development vs. Production) konseptlerini tek bir
projede birleştiriyoruz.
Adım
1: Gerekli
kütüphaneleri kurun:
npm install express helmet morgan dotenv
Adım
2: Proje
dizininde public adında bir klasör oluşturun ve içine profil.jpg adında örnek bir resim
yükleyin.
Adım
3: server.js dosyasını oluşturup kurumsal
mimariyi inşa edin:
const express = require('express');
const helmet = require('helmet');
const morgan = require('morgan');
const path = require('path');
require('dotenv').config();
const app
= express();
// --- 1. GÜVENLİK VE
YERLEŞİK MIDDLEWARE KATMANI ---
app.use(helmet()); // HTTP
başlıklarını zırhlandırır
app.use(express.json());
// Gelen JSON gövdelerini ayrıştırır
// --- 2. STATİK DOSYA
PAYLAŞIMI ---
// public
klasöründeki her şeye tarayıcıdan doğrudan erişilmesini sağlar
// Örn:
http://localhost:5000/resimler/profil.jpg
app.use('/resimler', express.static(path.join(__dirname, 'public')));
// --- 3. ORTAMA GÖRE
KODLAMA (ENVIRONMENT CONFIG) ---
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev')); // Geliştirme modunda detaylı ve renkli loglama yapar
console.log("🛠️ Geliştirme
(Development) ortamı aktif. Detaylı loglar devrede.");
}
else if
(process.env.NODE_ENV === 'production') {
app.use(morgan('combined')); // Canlı ortamda
standart apache formatında log üretir
console.log("🚀 Canlı (Production) ortamı aktif. Güvenlik ve performans modu
maksimumda.");
}
// --- 4. ÖZEL KİMLİK
DOĞRULAMA (AUTH) MIDDLEWARE ---
const adminKontrol = (req,
res, next) => {
const apiKey = req.headers['x-api-key'];
if (apiKey
&& apiKey === 'gizli_token_123') {
next(); // API
anahtarı doğruysa geçişe izin ver
} else {
res.status(403).json({ hata: "Yetkisiz
Erişim! Geçerli bir X-API-KEY sağlanmadı." });
}
};
// --- 5. ROTALAR ---
app.get('/api/kamu-verisi', (req,
res) => {
res.json({
mesaj: "Bu
veriyi herkes görebilir." });
});
// Korumalı Rota:
Araya adminKontrol middleware'ini
enjekte ediyoruz
app.get('/api/gizli-raporlar', adminKontrol,
(req, res)
=> {
res.json({
finansalDurum: "Mükemmel", sirketKari: "%45" });
});
const PORT = process.env.PORT
|| 5000;
app.listen(PORT, () => {
console.log(`Gelişmiş Express Sunucusu ${PORT}
portunda yayında...`);
});
🔄 BÖLÜM 6:
Asenkron JavaScript (Asynchronous JS)
Node.js
tek bir iş parçacığıyla (Single-Thread) çalıştığı
için, diskten dosya okuma veya veri tabanına sorgu atma gibi ağır işlerin
işlemciyi kilitlememesi gerekir. Bu yüzden asenkron programlama Node.js'in temel taşıdır.
➡️ Evrim Süreci:
Callbacks, Callback Hell, Promises ve Async/Await
1.
Callbacks (Geri Çağırmalar):
Bir
işlem bittiğinde tetiklenen fonksiyonlardır. Ancak iç içe asenkron işlemler
arttıkça kod okunamaz hale gelir ve sağa doğru uzayan Callback
Hell (Geri Çağırma Cehennemi) yapısı oluşur.
// Callback
Hell Örneği (Yazılması ve bakımı tam bir kabustur)
kullaniciyiBul(id, (err,
kullanici) => {
siparisleriniGetir(kullanici.id, (err,
siparisler) => {
faturaDetayiniBul(siparisler[0].id, (err,
fatura) => {
// Kod okunamaz hale geldi...
});
});
});
2.
Promises (Sözler):
Asenkron
işlemin gelecekteki sonucunu (başarı: Resolve, başarısızlık: Reject) temsil eden nesnelerdir. .then() ve .catch() bloklarıyla zincirleme (Chaining) olarak yönetilir.
3.
Async / Await:
Promise yapısının üzerine inşa edilmiş modern söz dizimidir (Syntactic Sugar). Asenkron kodu
sanki düz senkron kod yazıyormuş gibi yukarıdan aşağıya, temizce yazmamızı
sağlar. Hatalar klasik try...catch bloklarıyla yakalanır.
💻 Uygulama 2:
Gerçekçi E-Ticaret Senaryolu Asenkron Akış Simülasyonu
Aşağıdaki
uygulama, bir e-ticaret sistemindeki veri tabanı gecikmelerini simüle ederek
asenkron evrimi uygulamalı olarak gösterir. index.js adıyla kaydedip test edin:
// --- VERİ TABANI
SİMÜLASYONU (PROMISE TABANLI) ---
const kullaniciGetir = (userId) => {
return new
Promise((resolve, reject) => {
setTimeout(() => {
console.log("1. 👤 Kullanıcı verisi veri
tabanından çekildi.");
if (userId === 99)
{
resolve({ id:
99, isim: "Can", uyelik: "Premium" });
} else
{
reject(new Error("Kullanıcı veri tabanında bulunamadı!"));
}
}, 1500); //
1.5 saniye gecikme simülasyonu
});
};
const sepetiBul = (kullaniciIsmi) => {
return new
Promise((resolve) => {
setTimeout(() => {
console.log(`2. 🛒 ${kullaniciIsmi} isimli kullanıcının aktif sepeti getirildi.`);
resolve({ urunAdet: 3, toplamTutar: 1250 });
}, 1000);
});
};
const odemeYap = (kartNo, tutar) => {
return new
Promise((resolve, reject) => {
setTimeout(() => {
if (kartNo.startsWith("4")) {
console.log(`3. 💳 ${tutar}
TL tutarındaki ödeme başarıyla tahsil edildi.`);
resolve({ odemeId: "PAY_XYZ890", durum: "Onaylandi" });
} else
{
reject(new Error("Ödeme Reddedildi! Geçersiz kart numarası."));
}
}, 1200);
});
};
// --- MODERN
ASYNC/AWAIT YÖNTEMİYLE AKIŞ YÖNETİMİ ---
const eTicaretSatinAlmaAkisi = async () => {
try {
console.log("⚡ Sipariş süreci başlatılıyor...");
const baslangic = Date.now();
//
Sırasıyla birbirine bağlı asenkron adımları bekliyoruz (await)
const kullanici = await kullaniciGetir(99);
const sepet = await sepetiBul(kullanici.isim);
const odemeSonucu = await odemeYap("4242-XXXX-XXXX-XXXX", sepet.toplamTutar);
const bitis = Date.now();
console.log("🎉 Başarılı! Sipariş
Makbuzu:",
odemeSonucu);
console.log(`⏱️ Toplam İşlem Süresi:
${(bitis
- baslangic) / 1000} saniye.`);
} catch (hata) {
//
Tüm zincirdeki herhangi bir hata buraya düşer
console.error("🚨 Sipariş Adımında Hata
Çıktı:",
hata.message);
}
};
// Fonksiyonu
tetikleyelim
eTicaretSatinAlmaAkisi();
Kesintisiz
bir şekilde, müfredatımızın en hacimli ve en önemli veri katmanı olan Bölüm
7: MongoDB & Mongoose
Giriş ve Bölüm 8: İleri Düzey MongoDB
Uzmanlığı (Advanced MongoDB Mastery)
konularına geçiyoruz.
Zaman
ve reklam ibarelerinden tamamen arındırılmış, derinlemesine konu anlatımları,
SQL vs NoSQL mimari
karşılaştırmaları, regex yapıları, indeksleme
deneyleri ve gerçekçi veri tabanı senaryolarını içeren rehberimize devam
ediyoruz.
🗄️ BÖLÜM 7: MongoDB & Mongoose Giriş
➡️ SQL ve NoSQL Arasındaki Yapısal Farklar
Doğru
veri tabanını seçmek backend mimarisinin geleceğini
belirler.
|
Özellik |
SQL
(İlişkisel - Örn: PostgreSQL, MySQL) |
NoSQL (Doküman Tabanlı - Örn: MongoDB) |
|
Veri
Yapısı |
Tablolar,
satırlar (rows) ve sütunlardan (columns)
oluşur. |
JSON
benzeri esnek BSON (Binary JSON) dokümanlarından
oluşur. |
|
Şema
Katılığı |
Sabittir.
Yeni sütun eklemek için tüm tabloyu göç ettirmek (migration)
gerekir. |
Esnektir
(Dinamik Şema). Her doküman farklı alanlara sahip olabilir. |
|
Ölçeklenebilirlik |
Dikey
(Vertical) ölçeklenir (Daha güçlü CPU/RAM gerekir). |
Yatay
(Horizontal) ölçeklenir (Sharding ile binlerce ucuz
sunucuya dağıtılır). |
|
İlişkiler |
JOIN
komutları ile tablolar birbirine sıkı sıkıya bağlanır. |
Dokümanlar
iç içe gömülebilir (Embed) veya referans
verilebilir. |
➡️ Mongoose Nedir ve Şemalar (Schemas)
Neden Önemlidir?
MongoDB doğası gereği şemasızdır (schemaless).
Bir koleksiyona şifre alanına sayı, email alanına
rastgele bir metin yazarak veri kaydedebilirsiniz. Bu esneklik kaosa yol
açmasın diye Mongoose kütüphanesini
kullanırız.
Mongoose, bir ODM (Object Data Modeling)
aracıdır. JavaScript kodlarımız ile MongoDB arasında
kurallar koymamızı sağlar. Verilerin tipini belirler, doğrular (Validation) ve iş kuralları dayatır.
💻 Uygulama 1:
Tam Donanımlı MongoDB Bağlantısı ve Gelişmiş
Filtreleme API'si
Bu
uygulamada bir e-ticaret ürün kataloğu simülasyonu üzerinden MongoDB bağlantısını yapacak; Mongoose
şemalarını, karşılaştırma operatörlerini ($gt, $lte), mantıksal operatörleri ($or, $and), Regex
(düzenli ifadeler) ile aramayı ve Sayfalamayı (Pagination) tek bir projede ayağa
kaldıracağız.
Adım
1: Gerekli
paketleri kurun:
npm install express mongoose dotenv
Adım
2: app.js dosyasını oluşturup veri
tabanı operasyonlarını kodlayın:
const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();
const app
= express();
app.use(express.json());
// --- 1. MONGODB
BAĞLANTISI ---
// Yerel veya MongoDB Atlas bulut bağlantı dizesi
const MONGO_URI = process.env.MONGO_URI
|| 'mongodb://localhost:2017/kurs_eticaret';
mongoose.connect(MONGO_URI)
.then(()
=> console.log("🍃 MongoDB'ye başarıyla
bağlanıldı..."))
.catch(hata
=> console.error("🚨 Veri tabanı bağlantı
hatası:",
hata));
// --- 2. URUN ŞEMASI
VE MODEL TANIMLANMASI ---
const urunSemasi = new
mongoose.Schema({
ad:
{ type: String, required: true, trim: true },
kategori: { type: String, required: true },
fiyat: { type: Number, required: true },
stok: { type: Number, default: 0 },
etiketler: [String],
olusturulmaTarihi: { type: Date, default: Date.now }
});
const Urun = mongoose.model('Urun',
urunSemasi);
// --- 3. SEED DATA
(TEST VERİSİ EKLEME ROUTE) ---
app.post('/api/urunler/seed',
async (req,
res) => {
try {
await Urun.deleteMany();
// Eskileri temizle
const testUrunleri = [
{ ad: "MacBook Pro M3", kategori: "Elektronik", fiyat: 85000, stok: 15, etiketler: ["apple", "laptop", "premium"] },
{ ad: "Dell
XPS 13",
kategori: "Elektronik", fiyat: 62000, stok: 8, etiketler: ["dell", "laptop", "windows"] },
{ ad: "Kablosuz
Oyuncu Faresi",
kategori: "Aksesuar", fiyat: 2500, stok: 50, etiketler: ["gaming", "mouse"] },
{ ad: "Mekanik
Klavye RGB",
kategori: "Aksesuar", fiyat: 4500, stok: 0, etiketler: ["gaming", "keyboard"] },
{ ad: "Ergonomik
Çalışma Koltuğu",
kategori: "Mobilya", fiyat: 12000, stok: 5, etiketler: ["ofis", "koltuk"] }
];
await Urun.insertMany(testUrunleri);
res.status(201).json({ mesaj: "Test
verileri başarıyla yüklendi." });
} catch (error) {
res.status(500).json({ hata: error.message
});
}
});
// --- 4. GELİŞMİŞ
SORGULAMA VE PAGINATION API ---
// İstek Örneği: /api/urunler?sayfa=1&limit=2&minFiyat=3000&ara=MacBook
app.get('/api/urunler', async (req, res)
=> {
try {
//
Query parametrelerini alma ve varsayılan değerler atama
let { sayfa, limit, ara, minFiyat, maxFiyat, kategori } = req.query;
sayfa = parseInt(sayfa) || 1;
limit = parseInt(limit) || 10;
const atlanacakDokuman = (sayfa - 1) * limit;
//
Dinamik filtre nesnesi oluşturma
let sorguFiltresi = {};
//
A) REGEX İLE METİN ARAMASI (Büyük/Küçük harf duyarsız 'i')
if (ara) {
sorguFiltresi.ad
= { $regex: ara, $options: 'i' };
}
//
B) KARŞILAŞTIRMA OPERATÖRLERİ ($gte: Büyük Eşit, $lte: Küçük Eşit)
if (minFiyat || maxFiyat) {
sorguFiltresi.fiyat
= {};
if (minFiyat)
sorguFiltresi.fiyat.$gte = Number(minFiyat);
if (maxFiyat)
sorguFiltresi.fiyat.$lte = Number(maxFiyat);
}
if (kategori) {
sorguFiltresi.kategori
= kategori;
}
//
MANTIKSAL OPERATÖR ÖRNEĞİ (Örn: Stokta olanlar VEYA
fiyatı 5000'den az olanlar)
//
sorguFiltresi = { $or: [ { stok: { $gt: 0 } }, {
fiyat: { $lt: 5000 } } ] };
//
Veri tabanından filtreye, sayfalamaya göre verileri çekme
const urunler = await Urun.find(sorguFiltresi)
.skip(atlanacakDokuman)
.limit(limit)
.sort({ fiyat: 1 }); // Fiyata göre artan sıralama
//
Toplam doküman sayısını bulma (Ön yüzdeki sayfalama butonları için)
const toplamDokuman = await Urun.countDocuments(sorguFiltresi);
res.status(200).json({
mevcutSayfa: sayfa,
toplamSayfa: Math.ceil(toplamDokuman / limit),
toplamUrunSayisi: toplamDokuman,
veri: urunler
});
} catch (error) {
res.status(500).json({ hata: error.message
});
}
});
// --- 5. CRUD:
GÜNCELLEME VE SİLME METODLARI ---
// findByIdAndUpdate
kullanımı
app.put('/api/urunler/:id', async (req, res)
=> {
try {
const guncellenenUrun = await Urun.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true } // new:
true güncel veriyi döner, runValidators:
şema kurallarını işletir
);
if (!guncellenenUrun)
return res.status(404).json({ mesaj: "Ürün
bulunamadı"
});
res.json(guncellenenUrun);
} catch (error) {
res.status(400).json({ hata: error.message
});
}
});
const PORT = process.env.PORT
|| 5000;
app.listen(PORT, () => console.log(`Sunucu ${PORT} üzerinde aktif.`));
🚀 BÖLÜM 8:
Gelişmiş MongoDB Uzmanlığı (Advanced MongoDB)
➡️ Veri
Modelleme İlişkileri: Reference vs. Embed (Gömülü)
MongoDB'de tablolar arası JOIN olmadığı için ilişkileri kurgulamanın iki yolu vardır:
💻 Uygulama 2:
Canlı İndeksleme (Indexing) Performans Deneyi ve
İlişkiler
Bu
uygulamada hem Referans (Yazar-Kitap) ilişkisini kuracağız hem de İndekslemenin
(Indexing) veri tabanı arama hızını nasıl 100 kat
hızlandırdığını canlı bir kod deneyi ile ispatlayacağız.
const mongoose = require('mongoose');
// --- 1. REFERANS
İLİŞKİSİ MODEL TANIMLARI ---
const yazarSemasi = new
mongoose.Schema({
isim: String,
biyografi: String
});
const Yazar = mongoose.model('Yazar',
yazarSemasi);
const kitapSemasi = new
mongoose.Schema({
baslik: String,
yayinYili: Number,
//
Yazar modeline referans bağı oluşturuyoruz
yazarId: { type: mongoose.Schema.Types.ObjectId, ref: 'Yazar' }
});
const Kitap = mongoose.model('Kitap',
kitapSemasi);
// --- 2. PERFORMANS
TESTİ İÇİN BÜYÜK VERİ ŞEMASI (INDEX DENEYİ) ---
const performansSemasi = new
mongoose.Schema({
tcNo: { type: String, unique: true }, // Benzersiz alan
eposta: { type: String },
skor: Number
});
// DIKKAT: Aşağıdaki
satır eposta alanına indeks ekler. Deney için şimdilik yorumda bırakabilir veya
açabilirsiniz.
// performansSemasi.index({ eposta: 1 });
const Musteri = mongoose.model('Musteri', performansSemasi);
// --- ANA TEST
FONKSİYONU ---
const advancedMongoRun = async () => {
await mongoose.connect('mongodb://localhost:2017/gelismis_mongo');
console.log("Bağlantı kuruldu.");
//
--- SENARYO A: POPULATE İLE VERİ ÇEKME ---
//
1. Yeni bir yazar ekle
const yeniYazar = await Yazar.create({
isim: "Fyodor Dostoyevski", biyografi: "Rus
Roman Yazarı"
});
//
2. Yazara bağlı bir kitap ekle (Referans vererek)
await Kitap.create({
baslik: "Suç
ve Ceza",
yayinYili: 1866, yazarId: yeniYazar._id });
//
3. Kitapları yazar verileriyle birlikte tek sorguda çekme (Populate)
const kitapDetayli = await Kitap.findOne({ baslik: "Suç
ve Ceza"
}).populate('yazarId');
console.log("\n📚 Referanslı Veri (Populate Sonucu):");
console.log(kitapDetayli);
//
--- SENARYO B: İNDEKS (INDEX) PERFORMANS DENEYİ ---
console.log("\n⚡ 100.000 Test verisi
oluşturuluyor, lütfen bekleyin...");
await Musteri.deleteMany();
let musteriler = [];
for (let
i = 1; i <= 100000;
i++) {
musteriler.push({
tcNo: `TC_${100000
+ i}`,
eposta: `kullanici_${i}@sirket.com`,
skor: Math.floor(Math.random()
* 1000)
});
}
await Musteri.insertMany(musteriler); //
100 bin veriyi toplu basıyoruz
console.log("✅ 100.000 veri tabana
yazıldı.");
//
İndekssiz Arama Hızı Ölçümü
const t0 = performance.now();
const sonucIndekssiz = await Musteri.findOne({ eposta:
'kullanici_89542@sirket.com' }).explain('executionStats');
const t1 = performance.now();
console.log(`\n🔍 İndekssiz
Arama İstatistikleri:`);
console.log(`- Aranan Doküman Sayısı: ${sonucIndekssiz.executionStats.totalDocsExamined} adet doküman taranmak zorunda kaldı!`);
console.log(`- Kodun Harcadığı Süre: ${(t1 - t0).toFixed(4)} milisaniye.`);
//
ŞİMDİ CANLI İNDEKS EKLEYELİM
console.log("\n🔧 Arka planda 'eposta'
alanına B-Tree Indeksi
kuruluyor...");
await Musteri.collection.createIndex({
eposta: 1 });
console.log("🚀 İndeks oluşturuldu.
Yeniden aranıyor...");
//
İndeksli Arama Hızı Ölçümü
const t2 = performance.now();
const sonucIndeksli = await Musteri.findOne({ eposta:
'kullanici_89542@sirket.com' }).explain('executionStats');
const t3 = performance.now();
console.log(`\n🎯 İndeksli (Optimized) Arama İstatistikleri:`);
console.log(`- Aranan Doküman Sayısı: ${sonucIndeksli.executionStats.totalDocsExamined} (100.000 dokümana bakmak yerine doğrudan hedefe nokta atışı
yaptı!)`);
console.log(`- Kodun Harcadığı Süre: ${(t3 - t2).toFixed(4)} milisaniye.`);
mongoose.disconnect();
};
advancedMongoRun();
Müfredatımızın
en kritik güvenlik ve kullanıcı yönetim katmanı olan Bölüm 9: JWT Authentication & OAuth
Özellikleri ve Bölüm 10: Görsel Yükleme & Rol Tabanlı Yetkilendirme
(RBAC) konuları ile devam ediyoruz.
Önceki
soruma istinaden, kurumsal dünya standartlarında esneklik sağlaması adına OAuth mekanizmalarını ham (raw)
kütüphane ve mantıksal akışla, mimariyi ise orijinal sırasını bozmadan
tasarlıyorum. Reklam ve süre ibarelerinden arındırılmış, derinlemesine konu
anlatımları ve eksiksiz kod blokları aşağıdadır:
🔐 BÖLÜM 9:
JWT Authentication & OAuth
Özellikleri
Modern
web uygulamalarında güvenlik iki aşamadan oluşur: Authentication
(Kimlik Doğrulama - Kimsin?) ve Authorization (Yetkilendirme - Ne yapmaya iznin
var?).
➡️ Şifre Güvenliği
(Bcrypt Hashing) ve JWT
Mimarisi
➡️ Çift Token Yapısı: Access Token ve Refresh Token Mantığı
Güvenlik
açıklarını kapatmak için tek token yerine çift token yapısı kullanılır:
💻 Uygulama 1:
Kurumsal Seviye Çift Token'lı (Access/Refresh) JWT Kimlik Doğrulama Sistemi
Bu
uygulamada sıfırdan Kullanıcı Kaydı, Şifre Kriptolama, Giriş Yapma, Korunan
Rotaları Kilitleme ve Refresh Token
ile Yeni Oturum Üretme akışını kodluyoruz.
Adım
1: Gerekli
paketleri kurun:
npm install express mongoose bcryptjs jsonwebtoken joi dotenv
Adım
2: app.js dosyasını oluşturup güvenlik
altyapısını kurun:
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt
= require('jsonwebtoken');
const joi
= require('joi');
require('dotenv').config();
const app
= express();
app.use(express.json());
// Geçici olarak gizli
anahtarları .env yoksa diye
tanımlıyoruz
const ACCESS_SECRET = process.env.ACCESS_SECRET
|| 'cok_gizli_access_anahtari_9876';
const REFRESH_SECRET = process.env.REFRESH_SECRET
|| 'daha_gizli_refresh_anahtari_1234';
mongoose.connect('mongodb://localhost:2017/guvenlik_projesi');
// --- 1. KULLANICI
MODELİ ---
const kullaniciSemasi = new
mongoose.Schema({
isim: { type: String, required: true },
eposta: { type: String, required: true, unique: true },
sifre: { type: String, required: true },
refreshToken: { type: String } // Refresh token'ı
veri tabanında saklıyoruz
});
const Kullanici = mongoose.model('Kullanici', kullaniciSemasi);
// --- 2. DOĞRULAMA
ŞEMALARI (JOI) ---
const kayitDogrulama = (veri) => {
return joi.object({
isim: joi.string().min(2).required(),
eposta: joi.string().email().required(),
sifre: joi.string().min(6).required()
}).validate(veri);
};
// --- 3. API ROTALARI
---
// A) KAYIT OL
(REGISTER)
app.post('/api/auth/kayit', async (req,
res) => {
try {
const { error } = kayitDogrulama(req.body);
if (error) return res.status(400).json({ hata: error.details[0].message });
const epostaVarMi = await Kullanici.findOne({ eposta:
req.body.eposta });
if (epostaVarMi) return res.status(400).json({ hata: "Bu
e-posta zaten kayıtlı." });
//
Şifre Hashing (Tuzlama seviyesi: 10)
const salt = await bcrypt.genSalt(10);
const hashliSifre = await bcrypt.hash(req.body.sifre,
salt);
const yeniKullanici = new
Kullanici({
isim: req.body.isim,
eposta: req.body.eposta,
sifre: hashliSifre
});
await yeniKullanici.save();
res.status(201).json({ mesaj: "Kullanıcı
başarıyla kaydedildi." });
} catch (hata) {
res.status(500).json({ hata: hata.message
});
}
});
// B) GİRİŞ YAP (LOGIN) - ÇİFT TOKEN ÜRETİMİ
app.post('/api/auth/giris', async (req,
res) => {
try {
const kullanici = await Kullanici.findOne({
eposta: req.body.eposta });
if (!kullanici) return res.status(400).json({ hata: "E-posta
veya şifre hatalı!"
});
const sifreDogruMu = await bcrypt.compare(req.body.sifre,
kullanici.sifre);
if (!sifreDogruMu)
return res.status(400).json({ hata: "E-posta
veya şifre hatalı!"
});
//
Access Token Üretimi (15 Dakikalık kısa ömür)
const accessToken = jwt.sign({ id:
kullanici._id }, ACCESS_SECRET, { expiresIn: '15m' });
//
Refresh Token Üretimi (7
Günlük uzun ömür)
const refreshToken = jwt.sign({ id:
kullanici._id }, REFRESH_SECRET, { expiresIn: '7d' });
//
Refresh token'ı kullanıcı
dokümanına güncelle
kullanici.refreshToken
= refreshToken;
await kullanici.save();
res.json({
accessToken, refreshToken, mesaj:
"Giriş başarılı." });
} catch (hata) {
res.status(500).json({ hata: hata.message
});
}
});
// C) TOKEN YENİLEME
(REFRESH ROUTE)
app.post('/api/auth/yenile', async (req, res)
=> {
const { gelenRefreshToken } = req.body;
if (!gelenRefreshToken)
return res.status(401).json({ hata: "Yenileme
token'ı sağlanmadı." });
try {
//
Veri tabanında bu token'a sahip kullanıcı var mı?
const kullanici = await Kullanici.findOne({
refreshToken: gelenRefreshToken
});
if (!kullanici) return res.status(403).json({ hata: "Geçersiz
yenileme token'ı!" });
//
Token geçerlilik/süre kontrolü
jwt.verify(gelenRefreshToken, REFRESH_SECRET, (hata,
veri) => {
if (hata) return res.status(403).json({ hata: "Token süresi dolmuş veya geçersiz!" });
// Her şey yolundaysa yeni bir kısa ömürlü access
token ver
const yeniAccessToken = jwt.sign({ id:
veri.id }, ACCESS_SECRET, { expiresIn: '15m' });
res.json({ accessToken: yeniAccessToken });
});
} catch (hata) {
res.status(500).json({ hata: hata.message
});
}
});
// --- 4. GÜVENLİK
MIDDLEWARE'İ (KORUNAN ROTALAR İÇİN) ---
const tokenDogrula = (req,
res, next) => {
const baslik = req.header('Authorization');
if (!baslik) return res.status(401).json({ hata: "Erişim
engellendi. Token eksik." });
const token = baslik.split(' ')[1];
// 'Bearer <token>' formatından sadece token'ı
ayıklıyoruz
if (!token) return res.status(401).json({ hata: "Hatalı
token formatı." });
try {
const onaylanmisVeri = jwt.verify(token, ACCESS_SECRET);
req.kullanici
= onaylanmisVeri; // İlerideki rotalar kullanıcının ID'sine
erişebilsin diye req nesnesine bağlıyoruz
next();
} catch (hata) {
res.status(403).json({ hata: "Geçersiz
veya süresi dolmuş token!" });
}
};
// D) KORUNAN ROTA
ÖRNEĞİ (SECURE ROUTE)
app.get('/api/kullanici/profil', tokenDogrula,
async (req,
res) => {
//
req.kullanici.id middleware'den güvenle geldi
const kullanici = await Kullanici.findById(req.kullanici.id).select('-sifre -refreshToken');
res.json(kullanici);
});
app.listen(5000, () => console.log("Güvenlik API 5000 portunda aktif."));
➡️ OAuth Mimarisi (Google Sign-In
Mantığı)
Kullanıcıların
sitenize şifre üretmeden, Google altyapısı üzerinden tek tıkla güvenle giriş
yapmasını sağlayan protokoldür.
🖼️ BÖLÜM 10:
Görsel Yükleme & Rol Tabanlı Yetkilendirme (RBAC)
➡️ Multer ile Dosya Yönetimi, İsimlendirme ve Güvenlik
Filtreleri
Node.js,
gelen ham form dosyalarını (multipart/form-data) tek başına ayrıştıramaz.
Bunun için Multer middleware'i
kullanılır. Görsel yükleme süreçlerinde güvenlik için şu 3 kural
uygulanmalıdır:
💻 Uygulama 2:
Multer ile Görsel Yüklenebilen Rol Tabanlı (RBAC)
E-Ticaret Ürün API'si
Bu
uygulamada Multer dosya yükleme motorunu
kuracak, resim filtresi ekleyecek ve sadece Admin rolüne sahip
kullanıcıların ürün ekleyebileceği, normal Müşteri rolündekilerin ise
sadece ürünleri listeleyebileceği yetkilendirme (RBAC) yapısını kuracağız.
const express = require('express');
const multer = require('multer');
const path = require('path');
const jwt
= require('jsonwebtoken');
const app
= express();
app.use(express.json());
// --- 1. MULTER DOSYA
SAKLAMA MOTORU AYARLARI (STORAGE) ---
const diskSaklamaAlani = multer.diskStorage({
destination: function (req, file, cb)
{
cb(null, 'uploads/'); // Dosyaların
kaydedileceği klasör (Klasörün var olduğundan emin olun)
},
filename: function (req, file, cb)
{
//
Çakışmayı önlemek için benzersiz isim: zaman damgası + rastgele sayı + orijinal
uzantı
const benzersizEk = Date.now() + '-'
+ Math.round(Math.random()
* 1E9);
cb(null, file.fieldname
+ '-' + benzersizEk + path.extname(file.originalname));
}
});
// Güvenlik Filtresi:
Sadece JPG, JPEG ve PNG kabul et
const gorselFiltresi = (req,
file, cb)
=> {
const kabulEdilen
Tipler = /jpeg|jpg|png/;
const mimeTipiOnay = kabulEdilen Tipler.test(file.mimetype);
const uzantiOnay = kabulEdilen Tipler.test(path.extname(file.originalname).toLowerCase());
if (mimeTipiOnay
&& uzantiOnay) {
return cb(null, true);
}
cb(new Error("Yalnızca görsel dosyaları (.jpg, .jpeg, .png) yüklenebilir!"), false);
};
const yukleyici = multer({
storage: diskSaklamaAlani,
limits: { fileSize: 1024 * 1024 * 5 }, // Maksimum 5 MB boyutu
fileFilter: gorselFiltresi
});
// --- 2. ROL TABANLI
YETKİLENDİRME MIDDLEWARE'LERİ ---
// Sahte Token Doğrulama (Test kolaylığı için rolleri doğrudan token içine gömüyoruz)
const kimlikKontrol = (req,
res, next) => {
const baslik = req.header('Authorization');
if (!baslik) return res.status(401).json({ hata: "Erişim
reddedildi."
});
try {
const token = baslik.split(' ')[1];
//
Test amaçlı şifresiz jwt açma veya doğrudan veri
okuma simülasyonu
const cozulenVeri = jwt.verify(token, 'ortak_gizli_anahtar');
req.kullaniciRolVerisi
= cozulenVeri; //
Örn: { id: 1, rol: 'admin' } veya 'musteri'
next();
} catch (error) {
res.status(403).json({ hata: "Geçersiz
Token." });
}
};
// Dinamik Rol Kontrol
Fabrikası (Dizi olarak kabul eder)
const rolYetkiVer = (...izinVerilenRoller) => {
return (req,
res, next) => {
if (!req.kullaniciRolVerisi
|| !izinVerilenRoller.includes(req.kullaniciRolVerisi.rol))
{
return res.status(403).json({ hata: "Bu
işlem için yetkiniz yok! Gerekli Rol: " + izinVerilenRoller.join(',') });
}
next();
};
};
// --- 3. ÜRÜN EKLEME
RROTASI (GÖRSEL VE ADMIN ŞARTI) ---
// yukleyici.single('urunGorseli')
-> Form data içerisindeki dosya anahtar ismidir.
app.post('/api/urunler', kimlikKontrol,
rolYetkiVer('admin'), yukleyici.single('urunGorseli'), (req,
res) => {
try {
if (!req.file) return res.status(400).json({ hata: "Lütfen
bir ürün görseli yükleyin." });
const yeniUrun = {
ad:
req.body.ad,
fiyat: req.body.fiyat,
gorselYolu: req.file.path
// Veri tabanına kaydedilecek dosya yolu
};
//
Gerçek projede: await Urun.create(yeniUrun) işlemi yapılır.
res.status(201).json({
mesaj: "Ürün, görseliyle
birlikte başarıyla eklendi (Sadece Admin Yetkisiyle).",
urun: yeniUrun
});
} catch (error) {
res.status(400).json({ hata: error.message
});
}
});
// Herkese açık ürün
listeleme rotası
app.get('/api/urunler', (req,
res) => {
res.json({
mesaj: "Tüm
ürünler müşterilere listelendi." });
});
app.listen(6000, () => console.log("Görsel ve RBAC Sunucusu 6000 portunda yayında."));
Harika
bir mimari karar! Kurumsal projelerde sepet (cart) verileri, veri
kaybını önlemek için MongoDB gibi kalıcı bir
veri tabanında saklanırken, kullanıcının sepeti her görüntülemesinde veri
tabanına ağır yük bindirmemek adına Redis gibi
bir in-memory (bellek içi) önbellek katmanıyla
optimize edilir. Eğer sepet önbellekte varsa anında oradan okunur (Cache Hit), yoksa veri tabanından çekilip önbelleğe yazılır
(Cache Miss).
Müfredatın
orijinal sırasını bozmadan, sepet mekanizmasını bu hibrit yapıyla kuracağız.
Hemen ardından Bölüm 11: Kurumsal Hata Yönetimi & Loglama Altyapıları
ve Bölüm 12: Ödeme Ağ Geçitleri Yönetimi konularını eksiksiz kod
blokları ve derinlemesine açıklamalarla inşa ediyoruz.
🛒 BÖLÜM 11:
Sepet Yönetimi ve Bellek İçi Önbellek (Redis
Simülasyonu)
➡️ Hibrit
Sepet Mimarisi Mantığı
💻 Uygulama 1:
MongoDB Kalıcı Saklama ve Redis
Önbellek Simülasyonlu Sepet API'si
Adım
1: Projenize
gerekli paketleri dahil edin:
npm install express mongoose dotenv
Adım
2: sepet_server.js dosyasını oluşturup kurumsal
yapıyı kodlayın:
const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();
const app
= express();
app.use(express.json());
mongoose.connect('mongodb://localhost:2017/sepet_mimari_projesi');
// --- 1. MONGODB
KALICI SEPET MODELİ ---
const sepetSemasi = new
mongoose.Schema({
kullaniciId: { type: String, required: true, unique: true },
urunler: [
{
urunId: { type: String, required: true },
ad:
{ type: String, required: true },
adet: { type: Number, required: true, min: 1 },
fiyat: { type: Number, required: true }
}
],
toplamTutar: { type: Number, default: 0 },
guncellenmeTarihi: { type: Date, default: Date.now }
});
// Kaydetmeden önce
toplam tutarı otomatik hesaplayan Mongoose Middleware'i
sepetSemasi.pre('save', function(next) {
this.toplamTutar = this.urunler.reduce((toplam, urun) => toplam + (urun.fiyat
* urun.adet), 0);
this.guncellenmeTarihi = Date.now();
next();
});
const Sepet = mongoose.model('Sepet',
sepetSemasi);
// --- 2. REDIS BELLEK
İÇİ (IN-MEMORY) CACHE SİMÜLASYONU ---
// Gerçek Redis sunucusu mantığıyla çalışan bir JavaScript Key-Value nesnesi
const RedisCacheSimulasyonu = {
depo: {},
//
Belleğe veri yazma (TTL: Time-To-Live simülasyonu
ile)
set:
function(anahtar, deger) {
console.log(`[🚀 REDIS CACHE]: '${anahtar}' anahtarı belleğe yazıldı.`);
this.depo[anahtar] = JSON.stringify(deger);
},
//
Bellekten veri okuma
get: function(anahtar) {
if (this.depo[anahtar]) {
console.log(`[🎯 CACHE HIT]: '${anahtar}' verisi RAM'den anında okundu.`);
return JSON.parse(this.depo[anahtar]);
}
console.log(`[❌ CACHE MISS]: '${anahtar}' bellekte yok, veri tabanına gidiliyor...`);
return null;
},
//
Bellekten veri silme (Sepet güncellendiğinde eski cache
temizlenmelidir)
del:
function(anahtar) {
console.log(`[🧹 CACHE FLUSH]: '${anahtar}' eski verisi bellekten temizlendi.`);
delete this.depo[anahtar];
}
};
// --- 3. API ROTALARI
---
// A) SEPETE ÜRÜN EKLE
/ ADET ARTIR (Kalıcı MongoDB + Cache
Güncelleme)
app.post('/api/sepet/:kullaniciId/ekle', async (req, res)
=> {
const { kullaniciId } = req.params;
const { urunId, ad, adet, fiyat } = req.body;
try {
let sepet = await Sepet.findOne({ kullaniciId
});
if (!sepet) {
// Kullanıcının ilk sepeti ise yeni oluştur
sepet = new Sepet({ kullaniciId,
urunler: [{ urunId,
ad, adet, fiyat }] });
} else {
// Sepet zaten varsa, ürün daha önce eklenmiş mi kontrol et
const urunIndex = sepet.urunler.findIndex(u => u.urunId === urunId);
if (urunIndex
> -1) {
// Ürün zaten var, sadece adedini artır
sepet.urunler[urunIndex].adet
+= adet;
} else
{
// Ürün yok, diziye yeni ürün ekle
sepet.urunler.push({ urunId,
ad, adet, fiyat });
}
}
//
MongoDB'ye kalıcı kaydet (pre-save
tetiklenir, toplam tutar hesaplanır)
await sepet.save();
//
Önbelleği güncelle (Kalıcı tabandaki güncel veriyi RAM'e
de yazıyoruz)
RedisCacheSimulasyonu.set(`sepet:${kullaniciId}`,
sepet);
res.status(200).json({ mesaj: "Sepet
güncellendi",
veri: sepet });
} catch (error) {
res.status(500).json({ hata: error.message
});
}
});
// B) SEPETİ GÖRÜNTÜLE
(Önce Hızlı Cache, Yoksa MongoDB)
app.get('/api/sepet/:kullaniciId', async (req, res)
=> {
const { kullaniciId } = req.params;
const cacheAnahtar = `sepet:${kullaniciId}`;
try {
//
1. ADIM: Önce RAM simülasyonuna bak
const cachedSepet = RedisCacheSimulasyonu.get(cacheAnahtar);
if (cachedSepet)
{
return res.status(200).json({ kaynak: "Redis Bellek (RAM)", veri: cachedSepet
});
}
//
2. ADIM: Cache Miss olduysa
MongoDB'den oku
const sepet = await Sepet.findOne({ kullaniciId
});
if (!sepet) return res.status(404).json({ mesaj: "Sepetiniz
henüz boş."
});
//
3. ADIM: Bir sonraki istekte hızlı gelsin diye RAM'e
kopyala
RedisCacheSimulasyonu.set(cacheAnahtar, sepet);
res.status(200).json({ kaynak: "MongoDB Veri Tabanı (Disk)", veri: sepet });
} catch (error) {
res.status(500).json({ hata: error.message
});
}
});
app.listen(7000, () => console.log("Sepet ve Cache Sistemi 7000
portunda aktif."));
🛠️ BÖLÜM 12:
Kurumsal Hata Yönetimi ve Loglama (Error Handling
& Logging)
Profesyonel
bir backend uygulamasında hiçbir hata sunucuyu
çökertmemelidir. Hatalar merkezi bir noktada yakalanmalı, kullanıcıya dostça
bir arayüz dönmeli ve arka planda yazılımcıların incelemesi için log
dosyalarına veya veri tabanına kaydedilmelidir.
➡️ Kritik Hata
Tipleri:
💻 Uygulama 2:
Global Hata Yakalama ve MongoDB Loglama Altyapısı
Bu
uygulamada, projenin herhangi bir yerinde hata çıktığında devreye giren Merkezi
Hata Middleware'i kuracağız ve oluşan sistem
hatalarını MongoDB'de HataLog adlı ayrı bir koleksiyona
kaydedeceğiz.
const express = require('express');
const mongoose = require('mongoose');
const app
= express();
mongoose.connect('mongodb://localhost:2017/sistem_hatalari');
// --- 1. HATA LOG
MODELİ ---
const logSemasi = new mongoose.Schema({
mesaj: String,
hataYigini: String, // Stack trace
(Hatanın hangi satırda çıktığı)
tarih: { type: Date, default: Date.now }
});
const HataLog = mongoose.model('HataLog', logSemasi);
// --- 2. GLOBAL
SİSTEM ÇÖKMELERİNİ ÖNLEYEN DİNLEYİCİLER ---
// Uygulama genelinde
yakalanamayan düz kod hataları
process.on('uncaughtException', async (error) => {
console.error('🚨 ÇOK KRİTİK HATA (Uncaught Exception):', error.message);
//
Veri tabanına logla ve sunucuyu güvenli şekilde yeniden başlat
await HataLog.create({
mesaj: error.message, hataYigini: error.stack
});
process.exit(1);
// Sunucuyu kapat (PM2 gibi araçlar otomatik
geri açacaktır)
});
// Yakalanamayan
asenkron hatalar
process.on('unhandledRejection', async (reason, promise) => {
console.error('🚨 ASENKRON HATA (Unhandled Rejection):', reason.message);
await HataLog.create({
mesaj: reason.message, hataYigini: reason.stack
});
});
// --- 3. TEST
ROTALARI ---
app.get('/api/hata-test-senkron', (req,
res) => {
//
Tanımsız bir fonksiyon çağırarak kasıtlı hata fırlatıyoruz
tanimsizFonksiyon();
});
app.get('/api/hata-test-asenkron', async (req, res,
next) => {
try {
//
Geçersiz bir sorgu simülasyonu
throw new
Error("Veri
tabanı bağlantısı işlem sırasında koptu!");
} catch (error) {
next(error); //
Hatayı merkezi hata middleware'ine paslar
}
});
// --- 4. MERKEZİ HATA
MANAGEMENT MIDDLEWARE ---
// Express'te 4
parametre alan middleware'ler hata yakalayıcıdır. En
sonda yer almalıdır.
app.use(async (err,
req, res,
next) => {
console.log("💥 Merkezi Hata Yönetimi
Hatayı Yakaladı ve MongoDB'ye Kaydediyor...");
//
Hata detaylarını MongoDB'ye yazıyoruz
await HataLog.create({
mesaj: err.message,
hataYigini: err.stack
});
//
Kullanıcıya şık ve sistemi açık etmeyen bir hata mesajı dönüyoruz
res.status(500).json({
durum: "Hata",
mesaj: "Sistemsel bir
sorun oluştu. Teknik ekibimiz bilgilendirildi.",
//
Geliştirme modundaysak hatayı detaylı göster, canlıda gizle
detay: process.env.NODE_ENV === 'development' ? err.message
: null
});
});
app.listen(8000, () => console.log("Hata Kontrol Sunucusu 8000 portunda yayında."));
💳 BÖLÜM 13:
Ödeme Ağ Geçitleri Yönetimi (Payment Gateways)
E-ticaret
sistemlerinin en hassas noktası ödeme akışlarıdır. Sunucularımızda doğrudan
kredi kartı bilgisi tutmak yasal olarak (PCI-DSS kuralları) yasaktır. Bu yüzden
Razorpay veya PayPal gibi aracı
kurumlar entegre edilir.
➡️ Güvenli Ödeme
İşleyiş Adımları (Step-by-Step):
💻 Uygulama 3:
PayPal API Entegrasyon Altyapısı (Mock Ödeme
Doğrulama)
Gerçek
dünya API entegrasyon mantığını yansıtan ödeme ve imza doğrulama akışı:
const express = require('express');
const app
= express();
app.use(express.json());
// --- ÖDEME DOĞRULAMA
API UÇ NOKTASI (WEBHOOK / CALLBACK) ---
app.post('/api/odeme/paypal-dogrula', async (req, res)
=> {
const { paypalSiparisId, sepetTutari, gelenDijitalImza } = req.body;
try {
console.log(`💳 PayPal Siparişi (${paypalSiparisId}) için doğrulama başlatıldı...`);
//
GERÇEK DÜNYA SENARYOSU:
//
const paypalResponse = await axios.post('https://paypal.com' + paypalSiparisId,
{}, { headers: ... });
//
Simüle Edilen Güvenlik Kontrolü:
if (gelenDijitalImza
=== "SAHTE_IMZA_KONTROLU_XYZ") {
return res.status(400).json({ hata: "Güvenlik
İmzası Doğrulanamadı! Sahte İstek Engellendi." });
}
//
Ödeme doğrulandı, siparişi veri tabanında güncelleme aşaması
res.status(200).json({
durum: "Başarılı",
mesaj: "Ödeme PayPal
üzerinden başarıyla doğrulandı. Ürünler hazırlanıyor.",
referansNo: "PAYPAL_REF_" + Math.floor(Math.random() * 1000000)
});
} catch (error) {
res.status(500).json({ hata: error.message
});
}
});
app.listen(9000, () => console.log("Ödeme Yönetim Sunucusu 9000 portunda yayında."));
Eğitim
rehberimizin son aşaması olan ve modern arka plan mimarilerinin en heyecan
verici kısımlarını barındıran Bölüm 14: Socket.IO ile Gerçek Zamanlı
(Real-Time) Sohbet Altyapısı ve Bölüm 15: Projeyi Canlı Sunucuya Dağıtma
(Deployment) konuları ile devam ediyoruz.
Müfredatımızın
finalini, süre veya reklam ibarelerinden tamamen arındırılmış, mülakatlarda
fark yaratacak mimari detaylar, eksiksiz kod blokları ve kurumsal canlıya alma
pratikleri ile tamamlıyoruz.
💬 BÖLÜM 14:
Socket.IO ile Gerçek Zamanlı İletişim
Geleneksel
HTTP protokolü Request-Response
(İstek-Yanıt) modeline dayanır. İstemci (tarayıcı) istemeden sunucu veri
gönderemez. Anlık mesajlaşma uygulamalarında tarayıcının her saniye sunucuya
"Yeni mesaj var mı?" diye sorması (Polling)
sunucuyu çökertecek devasa bir yük oluşturur.
➡️ WebSockets ve Socket.IO Teknolojisi
💻 Uygulama 1:
Canlı Mesajlaşma, "Yazıyor..." Göstergesi ve Okundu Bilgisi Destekli
Chat Sunucusu
Bu
uygulamada Instagram veya WhatsApp benzeri; Kullanıcıları odalara ayırma,
anlık mesaj iletimi, canlı olarak "Yazıyor..." uyarısı tetikleme ve
mesaj durumu (Görüldü) güncellemelerini içeren tam donanımlı bir Socket.IO
sunucusu tasarlıyoruz.
Adım
1: Gerekli
paketleri projenize kurun:
npm install express socket.io
Adım
2: chat_server.js dosyasını oluşturup gerçek
zamanlı mimariyi kodlayın:
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app
= express();
// Socket.IO'nun
çalışabilmesi için Express'i ham bir HTTP sunucusu ile sarmalıyoruz
const sunucu = http.createServer(app);
// CORS ayarları ile frontend uygulamalarının sunucuya bağlanmasına izin
veriyoruz
const io
= new Server(sunucu, {
cors: {
origin: "*", //
Gerçek projede buraya sadece kendi frontend url'nizi
yazmalısınız
methods: ["GET", "POST"]
}
});
// --- REAL-TIME
SOCKET ETKİLEŞİM KATMANI ---
io.on('connection', (socket) => {
console.log(`🔌 Bir kullanıcı canlı
bağlandı. Socket ID: ${socket.id}`);
//
1. Odaya Katılma Eylemi (Join Room
Logic)
//
Kullanıcılar sadece aynı odada (örneğin özel bir chat
odasında) ise birbirlerinin mesajını görür
socket.on('odaya_katil', (odaId) => {
socket.join(odaId);
console.log(`👤 Müşteri, [Oda: ${odaId}] isimli sohbet odasına giriş yaptı.`);
});
//
2. Anlık Mesaj Gönderme ve İletildi Durumu
socket.on('mesaj_gonder', (veri) => {
//
veri içeriği: { odaId:
"oda_123", gonderen: "Ahmet",
metin: "Selam!" }
//
Mesajı gönderen kişi hariç odadaki diğer herkese (to)
anında yayınla
socket.to(veri.odaId).emit('mesaj_al', {
gonderen: veri.gonderen,
metin: veri.metin,
tarih: new Date().toISOString(),
durum: 'iletildi' //
Mesaj karşı tarafa ulaştığı an durumu set edilir
});
});
//
3. Canlı "Yazıyor..." Göstergesi (Typing Indicator)
socket.on('yaziyor_tetikle', (veri) => {
//
veri içeriği: { odaId:
"oda_123", kullanici: "Ahmet",
durum: true }
//
Odadaki diğer kullanıcılara Ahmet'in şu an klavyede olduğunu bildirir
socket.to(veri.odaId).emit('yaziyor_bilgisi', {
kullanici: veri.kullanici,
durum: veri.durum // true: yazıyor, false:
bıraktı
});
});
//
4. Okundu Bilgisi (Mark as Seen)
socket.on('mesaj_okundu', (veri) => {
//
Karşı taraf sohbet ekranını açtığında veya mesajı gördüğünde tetiklenir
socket.to(veri.odaId).emit('görüldü_bilgisi', {
mesajId: veri.mesajId,
durum: 'görüldü'
});
});
//
Kullanıcı sekmesini kapattığında veya bağlantısı koptuğunda tetiklenir
socket.on('disconnect', () => {
console.log(`❌ Bir kullanıcının bağlantısı
koptu. Socket ID: ${socket.id}`);
});
});
const PORT = process.env.PORT
|| 9500;
sunucu.listen(PORT, () => {
console.log(`🚀 Canlı Mesajlaşma (WebSocket) Sunucusu ${PORT}
portunda aktif...`);
});
🚀 BÖLÜM 15:
Sunucu Dağıtımı ve Canlıya Taşıma (Deployment)
Yazmış
olduğumuz projenin yerel bilgisayarımızdan (localhost) çıkıp, tüm dünyanın
erişebileceği bulut sunucularına taşınması sürecidir.
➡️ Kurumsal Dağıtım
Stratejisi ve Hazırlık Adımları
💻 Uygulama 2:
Projeyi Canlı Ortama (Production) Hazırlama Adımları
Adım
1 (package.json Yapılandırması):
Bulut sunucuları hangi komutla projeyi ayağa kaldıracağını bilmek ister. Bu
yüzden package.json dosyanızı şu şekilde
güncelleyin:
{
"name": "modern-node-backend",
"version": "1.0.0",
"description": "Canlı
ortama hazır backend projesi",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.21.0",
"mongoose": "^8.6.0",
"dotenv": "^16.4.5"
}
}
Adım
2 (GitHub'a Yükleme Öncesi Kritik Güvenlik Dosyası: .gitignore):
Projenizin kök dizinine mutlaka .gitignore adında bir dosya oluşturun
ve içine şunları yazın:
node_modules/
.env
(Bu
sayede devasa boyutlu kütüphane klasörü ve en önemlisi gizli
şifreleriniz/veri tabanı linkleriniz asla GitHub'a
sızmaz, güvenliğiniz tehlikeye girmez).
Adım
3 (Adım Adım Canlıya Yayına Alma Süreci):
Kurumsal
standartlarda bir Node.js projesinin tamamlayıcı unsurları olan MVC Klasör
Yapısı, Postman API Test Stratejileri
ve Jest ile Unit Test (Birim Testi)
entegrasyonu aşağıda detaylı olarak tasarlanmıştır.
📂 1.
Profesyonel MVC Klasör Dizini Mimarisi (Folder Structure)
Büyük
ölçekli bir E-Ticaret ve Sosyal Medya backend
projesinin sürdürülebilir, ekipler arası çalışmaya uygun ve esnek olabilmesi
için kod sorumluluklarının kesin sınırlarla ayrılması gerekir. Aşağıdaki yapı,
endüstri standardı bir Model-View-Controller
(Controller-Service-Repository varyasyonu)
düzenidir:
modern-backend-project/
├── .env # Çevresel değişkenler
ve gizli anahtarlar
├── .gitignore # GitHub'a
gönderilmeyecek dosyalar (node_modules, .env)
├──
server.js # Uygulamanın
ana giriş noktası (Entry Point)
├──
config/ # Veri tabanı ve dış
servis yapılandırmaları
│ ├── db.js # MongoDB
Atlas ve Redis bağlantı ayarları
│ └── passport.js # OAuth
(Google/Facebook) stratejileri
├──
src/
│ ├── app.js # Express konfigürasyonu ve middleware entegrasyonu
│ ├── constants/ # Proje genelinde kullanılan
sabit metinler ve hata kodları
│ ├── models/ # Mongoose
Şemaları (Veri Katmanı)
│ │
├── User.js
│ │
├── Product.js
│ │
├── Cart.js
│ │
└── Order.js
│ ├── controllers/ # İstekleri karşılayan ve yanıt
dönen katman
│ │
├── authController.js
│ │
├── productController.js
│ │
└── cartController.js
│ ├── services/ # İş mantığının (Business Logic) döndüğü katman
│ │
├── paymentService.js # PayPal /
Razorpay API entegrasyonu
│ │
└── emailService.js # Amazon
SES / Nodemailer işleyicisi
│ ├── routes/ # API Uç noktalarının (Endpoints) tanımları
│ │
├── authRoutes.js
│ │
├── productRoutes.js
│ │
└── index.js # Tüm
rotaları toplayan ana rota dosyası
│ ├── middlewares/ # Araya giren doğrulama ve güvenlik
fonksiyonları
│ │
├── authMiddleware.js # JWT ve
Rol (RBAC) kontrolü
│ │
├── errorMiddleware.js
# Merkezi hata yakalayıcı
│ │
└── uploadMiddleware.js # Multer dosya yükleme
ayarı
│ └── utils/ # Yardımcı fonksiyonlar (Helper functions)
│ └── logger.js # Winston veya MongoDB
hata günlükçüsü
├──
public/ # Dışarıya açık statik
dosyalar
│ └── uploads/ # Multer
ile yüklenen ürün/profil görselleri
└──
tests/ # Test senaryoları
klasörü
├── integration/ # Entegrasyon testleri
└── unit/ # Birim testleri
└── authService.test.js # Örnek servis
testi
🎯 2.
Profesyonel Postman Test Koleksiyonu Stratejileri
Canlıya
alınan API'lerin her güncellemede doğru çalışıp
çalışmadığını manuel kontrol etmek yerine, Postman'in
içerisindeki JavaScript tabanlı test otomasyonu kullanılır.
➡️ Kritik Test
Stratejileri ve Örnek Senaryolar
Strateji
A: Otomatik Token Yönetimi (Environment Variables)
Kullanıcı
giriş yaptığında (Login) dönen accessToken değerini elinizle kopyalamak
yerine Postman'in hafızasına (Çevre Değişkeni)
otomatik yazdırın.
// HTTP durum kodunun
200 olduğunu doğrula
pm.test("Giriş
işlemi başarılı (Status 200)", function () {
pm.response.to.have.status(200);
});
// Gelen JSON yanıtını
parse et
const rasyonVerisi = pm.response.json();
// Yanıtın içinde accessToken var mı kontrol et ve çevre değişkenine kaydet
if(rasyonVerisi.accessToken) {
pm.environment.set("current_access_token", rasyonVerisi.accessToken);
console.log("Token başarıyla belleğe
alındı.");
}
Strateji
B: Veri Tipi ve Şema Doğrulama
Gelen
yanıtın sadece başarılı olması yetmez, içerikteki alanların doğruluğu da
kontrol edilmelidir.
pm.test("Sepet
veri yapısı doğru ve eksiksiz", function () {
const veri = pm.response.json();
pm.expect(veri).to.be.an('object');
pm.expect(veri).to.have.property('toplamTutar');
pm.expect(veri.toplamTutar).to.be.a('number');
pm.expect(veri.urunler).to.be.an('array');
});
🧪 3. Jest
Kütüphanesi ile Birim Testlerine (Unit Tests) Giriş
Unit Test (Birim Testi); uygulamanın veri tabanı, internet veya harici servis gibi
dış bağımlılıklarından tamamen soyutlanarak, sadece en küçük kod parçalarının
(fonksiyonların) kendi iç mantığının test edilmesidir.
➡️ Adım Adım
Jest Kurulumu ve Mock Mantığı
Dış
servisleri (Örn: Gerçekten e-posta göndermek veya
gerçek MongoDB'ye yazmak) test sırasında meşgul
etmemek için Mocking (Sahte Nesne Üretimi)
tekniği uygulanır.
Adım
1: Test Ortamının Kurulması
npm install jest supertest --save-dev
package.json içerisindeki test komutunu
güncelleyin:
"scripts": {
"test": "jest
--watchAll"
}
Adım
2: Test Edilecek Fonksiyon (İş Mantığı)
Senaryomuzda,
kullanıcının sepet tutarına göre kargo ücretini hesaplayan bir servis
fonksiyonu (src/services/kargoService.js) olsun:
const kargoHesapla = (sepetTutari, kuponKodu) => {
if (sepetTutari
<= 0) return 0;
let kargoBedeli = 50; //
Varsayılan kargo ücreti
if (sepetTutari
>= 1000 || kuponKodu
=== "BEDAVAKARGO") {
kargoBedeli
= 0;
}
return kargoBedeli;
};
module.exports = { kargoHesapla
};
Adım
3: Jest Test Senaryosunun Yazılması
tests/unit/kargoService.test.js adında bir dosya oluşturun.
Jest, .test.js uzantılı dosyaları otomatik
olarak tarar ve çalıştırır:
const { kargoHesapla } = require('../../src/services/kargoService');
// Birbiriyle ilgili
testleri "describe" bloku altında gruplarız
describe('📦 Kargo Ücreti
Hesaplama Birim Testleri',
() => {
//
Senaryo 1: 1000 TL altı sepetler
test('1000 TL altındaki sepetler için kargo ücreti 50 TL olmalıdır', () => {
const sonuc = kargoHesapla(500,
"");
//
expect ve toBe: Çıkan
sonucun beklediğimiz değere eşitliğini doğrular (Assertion)
expect(sonuc).toBe(50);
});
//
Senaryo 2: 1000 TL ve üzeri sepetler
test('1000 TL ve üzerindeki sepetler için kargo ücretsiz (0 TL)
olmalıdır',
() => {
const sonuc = kargoHesapla(1200,
"");
expect(sonuc).toBe(0);
});
//
Senaryo 3: Özel kupon kodu durumu
test('BEDAVAKARGO kuponu girildiğinde sepet tutarına bakılmaksızın
kargo 0 TL olmalıdır',
() => {
const sonuc = kargoHesapla(200,
"BEDAVAKARGO");
expect(sonuc).toBe(0);
});
//
Senaryo 4: Sınır durumlar (Edge Cases)
test('Sepet tutarı 0 veya negatif ise kargo ücreti de 0 dönmelidir', () => {
const sonuc = kargoHesapla(0,
"");
expect(sonuc).toBe(0);
});
});
Adım
4: Testleri Çalıştırma
Terminalinizde
şu komutu yürütün:
npm test
Jest
tüm senaryoları simüle edecek ve konsolda yeşil renkli başarı raporunu (PASS)
önünüze serecektir. Kodun içinde kargo limitini yanlışlıkla değiştirdiğinizde
testler anında kırmızıya (FAIL) dönerek hatayı canlı yakalamanızı sağlar.