Gönderdiğiniz
müfredat dosyasını inceledim. İçerik oldukça kapsamlı olduğu ve her konunun
derinlemesine, bol örnek ve uygulamalarla açıklanmasını istediğiniz için bu
eğitimi bölümlere ayırarak parça parça ele alacağız.
Bu
ilk aşamada, talep ettiğiniz üzere öncelikle "Öğrenecekleriniz"
bölümünü detaylıca açıklayacak, ardından müfredatın ilk kısımlarını (Node.js
Temelleri, Express ve API Dünyası) satır satır
işleyerek gerçek dünya örnekleri ve kod uygulamalarıyla tasarlayacağız.
🌟
"Öğrenecekleriniz" Bölümünün Detaylı Açıklaması
Bu
kurs, modern bir arka plan (back-end) yazılımcısının
bilmesi gereken tüm temel ve ileri düzey becerileri uygulamalı olarak
kazandırmayı hedeflemektedir. Maddelerin sektörel karşılıkları ve anlamları
şunlardır:
🛠 BÖLÜM 1:
Giriş & Node.js Kurulumu ve Proje Yapılandırması
➡️ Node.js ve VS Code Kurulumu
Node.js,
JavaScript kodlarımızı bilgisayarımızda veya sunucuda doğrudan çalıştıran bir
çalışma ortamıdır (runtime). VS Code
ise bu kodları en verimli şekilde yazacağımız editördür.
➡️ Yeni Bir Node
Projesi Oluşturma & package.json Mantığı
Her
Node.js projesi bir klasör ve o klasörün kimlik kartı olan package.json dosyası ile başlar.
Uygulama: Terminali açıp aşağıdaki
komutu yazdığımızda projemiz canlanır:
npm init -y
Bu
komut projemize kök dizinde bir package.json dosyası kazandırır. Bu
dosya, projenin adını, versiyonunu ve projemizde kullanacağımız dış paketlerin
(kütüphanelerin) listesini tutar.
➡️ NPM ile Paket Yönetimi
(Ekleme ve Çıkarma)
NPM
(Node Package Manager),
başkalarının yazdığı kod kütüphanelerini projemize dahil etmemizi sağlar.
🛠 BÖLÜM 2:
Node.js Esasları (Express, MongoDB ve Yapılandırma)
➡️ Express Framework
Node.js'in yalın haliyle web sunucusu yapmak çok uzun kodlar
gerektirir. Express, HTTP isteklerini (GET, POST vb.) kolayca
yönetmemizi sağlayan, Node.js üzerindeki en popüler web çatısıdır.
Kod
Örneği (İlk Sunucumuz):
const express = require('express');
const app
= express();
const PORT = 3000;
// Get
Routes: Tarayıcıdan bir istek geldiğinde yanıt dönme
app.get('/',
(req, res)
=> {
res.send('Node.js Dünyasına Hoş Geldiniz!');
});
app.listen(PORT, () => {
console.log(`Sunucu ${PORT} portunda başarıyla çalışıyor.`);
});
➡️ Nodemon
Kullanımı
Normal
şartlarda kodumuzda bir değişiklik yaptığımızda sunucuyu kapatıp (CTRL + C) tekrar açmamız gerekir. Nodemon paketi, biz kodu kaydettiğimiz anda sunucuyu
otomatik olarak yeniden başlatır.
Kurulum: npm install -g nodemon
ya da proje içine npm install --save-dev nodemon.
➡️ Ortam Değişkenleri (dotenv) ve .gitignore
Uygulama
(.env dosyası):
PORT=5000
MONGO_URI=mongodb+srv://kullanici:sifre@cluster.mongodb.net/testDB
Uygulama
(Kod içinde çağırma):
require('dotenv').config();
console.log(process.env.PORT); // Çıktı: 5000
➡️ Veri Tabanı Bağlantısı: Mongo & Mongoose
MongoDB, verileri esnek (JSON benzeri) dökümanlar
halinde tutan modern bir NoSQL veri tabanıdır. Mongoose ise bu veri tabanına bağlanmamızı ve
verilerin belirli bir şablona (Model) uymasını sağlayan köprü kütüphanedir.
🛠 BÖLÜM 3: API'ler ve Veri Tabanı İle Çalışma
(CRUD İşlemleri)
Bu
bölümde gerçek bir "Kitaplık Yönetim API'si"
tasarlayarak CRUD işlemlerini, modelleri ayırmayı ve Postman
testlerini simüle edeceğiz.
➡️ Modeli Ayırma (Separate Model)
Veri
tabanındaki koleksiyonlarımızın şemasını bağımsız dosyalarda tutarız.
Uygulama:
models/Book.js
const mongoose = require('mongoose');
const BookSchema = new
mongoose.Schema({
title: { type: String, required: true },
author: { type: String, required: true },
pages: Number,
createdAt: { type: Date, default: Date.now }
});
// MongoDB'deki
nesnelerin default gelen "_id" alanını
temiz bir "id" olarak döndürme ayarı
BookSchema.virtual('id').get(function(){
return this._id.toHexString();
});
BookSchema.set('toJSON', { virtuals: true });
module.exports = mongoose.model('Book', BookSchema);
➡️ Rotaları Ayırma ve CRUD
Operasyonları (Separate Route & Try Catch)
Tüm
HTTP isteklerini ana dosyada toplamak yerine iş kollarına göre rotalara
böleriz. Hataları yakalamak ve sunucunun çökmesini önlemek için mutlaka try-catch blokları kullanırız.
Uygulama:
routes/bookRoutes.js
const express = require('express');
const router = express.Router();
const Book = require('../models/Book');
// 1. CREATE: Yeni
Kitap Ekleme (POST)
router.post('/',
async (req,
res) => {
try {
const newBook = new Book(req.body);
const savedBook = await newBook.save();
// Veri tabanına kaydeder
res.status(211).json(savedBook); // 211: Http Status Code - Created
} catch (error) {
res.status(500).json({ message: "Veri
kaydedilirken hata oluştu." });
}
});
// 2. READ: Tüm
Kitapları Listeleme (GET)
router.get('/',
async (req,
res) => {
try {
const books = await Book.find();
res.status(200).json(books); // 200: OK
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 3. READ BY ID:
Belirli Bir Kitabı Detaylı Getirme (GET By ID)
router.get('/:id',
async (req,
res) => {
try {
const book = await Book.findById(req.params.id);
if (!book) return res.status(404).json({ message: "Kitap
bulunamadı."
});
res.status(200).json(book);
} catch (error) {
res.status(500).json({ message: "Geçersiz ID
formatı."
});
}
});
// 4. UPDATE: Kitap
Bilgilerini Güncelleme (PUT)
router.put('/:id',
async (req,
res) => {
try {
const updatedBook = await Book.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true } //
Güncellenmiş yeni veriyi geri döndürür
);
res.status(200).json(updatedBook);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 5. DELETE: Veri
Tabanından Kitap Silme (DELETE)
router.delete('/:id', async (req, res)
=> {
try {
await Book.findByIdAndDelete(req.params.id);
res.status(200).json({ message: "Kitap başarıyla
silindi."
});
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
➡️ Postman
& HTTP Durum Kodları (Status Codes)
Yazdığımız
bu API rotalarını test etmek için tarayıcı yetersiz kalır (çünkü tarayıcılar
doğrudan sadece GET isteği atabilir). Bu noktada Postman
programını kullanırız. Testlerimizi yaparken dönen yanıtların standartlara
uygun olması için doğru HTTP durum kodlarını seçeriz:
Harika,
kaldığımız yerden devam ediyoruz! Bir önceki bölümde temel CRUD mimarisini
kurmuştuk. Şimdi ise projemizi profesyonel seviyeye taşıyacak olan Veri
Doğrulama (Body Validation) ve Uluslararasılaştırma (Localization)
mekanizmalarını kuracağız. Ardından devasa E-Ticaret Uygulaması bölümünün
ilk yarısını inşa etmeye başlayacağız.
🛠 BÖLÜM 4:
Body Validation (Gelen Verilerin Doğrulanması)
Kullanıcıların
veri tabanımıza rastgele, hatalı ya da zararlı veriler göndermesini engellemek
için express-validator kütüphanesini kullanırız.
Örneğin bir kitabın adı boş bırakılamaz veya sayfa sayısı eksi bir değer
olamaz.
➡️ express-validator Kurulumu ve Kullanımı
Kurulum: npm install
express-validator
➡️ Rotaları Temiz Tutma (Refactor Code) & ID Parametre
Doğrulaması
Doğrulama
kurallarını doğrudan rota (route) dosyalarının içine
yazmak kod kirliliğine sebep olur. Bu yüzden kuralları ara yazılım (middleware) olarak dışarıya taşırız. Ayrıca MongoDB'nin kendine has ObjectId yapısını (req.params.id) kontrol eden bir doğrulama
da ekleriz.
Uygulama:
middleware/validateBook.js
const { body, param, validationResult } = require('express-validator');
// Kitap ekleme ve
güncelleme için doğrulama kuralları
const bookValidationRules = () => {
return [
body('title')
.notEmpty().withMessage('Kitap adı boş bırakılamaz.')
.isLength({ min:
2 }).withMessage('Kitap adı en az 2 karakter olmalıdır.'),
body('author')
.notEmpty().withMessage('Yazar adı boş bırakılamaz.'),
body('pages')
.optional()
.isInt({ min:
1 }).withMessage('Sayfa sayısı 1den küçük olamaz.')
];
};
// URL'den gelen ID'nin geçerli bir MongoDB ObjectId olup olmadığını kontrol eder
const idParamValidationRules = () => {
return [
param('id').isMongoId().withMessage('Geçersiz veri tabanı ID formatı.')
];
};
// Hataları yakalayıp
istemciye dönen ortak ara yazılım
const validate = (req, res,
next) => {
const errors = validationResult(req);
if (errors.isEmpty())
{
return next(); // Hata yoksa bir sonraki fonksiyona/işleme geç
}
//
Hataları temiz bir liste halinde döndürür
const extractedErrors = [];
errors.array().map(err => extractedErrors.push({
[err.path]: err.msg }));
return res.status(400).json({
errors: extractedErrors
});
};
module.exports = {
bookValidationRules,
idParamValidationRules,
validate
};
Uygulama:
routes/bookRoutes.js İçinde Kullanımı
const express = require('express');
const router = express.Router();
const { bookValidationRules, idParamValidationRules, validate } = require('../middleware/validateBook');
const Book = require('../models/Book');
// Yeni kitap eklerken
önce kuralları çalıştır, sonra doğrula, hata yoksa kaydet
router.post('/',
bookValidationRules(), validate,
async (req,
res) => {
try {
const newBook = new Book(req.body);
await newBook.save();
res.status(201).json(newBook);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// Detay getirirken
önce ID biçimini doğrula
router.get('/:id',
idParamValidationRules(), validate,
async (req,
res) => {
try {
const book = await Book.findById(req.params.id);
if (!book) return res.status(404).json({ message: "Kitap
bulunamadı."
});
res.status(200).json(book);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
🛠 BÖLÜM 5: Localization (Çoklu Dil ve Yerelleştirme Desteği)
Global
bir projede, API'den dönen hata mesajlarının veya
başarılı işlem yanıtlarının kullanıcının diline göre (İngilizce, Almanca,
Arapça vb.) dinamik olarak değişmesi istenir. Bunun için standart ISO 639-1
dil kodlarını (en, tr, de, ar) kullanırız.
➡️ Klasör Yapısı ve Dil
Dosyaları
Uygulama:
locales/tr.json
{
"BOOK_TITLE_REQUIRED": "Kitap
adı zorunludur.",
"BOOK_NOT_FOUND": "Aradığınız
kitap sistemde bulunamadı."
}
Uygulama:
locales/de.json (Almanca)
{
"BOOK_TITLE_REQUIRED": "Buchtitel ist erforderlich.",
"BOOK_NOT_FOUND": "Das gesuchte Buch
wurde herkunft nicht gefunden."
}
➡️ Dil Ara Yazılımı (Localization Middleware)
Gelen
isteğin başlığından (Accept-Language header)
kullanıcının hangi dili tercih ettiğini okuruz ve mesajları ona göre seçeriz.
// Basit bir dil
yönetim fonksiyonu mantığı
const i18n = (req,
res, next) => {
const lang = req.headers['accept-language'] || 'tr'; //
Varsayılan Türkçe
const locales = {
tr:
require('../locales/tr.json'),
de:
require('../locales/de.json')
};
//
İstek nesnesine (req) dile özel çeviri fonksiyonu
ekliyoruz
req.translate
= (key) => {
return locales[lang]?.[key] || locales['tr'][key] || key;
};
next();
};
🛠 BÖLÜM 6:
Büyük Proje - E-Ticaret Uygulaması (Aşama 1: Altyapı
ve Kullanıcı Yönetimi)
Müfredatın
en büyük bölümü olan bu e-ticaret uygulamasını adım adım, modüller halinde inşa
edeceğiz. Bu aşamada Kategori Yönetimi, Kullanıcı Modeli, Şifre
Güvenliği ve JWT (JSON Web Token) ile Kimlik
Doğrulama mekanizmalarını tasarlayacağız.
➡️ Kategori Yönetimi (Category Model & CRUD)
E-ticaret
sistemindeki ürünlerin gruplanması için dinamik bir kategori yapısı kuruyoruz.
Uygulama:
models/Category.js
const mongoose = require('mongoose');
const CategorySchema = new
mongoose.Schema({
name: { type: String, required: true, unique: true },
description: String
});
module.exports = mongoose.model('Category', CategorySchema);
➡️ Kullanıcı Yönetimi ve Şifre
Güvenliği (Password Hashing)
Kullanıcıların
şifreleri asla veri tabanına düz metin ("123456") olarak kaydedilmez.
Siber saldırılara karşı bu şifreleri tek yönlü algoritmalardan geçiren bcrypt kütüphanesi ile şifreleriz (hashing).
Uygulama:
models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const UserSchema = new
mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
role: { type: String, enum: ['user',
'admin'], default: 'user' }, // Rol tabanlı yetkilendirme altyapısı
addresses: [{
title: String,
city: String,
zip: String
}]
});
// Veri tabanına
kaydedilmeden hemen önce (Pre-save hook) şifreyi otomatik tuzla ve şifrele
UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(10);
// Güvenlik katmanı derecesi
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
next(error);
}
});
// Giriş yapılırken
gelen şifre ile veri tabanındaki şifreli yapıyı karşılaştırma metodu
UserSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword,
this.password);
};
module.exports = mongoose.model('User',
UserSchema);
➡️ JWT (Token)
Üretimi ve Kimlik Doğrulama Middleware (Auth Middleware)
Kullanıcı
giriş yaptıktan sonra sunucu ona geçici bir kimlik kartı (JWT Token) verir. Kullanıcı sonraki isteklerinde (profilini
görme, sipariş verme vb.) bu kartı sunarak kendini kanıtlar.
Kurulum: npm install
jsonwebtoken
Uygulama:
middleware/auth.js
const jwt
= require('jsonwebtoken');
const authMiddleware = (req,
res, next) => {
//
İstek başlığından (Header) Token'ı
yakala (Format: Bearer <TOKEN>)
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split('
')[1];
if (!token) {
return res.status(401).json({ message: 'Erişim engellendi. Token bulunamadı.' });
}
try {
//
Token'ın doğruluğunu ve süresini gizli anahtarımızla (.env dosyasından gelen) kontrol
et
const verified = jwt.verify(token,
process.env.JWT_SECRET);
req.user
= verified; //
Token içindeki kullanıcı bilgilerini (id, rol) istek
nesnesine ekle
next();
} catch (error) {
res.status(403).json({ message: 'Geçersiz veya süresi
dolmuş token.' });
}
};
// Sadece
yöneticilerin erişebileceği rotaları koruma yazılımı
const authorizeRoles = (...roles) => {
return (req,
res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ message: 'Bu işlem için
yetkiniz bulunmamaktadır.' });
}
next();
};
};
module.exports = { authMiddleware,
authorizeRoles };
➡️ Kullanıcı Kayıt (Register) ve Giriş (Login) Rotaları
Şimdi
tüm bu öğrendiklerimizi birleştirerek kullanıcı giriş ve kayıt mekanizmasını
tamamlayalım.
Uygulama:
routes/authRoutes.js
const express = require('express');
const router = express.Router();
const User = require('../models/User');
const jwt
= require('jsonwebtoken');
// KULLANICI KAYIT
router.post('/register', async (req, res)
=> {
try {
const { name, email, password } = req.body;
//
E-posta adresi sistemde zaten var mı?
const existingUser = await User.findOne({ email
});
if (existingUser)
return res.status(400).json({ message: 'Bu e-posta zaten
kullanımda.'
});
const newUser = new User({ name, email,
password });
await newUser.save();
res.status(201).json({ message: 'Kullanıcı başarıyla
oluşturuldu.'
});
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// KULLANICI GİRİŞİ
router.post('/login',
async (req,
res) => {
try {
const { email, password } = req.body;
//
Kullanıcıyı bul
const user = await User.findOne({
email });
if (!user) return res.status(404).json({ message: 'Kullanıcı
bulunamadı.'
});
//
Şifreyi doğrula
const isMatch = await user.comparePassword(password);
if (!isMatch) return res.status(400).json({ message: 'Hatalı şifre.' });
//
Bilgiler doğruysa JWT Token üret (Ömrü 1 Gün)
const token = jwt.sign(
{ id: user._id, role:
user.role },
process.env.JWT_SECRET,
{ expiresIn: '1d'
}
);
res.status(200).json({
message: 'Giriş
başarılı',
token: token,
user: { id: user._id, name:
user.name, role: user.role }
});
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
E-Ticaret
projemizin en kritik altyapı adımlarına geçiyoruz. Bu aşamada Ürün Yönetimi,
Gelişmiş Arama/Filtreleme/Sayfalama algoritması, Multer
ile sunucuya dosya yükleme, Cloudinary
entegrasyonu ile buluta görsel taşıma ve Sipariş Yönetimi
modüllerini satır satır kodlayarak tasarlayacağız.
🛠 BÖLÜM 7:
E-Ticaret Uygulaması (Aşama 2: Ürünler, Gelişmiş Filtreleme ve Multer)
➡️ Ürün Modeli (Product Model)
Bir
e-ticaret sisteminde ürünlerin adı, açıklaması, fiyatı, stok adedi, kategorisi
ve resim yollarını tutacak esnek bir yapı kuruyoruz.
Uygulama:
models/Product.js
const mongoose = require('mongoose');
const ProductSchema = new
mongoose.Schema({
name: { type: String, required: true, trim: true },
description: { type: String, required: true },
price: { type: Number, required: true, min:
0 },
category: { type: mongoose.Schema.Types.ObjectId, ref: 'Category', required: true },
stock: { type: Number, required: true, min:
0, default: 0 },
images: [{ type: String }], // Resimlerin URL adresleri tutulur
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Product',
ProductSchema);
➡️ Multer
ile Sunucuya Yerel Dosya Yükleme
Kullanıcıların
veya adminlerin ürün resimlerini yükleyebilmesi için multer ara yazılımını (middleware) kullanırız. Gelen dosyaları sunucudaki uploads/ klasörüne kaydederiz.
Kurulum: npm install
multer
Uygulama:
middleware/upload.js
const multer = require('multer');
const path = require('path');
// Dosyanın sunucuda
nereye ve hangi isimle kaydedileceğini belirliyoruz
const storage = multer.diskStorage({
destination: function (req, file, cb)
{
cb(null, 'uploads/'); // Dosyaların
yükleneceği klasör adı
},
filename: function (req, file, cb)
{
//
Dosya isminin çakışmaması için benzersiz bir zaman damgası ekliyoruz
const uniqueSuffix = Date.now() + '-'
+ Math.round(Math.random()
* 1E9);
cb(null, file.fieldname
+ '-' + uniqueSuffix + path.extname(file.originalname));
}
});
// Sadece görsel
formatlarına (PNG, JPG, JPEG) izin veren güvenlik filtresi
const fileFilter = (req,
file, cb)
=> {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('Yalnızca resim dosyası yükleyebilirsiniz!'), false);
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: { fileSize: 1024 * 1024 * 5 } //
Maksimum 5 MB limit
});
module.exports = upload;
➡️ Gelişmiş Ürün Arama,
Filtreleme ve Sayfalama (Pagination) API Yapısı
Performanslı
bir API için yüzlerce ürünü tek seferde göndermek yerine; kelime araması yapan,
fiyata veya kategoriye göre filtreleyen ve veriyi sayfa sayfa bölen gelişmiş
bir rota yazarız.
Uygulama:
routes/productRoutes.js
const express = require('express');
const router = express.Router();
const Product = require('../models/Product');
const upload = require('../middleware/upload');
const { authMiddleware, authorizeRoles } = require('../middleware/auth');
// 1. ÜRÜN EKLEME
(Sadece Adminler Yapabilir - Çoklu Resim Yükleme
Desteği)
router.post('/',
authMiddleware, authorizeRoles('admin'),
upload.array('images', 5), async (req, res)
=> {
try {
const { name, description, price, category, stock } = req.body;
//
Yüklenen dosyaların yollarını diziye çeviriyoruz
const imagePaths = req.files ? req.files.map(file => `/uploads/${file.filename}`)
: [];
const newProduct = new
Product({
name, description, price, category, stock,
images: imagePaths
});
await newProduct.save();
res.status(201).json(newProduct);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
// 2. GELİŞMİŞ
LİSTELEME: Arama, Filtreleme ve Sayfalama (Herkese Açık)
router.get('/',
async (req,
res) => {
try {
//
Query parametrelerini alıyoruz (Örn: /products?search=ayakkabi&minPrice=100&page=1&limit=10)
const { search, category, minPrice, maxPrice, page = 1, limit = 10 } = req.query;
//
Dinamik filtre nesnesi oluşturuyoruz
let queryObj = {};
//
İsme göre Regex (Büyük/Küçük harf duyarsız) arama
if (search) {
queryObj.name = {
$regex: search, $options: 'i' };
}
//
Kategoriye göre filtreleme
if (category) {
queryObj.category
= category;
}
//
Fiyat aralığı filtreleme
if (minPrice || maxPrice) {
queryObj.price
= {};
if (minPrice)
queryObj.price.$gte = Number(minPrice); // ...'den büyük veya eşit
if (maxPrice)
queryObj.price.$lte = Number(maxPrice); // ...'den küçük veya eşit
}
//
Sayfalama (Pagination) Matematiği
const skip = (Number(page)
- 1) * Number(limit);
const totalProducts = await Product.countDocuments(queryObj);
//
Veri tabanından ilişkili Kategori bilgisini de çekerek (populate)
getiriyoruz
const products = await Product.find(queryObj)
.populate('category', 'name')
.skip(skip)
.limit(Number(limit))
.sort({ createdAt: -1 }); // En yeni eklenen en üstte
res.status(200).json({
meta: {
totalItems: totalProducts,
totalPages: Math.ceil(totalProducts / Number(limit)),
currentPage: Number(page),
perPage: Number(limit)
},
data: products
});
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
🛠 BÖLÜM 8: Cloudinary Entegrasyonu (Görselleri Buluta Taşımak)
Sunucunun
kendi yerel diskine resim kaydetmek, proje büyüdükçe disk alanının dolmasına ve
sunucu performansının düşmesine neden olur. Bu yüzden profesyonel projelerde
resimler Cloudinary gibi bir Bulut Depolama
(Cloud Storage) servisine yüklenir.
Kurulum: npm install
cloudinary
Uygulama:
services/cloudinaryService.js
const cloudinary = require('cloudinary').v2;
const fs
= require('fs');
// Cloudinary
kimlik bilgileri yapılandırması
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
});
// Yerel diskteki
resmi Cloudinary bulutuna yükleyen fonksiyon
const uploadToCloudinary = async (localFilePath) => {
try {
const result = await cloudinary.uploader.upload(localFilePath, {
folder: 'eticaret_urunler' //
Buluttaki klasör adı
});
//
Buluta başarıyla yüklendikten sonra sunucumuzdaki geçici yerel dosyayı
siliyoruz
fs.unlinkSync(localFilePath);
return result.secure_url;
// Ürünün internet ortamındaki güvenli URL'ini
döner
} catch (error) {
//
Hata durumunda da sunucuda dosya kalıntısı bırakmıyoruz
if (fs.existsSync(localFilePath)) {
fs.unlinkSync(localFilePath);
}
throw new
Error('Cloudinary yükleme hatası: ' + error.message);
}
};
module.exports = { uploadToCloudinary
};
🛠 BÖLÜM 9:
Sipariş Yönetimi (Order Model & Stok Kontrolü)
Kullanıcıların
sepetindeki ürünleri satın alabilmesi, fiyatların arka planda doğrulanması ve
her başarılı siparişte ürünün stok adedinin otomatik azaltılması (stoktan
düşme) işlemlerini yöneten mimaridir.
➡️ Sipariş Modeli (Order Model)
Uygulama:
models/Order.js
const mongoose = require('mongoose');
const OrderSchema = new
mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
items: [{
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
quantity: { type: Number, required: true, min:
1 },
price: { type: Number, required: true } //
Satın alındığı andaki sabit fiyatı
}],
totalPrice: { type: Number, required: true },
status: { type: String, enum: ['Beklemede', 'Hazırlanıyor', 'Kargoda', 'Teslim
Edildi'],
default: 'Beklemede' },
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Order', OrderSchema);
➡️ Sipariş Oluşturma ve Veri
Tabanı Validasyonu (Stok Azaltma İşlemi)
Uygulama:
routes/orderRoutes.js
const express = require('express');
const router = express.Router();
const Order = require('../models/Order');
const Product = require('../models/Product');
const { authMiddleware } = require('../middleware/auth');
router.post('/',
authMiddleware, async (req, res)
=> {
try {
const { items } = req.body; // Gelen veri: [{ product:
"ID", quantity: 2 }]
let calculatedTotalPrice = 0;
let finalOrderItems = [];
//
Her bir ürünü tek tek kontrol edip, fiyat doğrulama ve stok kontrolü yapıyoruz
for (let
item of items) {
const product = await Product.findById(item.product);
if (!product) {
return res.status(404).json({ message: `Ürün bulunamadı ID:
${item.product}`
});
}
// Stok yeterli mi kontrolü
if (product.stock
< item.quantity) {
return res.status(400).json({ message: `${product.name} için yetersiz stok. Mevcut: ${product.stock}`
});
}
// Arka planda güvenli fiyat hesaplama (Ön yüzden gelen fiyata
asla güvenilmez!)
calculatedTotalPrice += product.price
* item.quantity;
finalOrderItems.push({
product: product._id,
quantity: item.quantity,
price: product.price
});
// Ürünün stok miktarını azaltıp güncelliyoruz
product.stock -= item.quantity;
await product.save();
}
//
Siparişi kaydet
const newOrder = new Order({
user: req.user.id, // Auth middleware'den
gelen giriş yapmış kullanıcı ID'si
items: finalOrderItems,
totalPrice: calculatedTotalPrice
});
await newOrder.save();
res.status(201).json(newOrder);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;
Müfredatımızın
en yenilikçi ve popüler konularına geldik! Bu aşamada, projelere otomatik
e-posta bildirim yeteneği kazandıran Node
Mailer servisini kuracak, ardından yapay zekanın gücünü arkamıza alarak OpenAI ve Hugging
Face modelleri ile entegre çalışan bir AI Chat
API'si inşa edeceğiz.
🛠 BÖLÜM 10:
Real-World Services (Node Mailer ile E-Posta Servisi)
E-ticaret
sisteminde yeni bir sipariş verildiğinde veya bir kullanıcı kayıt olduğunda
arka planda otomatik e-posta göndermek için nodemailer kütüphanesini kullanırız.
Kurulum: npm install
nodemailer
Uygulama:
services/emailService.js
const nodemailer = require('nodemailer');
// E-posta gönderimini
sağlayan SMTP sunucu yapılandırması
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST, //
Örn: ://gmail.com
port: process.env.EMAIL_PORT, //
Örn: 465 veya 587
secure: true, // 465 portu için true, 587 için false
auth: {
user: process.env.EMAIL_USER,
// Gönderici e-posta adresi
pass: process.env.EMAIL_PASS //
E-posta şifresi veya uygulama şifresi
}
});
// Dinamik e-posta
gönderme fonksiyonu
const sendWelcomeEmail = async (userEmail, userName) => {
try {
const mailOptions = {
from: `"Modern
E-Ticaret" <${process.env.EMAIL_USER}>`,
to: userEmail,
subject: 'Sistemimize
Hoş Geldiniz! 🎉',
// HTML şablonu kullanarak şık bir e-posta tasarımı gönderiyoruz
html: `
<div
style="font-family: Arial, sans-serif; padding: 20px; border: 1px solid #eee;">
<h2
style="color:
#4CAF50;">Merhaba ${userName},</h2>
<p>Node.js
E-Ticaret dünyasına başarıyla adım attınız. Hesabınız aktif edilmiştir.</p>
<br>
<p>Keyifli
alışverişler dileriz,<br>Sosyal Destek
Ekibi</p>
</div>
`
};
const info = await transporter.sendMail(mailOptions);
console.log('E-posta başarıyla gönderildi: %s', info.messageId);
return true;
} catch (error) {
console.error('E-posta gönderim hatası:', error);
return false;
}
};
module.exports = { sendWelcomeEmail
};
🛠 BÖLÜM 11:
Proje 3 - AI Chat App (Yapay
Zeka Destekli Chatbot API)
Bu
projede, Node.js sunucumuz üzerinden hem popüler OpenAI
(GPT) modellerine istek atacağız hem de Hugging
Face platformundaki tamamen ücretsiz ve açık
kaynak kodlu yapay zeka modellerini projemize entegre
edeceğiz.
Kurulum: npm install
openai @huggingface/inference
➡️ OpenAI
Entegrasyonu
OpenAI platformundan aldığımız API anahtarı (process.env.OPENAI_API_KEY) ile kullanıcılardan gelen
soruları yapay zekaya sorup yanıt üreten bir rota tasarlıyoruz.
Uygulama:
routes/aiRoutes.js (OpenAI
Bölümü)
const express = require('express');
const router = express.Router();
const { OpenAI } = require('openai');
const { HfInference } = require('@huggingface/inference');
// OpenAI
İstemcisini başlatma
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
// OpenAI
ile Chat Bot Rotası
router.post('/chat-openai', async (req, res)
=> {
try {
const { message } = req.body; // Kullanıcının bota yazdığı mesaj
if (!message) {
return res.status(400).json({ message: 'Lütfen bir mesaj
içeriği gönderin.'
});
}
//
OpenAI API'sine chat tamamlama isteği gönderiyoruz
const response = await openai.chat.completions.create({
model: "gpt-4o-mini", //
İstediğiniz güncel modeli seçebilirsiniz
messages: [
{ role: "system", content: "Sen yardımsever
ve profesyonel bir yazılım asistanısın." },
{ role: "user", content: message }
],
max_tokens: 500
});
//
Üretilen cevabı istemciye geri dönüyoruz
res.status(200).json({
reply: response.choices[0].message.content
});
} catch (error) {
res.status(500).json({ message: 'OpenAI
Servis Hatası: '
+ error.message });
}
});
➡️ Hugging
Face Entegrasyonu (Ücretsiz Açık Kaynak Modeller)
Açık
kaynak dünyasındaki güçlü modelleri (Örn: Meta'nın Llama modelleri veya Mistral) Hugging Face Inference
API aracılığıyla projemize tamamen ücretsiz bağlayabiliriz.
Uygulama:
routes/aiRoutes.js (Hugging
Face Bölümü - Devamı)
// Hugging
Face İstemcisini başlatma (Erişim jetonunu
.env içinden alır)
const hf
= new HfInference(process.env.HF_ACCESS_TOKEN);
router.post('/chat-opensource', async (req, res)
=> {
try {
const { message } = req.body;
if (!message) {
return res.status(400).json({ message: 'Lütfen bir mesaj
içeriği gönderin.'
});
}
//
Hugging Face üzerindeki
güncel bir metin üretme modelini çağırıyoruz
const response = await hf.textGeneration({
model: 'mistralai/Mistral-7B-Instruct-v0.2', //
Açık kaynak popüler model
inputs: `<s>[INST]
${message} [/INST]`,
parameters: {
max_new_tokens: 300,
temperature: 0.7
}
});
res.status(200).json({
reply: response.generated_text
});
} catch (error) {
res.status(500).json({ message: 'Hugging
Face Servis Hatası: ' + error.message
});
}
});
module.exports = router;
🛠 BÖLÜM 12:
Gelecek Adımlar & JavaScript Hızlı Tekrar (Bonus)
Müfredatımızın
teknik back-end konularını eksiksiz olarak
tamamladık. Son iki küçük bölümde ise şu vizyon adımları yer almaktadır:
Yüklediğiniz
müfredat dosyası oldukça geniş ve kapsamlı bir Node.js uzmanlık eğitimine ait.
Bu içeriği en yüksek verimle inceleyebilmeniz için, yönergeleriniz
doğrultusunda dakika/saniye bilgilerini, önizleme
yazılarını, ders sayılarını ve anlamsız satırları temizleyerek tamamen konu
odaklı bir yapı kurdum.
İçeriğin
derinliği nedeniyle bu planı parça parça ele alacağız. İlk olarak talep
ettiğiniz üzere kursun en başındaki "Öğrenecekleriniz"
bölümünün detaylı mantığını ve backend dünyasındaki
karşılığını açıklayarak başlıyoruz.
🌟
"Öğrenecekleriniz" Bölümünün Detaylı Açıklaması
Bu
kurs, katılımcıyı sıradan bir script yazıcısından
alıp büyük ölçekli (enterprise) mimarileri
yönetebilen bir Kıdemli (Senior) Arka Plan (Backend) Geliştiricisine dönüştürmeyi hedefler.
Müfredatta vadedilen yetkinliklerin teknik açılımları şu şekildedir:
🛠 BÖLÜM 1:
Node.js Temelleri (Giriş, İç Yapı ve Modüller)
Şimdi
müfredatın ilk büyük bloğunu oluşturan temel ve iç yapı konularını
derinlemesine inceleyelim, örnekler ve uygulamalarla somutlaştıralım.
1.
Node.js Çalışma Zamanı (Runtime) ve Ortam Kurulumu
Node.js
bir programlama dili değildir; JavaScript kodlarının tarayıcı dışında, doğrudan
bilgisayarınızda veya bir sunucuda çalışmasını sağlayan bir çalışma
ortamıdır (runtime). Gücünü Google Chrome'un da
kalbi olan V8 JavaScript Motoru'ndan alır.
💻 Uygulama / Örnek: İlk
Node.js Kodunuz
Terminalinizi
veya komut satırınızı açıp node yazarak REPL ortamına girin
ve şu kodu test edin:
// Global nesnesini
inceleme ve ilk çıktı
global.console.log("Merhaba
Backend Dünyası!");
// Ortam kontrolü
if (global.process) {
console.log(`Çalışan Node sürümü: ${process.version}`);
}
2.
Node.js İç Yapısı (Internals Deep
Dive & Libuv)
Node.js'i performanslı kılan şey arka plandaki mimarisidir.
JavaScript doğası gereği tek iş parçacıklıdır (Single-Threaded),
yani aynı anda yalnızca bir işlem yapabilir. Ancak Node.js, ağır I/O
(Giriş/Çıkış) işlemlerini arka planda asenkron olarak yürütebilir.
💡 Analoji ve Örnek
Bir
restorandasınız. Blocking (Senkron)
sistemde garson siparişinizi aşçıya götürür, yemek pişene kadar mutfağın
kapısında bekler ve o sırada başka masalara bakamaz. Non-Blocking
(Asenkron) sistemde ise garson siparişi aşçıya verir, masanıza bir çağrı
cihazı (callback) koyar ve hemen diğer masaların
siparişini almaya gider. Yemek pişince cihaz titrer (Event
Loop tetiklenir) ve garson yemeği getirir.
const fs
= require('fs');
console.log("1.
Sipariş alındı.");
// Dosya okuma işlemi
asenkron (Non-blocking) olarak başlar
fs.readFile('menu.txt', 'utf8', (err,
data) => {
if (err) throw err;
console.log("3. Yemek pişti, içerik: " + data);
});
console.log("2.
Garson diğer masaya geçti.");
Çıktı
Sıralaması: 1 -> 2 -> 3 olacaktır. Sunucu dosyanın okunmasını
beklemeden 2. satırı çalıştırmıştır.
3.
Modül Sistemi (Module System)
ve Paket Yönetimi (NPM)
Büyük
projeleri tek bir dosyada yazmak imkansızdır. Node.js kodu mantıksal parçalara
(modüllere) ayırmamızı sağlar.
💻 Uygulama: Kendi Modülünü Yaz
ve Çalıştır
Matematik
Modülü (math.js):
function topla(a, b) {
return a + b;
}
// CommonJS
standardı ile dışa aktarma
module.exports = { topla };
Ana
Uygulama (app.js):
// Modülü içe aktarma
const matematik = require('./math');
const sonuc = matematik.topla(15,
25);
console.log(`Hesaplama
Sonucu: ${sonuc}`);
🚀 BÖLÜM 2:
Veri Akışları ve Web Sunucuları (File I/O, Streams
& HTTP)
Bu
bölümde ham veriyi işlemeyi ve harici dünyaya açılan ilk kapı olan web
sunucularını tasarlamayı inceleyeceğiz.
1.
Node.js Dosya I/O ve Akışlar (Streams)
Büyük
boyutlu verileri (örneğin gigabaytlarca süren Kepler uzay teleskobu verilerini)
tek seferde hafızaya (RAM) yüklemeye çalışırsanız sunucunuz kilitlenir. Node.js
bunun önüne geçmek için Streams (Akışlar)
yapısını kullanır.
💻 Uygulama: Büyük Bir CSV
Dosyasını Parça Parça Okuma
Aşağıdaki
örnek, binlerce satırlık bir gezegen veri listesini belleği yormadan asenkron
olarak nasıl işleyebileceğimizi gösterir.
const fs
= require('fs');
// Dosyayı bir akış (stream) olarak açıyoruz
const okumaAkisi = fs.createReadStream('uzay_verileri.csv', 'utf8');
okumaAkisi.on('data', (parca) => {
console.log('--- Yeni bir veri parçası (chunk)
geldi ---');
console.log(parca.substring(0, 100)); // Gelen parçanın ilk 100 karakteri
});
okumaAkisi.on('end',
() => {
console.log('Tüm dosya başarıyla işlendi.');
});
2.
Saf Node.js ile Web Sunucusu Oluşturmak
Herhangi
bir kütüphane (Express vb.) kullanmadan, Node.js'in
yerleşik http
modülü ile gelen istekleri (Requests) dinleyen ve
cevap (Response) dönen bir web sunucusu inşa
edebiliriz.
💻 Uygulama: İlk HTTP Web
Sunucunuz
const http = require('http');
const sunucu = http.createServer((req, res)
=> {
//
CORS Başlıkları ekleniyor
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'application/json');
//
Basit Yönlendirme (Routing)
if (req.url === '/api/durum' && req.method
=== 'GET') {
res.writeHead(200);
res.end(JSON.stringify({ mesaj:
"Sunucu ayakta ve çalışıyor" }));
} else if
(req.url === '/api/veri' && req.method === 'POST') {
let govde = '';
req.on('data',
chunk => { govde += chunk; });
req.on('end', () => {
res.writeHead(201);
res.end(JSON.stringify({
mesaj: "Veri
alındı",
gelen: JSON.parse(govde) }));
});
} else {
res.writeHead(404);
res.end(JSON.stringify({ hata:
"Sayfa bulunamadı" }));
}
});
sunucu.listen(3000, () => {
console.log("Sunucu 3000 portunda çalışmaya başladı...");
});
Harika,
kaldığımız yerden devam ediyoruz! Müfredatın bir sonraki devasa bloğu olan Express.js,
kurumsal mimariye adım attığımız NASA Projesi, API Testleri ve
Node.js sunucularının performans sınırlarını zorlayan Çoklu Süreç
(Clustering/Worker) Yönetimi konularını
derinlemesine, bol pratikle ele alalım.
🚀 BÖLÜM 3:
Gelişmiş API Geliştirme (Express.js & MVC Mimarisi)
Yerleşik
http modülüyle sunucu yazmak
esneklik sağlasa da, büyük projelerde rota (route) yönetimi ve ara yazılımlar (middleware)
karmaşıklaşır. Sektör standardı olan Express.js bu karmaşayı çözer.
💻 Uygulama: Express.js ile MVC
Yapısında RESTful API
1.
Veri Katmanı (models/GezegenlerModel.js)
// Basit bir bellek
içi (in-memory) veri modeli
const gezegenler = [
{ id: 1, isim: "Kepler-452b", yasamaUygun: true },
{ id: 2, isim: "Mars", yasamaUygun: false }
];
function tumGezegenleriGetir() {
return gezegenler;
}
function yeniGezegenEkle(gezegen) {
gezegenler.push(gezegen);
return gezegen;
}
module.exports = { tumGezegenleriGetir,
yeniGezegenEkle };
2.
İş Mantığı Katmanı (controllers/GezegenlerController.js)
const model = require('../models/GezegenlerModel');
function httpTumGezegenler(req,
res) {
return res.status(200).json(model.tumGezegenleriGetir());
}
function httpYeniGezegen(req,
res) {
const { isim, yasamaUygun } = req.body;
//
Basit Validasyon (Doğrulama)
if (!isim || yasamaUygun
=== undefined) {
return res.status(400).json({ hata: "Eksik
parametre gönderdiniz." });
}
const eklenecek = { id: Date.now(),
isim, yasamaUygun };
const sonuc = model.yeniGezegenEkle(eklenecek);
return res.status(201).json(sonuc);
}
module.exports = { httpTumGezegenler,
httpYeniGezegen };
3.
Sunucu ve Rota Yapılandırması (server.js)
const express = require('express');
const controller = require('./controllers/GezegenlerController');
const app
= express();
// Yerleşik Middleware: Gelen JSON isteklerini ayrıştırır (parse)
app.use(express.json());
// Özel Loglama Middleware'i
app.use((req,
res, next) => {
console.log(`[${new Date().toISOString()}]
${req.method} isteği yapıldı: ${req.url}`);
next(); // Bir
sonraki middleware veya rotaya geçişi sağlar
});
// Tanımlanan REST API
Rotaları
app.get('/api/gezegenler', controller.httpTumGezegenler);
app.post('/api/gezegenler', controller.httpYeniGezegen);
app.listen(8000, () => {
console.log("Express API 8000 portunda hazır!");
});
🧪 BÖLÜM 4:
API Test Stratejileri (Jest & Supertest)
Kurumsal
düzeyde kod yazarken projenin doğruluğunu elle test etmek (Postman/Insomnia üzerinden sürekli istek atmak) sürdürülebilir
değildir. Kod kalitesini korumak ve güncellemelerde mevcut sistemin
bozulmadığından emin olmak için otomatik testler yazarız.
💻 Uygulama: API Uç
Noktalarının Test Edilmesi
Yukarıda
yazdığımız /api/gezegenler rotasını test eden bir dosya
tasarlayalım (gezegenler.test.js):
const request = require('supertest');
const express = require('express');
const controller = require('./controllers/GezegenlerController');
// Test için geçici
bir express uygulaması simüle ediyoruz
const app
= express();
app.use(express.json());
app.get('/api/gezegenler', controller.httpTumGezegenler);
app.post('/api/gezegenler', controller.httpYeniGezegen);
describe('Gezegenler API Test Senaryoları', () => {
//
1. Başarılı GET İsteği Testi
test('GET /api/gezegenler 200 dönmeli ve
dizi içeriği içermeli',
async () => {
const response = await request(app)
.get('/api/gezegenler')
.expect('Content-Type',
/json/)
.expect(200);
expect(Array.isArray(response.body)).toBe(true);
});
//
2. Hatalı POST İsteği Validasyon Testi
test('POST /api/gezegenler eksik veriyle
400 hatası vermeli',
async () => {
const hataliVeri = { isim: "Yeni
Dünya"
}; // yasamaUygun
alanı eksik
const response = await request(app)
.post('/api/gezegenler')
.send(hataliVeri)
.expect(400);
expect(response.body.hata).toBe("Eksik parametre gönderdiniz.");
});
});
⚡ BÖLÜM 5:
Node.js Performans ve Ölçekleme (Cluster & Worker
Threads)
Node.js
tek bir çekirdekte (Single Thread)
çalışır demiştik. Eğer sunucunuz 8 çekirdekli güçlü bir makinede
bulunuyorsa, varsayılan ayarlarda Node.js yalnızca 1 çekirdeği tam kapasite
kullanır, diğer 7 çekirdek boşa bekler. Sunucu performansını maksimize etmek
için iki yöntem kullanılır:
💻 Uygulama: Yoğun CPU Yükünü Worker Threads ile Çözmek
Ana
sunucumuzda şifreleme veya ağır bir döngü döndüğünü hayal edelim. Bunu ana iş
parçacığında yaparsak sunucu kilitlenir.
Ağır
İşi Yapan Dosya (agirIsYuku.js):
const { parentPort } = require('worker_threads');
// Ağır matematiksel
işlem simülasyonu
let sayac = 0;
for (let
i = 0; i < 5000000000;
i++) {
sayac++;
}
// Sonucu ana sunucuya
geri gönder
parentPort.postMessage(sayac);
Ana
Sunucu Dosyası (sunucu.js):
const express = require('express');
const { Worker } = require('worker_threads');
const app
= express();
// Bloklayan Rota (Worker olmadan - Sunucuyu
kilitler)
app.get('/bloklayan-is', (req, res)
=> {
let sayac = 0;
for (let
i = 0; i < 5000000000;
i++) { sayac++; }
res.send(`İşlem bitti: ${sayac}`);
});
// Performanslı Rota (Worker Kullanarak - Sunucu
kilitlenmez)
app.get('/performansli-is', (req,
res) => {
//
Ağır iş yükünü arka plana fırlatıyoruz
const worker = new Worker('./agirIsYuku.js');
worker.on('message', (sonuc) => {
res.send(`Arka planda tamamlanan işlem sonucu: ${sonuc}`);
});
worker.on('error', (hata) => {
res.status(500).send(`Hata oluştu: ${hata.message}`);
});
});
app.get('/ziyaret et', (req, res)
=> {
res.send("Ben ana event loop'u bloklamadığım için anında cevap verebiliyorum!");
});
app.listen(9000, () => console.log("Performans sunucusu 9000 portunda aktif!"));
Farkı
Anlamak İçin:
/bloklayan-is rotasına tarayıcıdan istek
attığınızda o sekme yüklenirken, yan sekmede açacağınız /ziyaret-et rotası da yanıt vermez,
donar. Ancak /performansli-is rotasına istek attığınızda
ağır yük arka thread'e geçtiği için /ziyaret-et rotası anında açılmaya devam
eder.
Harika,
hız kesmeden devam ediyoruz! Müfredatın kurumsal mimaride en çok zaman alan ve
profesyonelliği belirleyen kritik aşamalarına geldik.
Bu
bölümde Veritabanı Tasarımı (MongoDB & SQL), harici servislerle haberleştiğimiz SpaceX API Entegrasyonu ve sistemlerin
güvenliğini sağlayan Kimlik Doğrulama (Authentication
& Security) konularını derinlemesine pratik örneklerle ele alacağız.
🗄️ BÖLÜM 6:
Kalıcı Veri Yönetimi (SQL vs NoSQL
ve MongoDB/Mongoose)
Verileri
RAM'de (bellekte) tutmak kalıcı bir çözüm değildir;
sunucu kapandığında tüm veriler uçar. Gerçek dünya uygulamalarında verileri
kalıcı hale getirmek için İlişkisel (SQL) veya Döküman
Tabanlı (NoSQL) veritabanları
kullanılır.
💻 Uygulama: Mongoose ile Şema Tasarımı ve Veri Kaydı
1.
Veritabanı Şeması Tasarlama (models/FirlatmaSema.js)
const mongoose = require('mongoose');
const firlatmaSemasi = new
mongoose.Schema({
ucusNumarasi: {
type: Number,
required: true,
default: 100
},
gorevIsmi: {
type: String,
required: true
},
roketTipi: {
type: String,
required: true
},
firlatmaTarihi: {
type: Date,
required: true
},
basariliMi: {
type: Boolean,
required: true,
default: true
}
});
// Şemayı modele
dönüştürüp dışa aktarıyoruz
module.exports = mongoose.model('Firlatma', firlatmaSemasi);
2.
Veritabanı Bağlantısı ve Upsert
İşlemi (veritabaniServis.js)
const mongoose = require('mongoose');
const Firlatma = require('./models/FirlatmaSema');
// MongoDB
Atlas veya yerel veritabanına bağlanma
const MONGO_URL = 'mongodb://localhost:27017/nasa_projesi';
async function veritabaninaBaglan() {
await mongoose.connect(MONGO_URL);
console.log('MongoDB Bağlantısı Başarılı!');
}
async function firlatmaVerisiniKaydet(firlatmaVerisi) {
//
UPSERT Operasyonu: ucusNumarasi kontrol edilir.
//
Varsa güncellenir, yoksa yeni kayıt (insert) atılır.
await Firlatma.updateOne(
{ ucusNumarasi: firlatmaVerisi.ucusNumarasi
}, // Filtre kriteri
firlatmaVerisi, // Güncellenecek/Eklenecek Veri
{ upsert: true }
// Upsert
özelliğini aktif etme
);
console.log(`Uçuş #${firlatmaVerisi.ucusNumarasi} veritabanına işlendi.`);
}
// Senaryo Çalıştırma
async function calistir() {
await veritabaninaBaglan();
await firlatmaVerisiniKaydet({
ucusNumarasi: 101,
gorevIsmi: "Falcon
Heavy Test",
roketTipi: "Falcon
Heavy",
firlatmaTarihi: new
Date()
});
}
calistir();
🛰️ BÖLÜM 7:
Harici API Entegrasyonu ve Sayfalama (SpaceX Projesi)
Kendi
veritabanımızı yönetmenin yanı sıra, üçüncü parti
servislerin (SpaceX API gibi) verilerini de kendi
sistemimize senkronize etmemiz gerekir.
💻 Uygulama: Sayfalama (Pagination) Altyapısı Tasarlama
Kendi
API uç noktalarımızda sorgu parametrelerine (?sayfa=1&limit=10) göre dinamik sayfalama
yapan bir fonksiyon tasarlayalım.
// queryServis.js
- Gelen istek parametrelerini MongoDB
formatına çevirir
const VARSAYILAN_SAYFA_LIMITI = 50;
const VARSAYILAN_SAYFA_NUMARASI = 1;
function sayfalamaAyarlariniAl(query) {
//
Sayfa numarasını mutlak sayıya çeviriyoruz (Örn:
"2" -> 2)
const sayfa = Math.abs(query.sayfa) || VARSAYILAN_SAYFA_NUMARASI;
const limit = Math.abs(query.limit) || VARSAYILAN_SAYFA_LIMITI;
//
Kaç adet kaydın atlanacağını hesaplama
//
Sayfa 1 ise: (1 - 1) * 10 = 0 kayıt atla
//
Sayfa 2 ise: (2 - 1) * 10 = 10 kayıt atla
const skip = (sayfa - 1) * limit;
return {
skip,
limit
};
}
module.exports = { sayfalamaAyarlariniAl
};
Controller
İçinde Kullanımı:
const Firlatma = require('./models/FirlatmaSema');
const { sayfalamaAyarlariniAl } = require('./queryServis');
async function httpTumFirlatmalar(req,
res) {
//
req.query: gelen URL
parametrelerini taşır (?sayfa=2&limit=5 gibi)
const { skip, limit } = sayfalamaAyarlariniAl(req.query);
//
MongoDB'den verileri sıralı ve sayfalanmış olarak
çekiyoruz
const firlatmalar = await Firlatma.find({}, { '_id':
0, '__v': 0 })
.sort({
ucusNumarasi: 1 }) // Uçuş numarasına göre artan sıralama
.skip(skip)
.limit(limit);
return res.status(200).json(firlatmalar);
}
🔒 BÖLÜM 8:
Sunucu Güvenliği ve Kimlik Doğrulama (Security, JWT & OAuth)
Bir backend geliştiricisinin en öncelikli görevi projenin
güvenliğidir. Sistemimize kimlerin erişebileceğini doğrulamak (Authentication) ve yetkilerini belirlemek (Authorization) zorundayız.
💻 Uygulama: JWT ile Güvenli
Rota Koruması (Middleware)
Kullanıcının
sadece geçerli bir token'a sahip olduğunda
erişebileceği gizli bir rota mekanizması kuralım.
const jwt
= require('jsonwebtoken');
const GIZLI_ANAHTAR = 'nasa_cok_gizli_anahtar_123'; //
Gerçek dünyada .env dosyasında tutulur
// 1. Kimlik Doğrulama
Ara Yazılımı (Authentication Middleware)
function kimlikDogrula(req,
res, next) {
//
İstemciden token genellikle 'Authorization:
Bearer <TOKEN>' formatında gelir
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split('
')[1];
if (!token) {
return res.status(401).json({ hata: "Erişim
engellendi. Token bulunamadı." });
}
try {
//
Token'ı çöz ve doğruluğunu kontrol et
const dogrulanmisKullanici = jwt.verify(token, GIZLI_ANAHTAR);
req.kullanici
= dogrulanmisKullanici; // Kullanıcı verisini isteğe ekle
next(); //
Başarılıysa bir sonraki rotaya/fonksiyona geç
} catch (hata) {
return res.status(403).json({ hata: "Geçersiz
veya süresi dolmuş token." });
}
}
// 2. Sunucu Üzerinde
Kullanımı
const express = require('express');
const helmet = require('helmet');
const app
= express();
app.use(helmet()); // Temel
HTTP güvenlik başlıklarını aktif et
app.use(express.json());
// Herkese açık rota
app.get('/api/genel', (req,
res) => {
res.send("Bu veriye herkes erişebilir.");
});
// Sadece giriş yapmış
kullanıcılara özel korumalı rota
app.get('/api/nasa-gizli-kodlar', kimlikDogrula,
(req, res)
=> {
res.json({
mesaj: `Hoş geldin ${req.kullanici.isim}. İşte gizli veriler...`,
veriler: ["Mars Koloni
Planları V1",
"Kepler Sinyal Analizleri"]
});
});
app.listen(5000, () => console.log("Güvenli sunucu 5000 portunda!"));
Müfredatımızın
kurumsal backend dünyasındaki son ve en heyecan
verici adımlarına geldik. Bu bölümde, yazdığımız projelerin tüm dünyaya
kesintisiz hizmet vermesini sağlayan DevOps
Süreçlerini (Docker, CI/CD, AWS) ve modern
iletişim protokolleri olan GraphQL ile WebSockets (Socket.io) teknolojilerini
derinlemesine, pratik uygulamalarla ele alacağız.
🌐 BÖLÜM 9: DevOps, Konteynerizasyon ve Bulut
Dağıtımı (Docker & AWS)
Bir backend geliştiricisi olarak harika kod yazabilirsiniz,
ancak bu kod kendi bilgisayarınız dışında çalışmadığı sürece bir değeri yoktur.
Kurumsal sistemlerde "Benim bilgisayarımda çalışıyordu!"
sorununu çözmek için altyapıyı standartlaştırır ve buluta taşırız.
💻 Uygulama: Node.js Projesi
İçin Kurumsal Dockerfile Tasarımı
Projemizin
kök dizinine ekleyeceğimiz bir Dockerfile ile uygulamamızı nasıl
paketleyeceğimizi katmanlı mimari (layering)
kurallarına göre tasarlayalım:
# 1. Aşama: Resmi
hafif Node.js imajını taban alıyoruz
FROM node:18-alpine
# 2. Aşama: Konteyner
içinde uygulamanın çalışacağı dizini oluşturuyoruz
WORKDIR /app
# 3. Aşama:
Bağımlılıkları kopyalıyoruz (Katman önbellekleme
performansı için önce package.json)
COPY package*.json ./
# 4. Aşama: Sadece
üretim (production) paketlerini kuruyoruz, geliştirme
araçlarını eliyoruz
RUN npm install --only=production
# 5. Aşama: Kalan tüm
proje kodlarını konteyner içine kopyalıyoruz
COPY . .
# 6. Aşama:
Uygulamanın dış dünyaya açılacağı portu belirtiyoruz
EXPOSE 8000
# 7. Aşama: Uygulamayı
başlatacak ana komut
CMD ["npm", "start"]
📊 BÖLÜM 10:
Modern Veri Sorgulama (GraphQL)
Geleneksel
REST API tasarımlarında iki büyük problem yaşarız: Over-fetching
(Kullanıcının ihtiyacından çok daha fazla veri göndermek) ve Under-fetching (İhtiyacı karşılamak için ardı ardına 3-4
farklı API uç noktasına istek atmak). GraphQL,
istemcinin tam olarak neye ihtiyacı varsa sunucudan sadece o veriyi tek bir
istekte talep etmesini sağlar.
💻 Uygulama: Apollo Server ve
Node.js ile Basit E-Ticaret GraphQL API
const { ApolloServer, gql
} = require('apollo-server');
// 1. Tip
Tanımlamaları (Schema)
const typeDefs = gql`
type
Urun {
id: ID!
isim: String!
fiyat: Float!
stoktaVarMi:
Boolean!
}
# Kullanıcıların atabileceği GET istekleri
(Sorgular)
type
Query {
urunler:
[Urun]
urunGetir(id: ID!): Urun
}
`;
// 2. Mock (Geçici) Veritabanı
const urunlerListesi = [
{ id: "1", isim: "Kablosuz
Kulaklık",
fiyat: 1250.0, stoktaVarMi: true },
{ id: "2", isim: "Mekanik
Klavye",
fiyat: 2100.0, stoktaVarMi: false }
];
// 3. Çözümleyiciler (Resolvers)
const resolvers = {
Query: {
urunler: () => urunlerListesi,
urunGetir: (parent, args) => urunlerListesi.find(u
=> u.id === args.id)
}
};
// 4. Sunucunun
Başlatılması
const sunucu = new
ApolloServer({ typeDefs, resolvers });
sunucu.listen({ port: 4000 }).then(({ url
}) => {
console.log(`GraphQL Sunucusu ${url}
adresinde hazır!`);
});
İstemci
Tarafından Atılabilecek Örnek Sorgu (Sadece isim ve fiyat istenir, stok bilgisi
ağ üzerinden taşınmaz):
query {
urunGetir(id: "1") {
isim
fiyat
}
}
🔄 BÖLÜM 11:
Gerçek Zamanlı İletişim (Sockets & Socket.io)
HTTP
protokolü "İstek-Cevap" (Request-Response)
mantığıyla çalışır. Sunucu durduk yere istemciye veri gönderemez, illa
istemcinin bir istek tetiklemesi gerekir. Ancak canlı chat
uygulamaları, borsa grafikleri veya çok oyunculu (multiplayer)
oyunlarda sunucunun anlık olarak veriyi istemcilere itmesi (push)
gerekir. Burada devreye WebSockets girer. Socket.io,
bağlantı kopsa bile otomatik yeniden bağlanma gibi yetenekleri olan gelişmiş
bir WebSocket kütüphanesidir.
💻 Uygulama: Socket.io ile
Canlı Mesajlaşma Odası Tasarımı
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app
= express();
const sunucu = http.createServer(app);
// CORS ayarlarıyla
Soket sunucusunu başlatıyoruz
const io
= new Server(sunucu, {
cors: { origin: "*" }
});
// Soket Bağlantı
Dinleyicisi
io.on('connection', (socket) => {
console.log(`Yeni bir kullanıcı bağlandı: ${socket.id}`);
//
Kullanıcıyı belirli bir odaya alma (Room)
socket.on('odaya_katil', (odaIsmi) => {
socket.join(odaIsmi);
console.log(`${socket.id} isimli kullanıcı [${odaIsmi}] odasına girdi.`);
});
//
Odadakilere mesaj gönderme olayı
socket.on('yeni_mesaj', (veri) => {
//
veri: { oda: "uzay-odasi",
mesaj: "Merhaba Mars!" }
//
Mesajı gönderen kişi hariç odadaki herkese (broadcast)
mesajı ilet
socket.to(veri.oda).emit('mesaj_al', {
gonderen: socket.id,
mesaj: veri.mesaj
});
});
//
Bağlantı koptuğunda tetiklenir
socket.on('disconnect', () => {
console.log(`Kullanıcı ayrıldı: ${socket.id}`);
});
});
sunucu.listen(3050, () => {
console.log("Real-time Soket Sunucusu 3050 portunda aktif!");
});
📝 BÖLÜM 12:
Ek Teknolojiler ve JavaScript Altyapısı (Deno &
JS Engine)
Müfredatın
son ek kısımları, bir backend geliştiricisinin
entelektüel ve teknik derinliğini artırmayı hedefler.
Yüklediğiniz
kurumsal Node.js müfredatında yer alan kritik mimarilerin yerel
bilgisayarınızda (localhost) sıfırdan kurulup ayağa
kaldırılması için gereken teknik rehberi ve ardından kıdemli backend mülakatlarında karşılaşabileceğiniz seçici soruları
cevaplarıyla birlikte aşağıda hazırladım.
🛠️ BÖLÜM 1:
Kurumsal Mimarileri Yerelde Sıfırdan Ayağa Kaldırma Rehberi
Uygulamalara
başlamadan önce bilgisayarınızda Node.js (v18+) ve Docker
Desktop yazılımlarının kurulu olduğundan emin olun.
1. Proje Altyapısının Hazırlanması (Ortak Adım)
Tüm
mimarileri tek bir çatı altında veya ayrı klasörlerde denemek için temiz bir
terminal açın ve şu komutları sırasıyla çalıştırın:
# Proje klasörü
oluşturun ve içine girin
mkdir node-uzmanlik-serisi && cd node-uzmanlik-serisi
# Bağımlılıkları
yöneteceğimiz package.json
dosyasını varsayılan ayarlarla oluşturun
npm init -y
# Gerekli tüm kurumsal
paketleri tek seferde kurun
npm install express mongoose apollo-server graphql socket.io jsonwebtoken helmet
# Geliştirme
aşamasında kod değiştikçe sunucuyu otomatik yenilemesi için nodemon
paketini kurun
npm install --save-dev nodemon
package.json dosyanızı açın ve "scripts" bölümüne şu satırı ekleyin:
"start": "nodemon server.js"
2. Veritabanı ve Güvenlik Katmanı
(MongoDB & JWT & Helmet)
Veritabanını bilgisayarınıza kurmakla uğraşmamak için en pratik ve
kurumsal yol Docker kullanmaktır.
Adım
A: MongoDB'yi Docker ile
Başlatma
Terminalinizde
şu komutu çalıştırarak arka planda çalışan izole bir MongoDB
ayağa kaldırın:
docker run -d --name
lokal-mongo -p 27017:27017 mongo:latest
Adım
B: Kod Altyapısı
Proje
kök dizininde server.js
dosyası oluşturun ve aşağıdaki entegre yapıyı yapıştırın:
const express = require('express');
const mongoose = require('mongoose');
const helmet = require('helmet');
const jwt
= require('jsonwebtoken');
const app
= express();
const GIZLI_ANAHTAR = 'mülakat_sırrı_123';
// Güvenlik ve veri
işleme ara yazılımları (Middleware)
app.use(helmet());
app.use(express.json());
// MongoDB
Bağlantısı
mongoose.connect('mongodb://localhost:27017/uzmanlik_db')
.then(()
=> console.log('🍃 MongoDB Bağlantısı Başarılı!'))
.catch(err => console.error('MongoDB Bağlantı Hatası:', err));
// Test için JWT
Üreten Rota (Login simülasyonu)
app.get('/auth/login', (req,
res) => {
const token = jwt.sign({ user: 'Ahmet', rol: 'Senior' }, GIZLI_ANAHTAR, { expiresIn: '1h' });
res.json({
token });
});
// Korunan Rota
Kontrolü (Middleware)
const kimlikKontrol = (req,
res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split('
')[1];
if (!token) return res.status(401).json({ hata: 'Token eksik' });
jwt.verify(token, GIZLI_ANAHTAR, (err,
decoded) => {
if (err) return res.status(403).json({ hata: 'Geçersiz
Token' });
req.user
= decoded;
next();
});
};
app.get('/api/korunan-veri', kimlikKontrol,
(req, res)
=> {
res.json({
mesaj: 'Güvenli
veritabanı alanına hoş geldiniz!', yetki: req.user.rol
});
});
app.listen(8000, () => console.log('🚀 API Sunucusu 8000
portunda aktif!'));
Çalıştırma
ve Test:
Terminalde npm start yazın. Postman veya tarayıcı
üzerinden http://localhost:8000/auth/login adresine giderek token alın. Ardından bu token'ı Bearer Token olarak ekleyip http://localhost:8000/api/korunan-veri adresine istek atın.
3. Modern Veri Katmanı (GraphQL - Apollo Server)
Mevcut
projeniz açıkken, bağımsız çalışacak bir GraphQL oyun
alanı (Playground) tasarlayalım.
Adım
A: Kod Altyapısı
Projenizde
graphql-server.js isimli bir dosya oluşturun:
const { ApolloServer, gql
} = require('apollo-server');
// Şema Tanımı
const typeDefs = gql`
type
Gorev {
id: ID!
baslik:
String!
tamamlandi:
Boolean!
}
type
Query {
gorevler:
[Gorev]
}
`;
// Sahte Veri seti
const veriKumesi = [
{ id: '1', baslik: 'Event
Loop Mülakat Sorusuna Çalış', tamamlandi: true },
{ id: '2', baslik: 'Docker
İmajını AWS\'e Yükle',
tamamlandi: false }
];
// Çözümleyiciler (Resolvers)
const resolvers = {
Query: {
gorevler: () => veriKumesi
}
};
const server = new
ApolloServer({ typeDefs, resolvers });
server.listen({ port: 4000 }).then(({ url
}) => {
console.log(`📊 GraphQL
Sunucusu ${url} adresinde hazır!`);
});
Çalıştırma
ve Test: Terminalde
ayrı bir sekme açıp node graphql-server.js komutunu çalıştırın.
Tarayıcınızdan http://localhost:4000 adresine girerek Apollo Sandbox
arayüzünde sorgularınızı görsel olarak test edin.
4. Gerçek Zamanlı İletişim Katmanı (WebSockets - Socket.io)
Anlık
veri transferlerini simüle etmek için entegre bir HTTP ve WebSocket
sunucusu kuruyoruz.
Adım
A: Kod Altyapısı
Projenizde
socket-server.js isimli bir dosya oluşturun:
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app
= express();
const sunucu = http.createServer(app);
const io
= new Server(sunucu, { cors: { origin: "*" } });
// Statik HTML testi
için basit bir arayüz sunalım
app.get('/',
(req, res)
=> {
res.send(`
<h1>Soket
Test Alanı</h1>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
socket.on('baglanti_mesaji',
(data) => console.log('Sunucudan gelen:', data));
</script>
<p>Konsolu
(F12) açarak anlık bağlantı logunu inceleyin.</p>
`);
});
io.on('connection', (socket) => {
console.log(`🔌 Yeni bir cihaz
bağlandı. Soket ID: ${socket.id}`);
//
Bağlanan istemciye anında mesaj gönder
socket.emit('baglanti_mesaji', { mesaj: 'Canlı
sisteme başarıyla bağlandınız.' });
socket.on('disconnect', () => {
console.log(`❌ Cihaz ayrıldı: ${socket.id}`);
});
});
sunucu.listen(5000, () => console.log('💬 Soket Sunucusu 5000
portunda aktif!'));
Çalıştırma
ve Test:
Terminalde node socket-server.js çalıştırın ve tarayıcıdan http://localhost:5000 adresine gidin. Sayfayı her
yenilediğinizde terminalde yeni bir Soket ID'sinin
düştüğünü göreceksiniz.
🖥️ BÖLÜM 2:
Kıdemli (Senior) Backend
Mülakat Soruları ve Cevapları
Soru 1: Node.js tek iş parçacıklı (Single-Threaded)
olmasına rağmen, binlerce eşzamanlı I/O isteğini veritabanını
veya sunucuyu kilitlemeden nasıl bu kadar performanslı yönetebiliyor? Arka
plandaki mimariyi açıklayınız.
Cevap: Node.js'in
gücü, JavaScript motorunun (V8) tek iş parçacıklı olmasıyla sınırlı değildir.
Node.js mimarisi altında libuv adında C++ ile
yazılmış güçlü bir kütüphane barındırır.
Soru 2: Event Loop
(Olay Döngüsü) üzerindeki "Microtask Queue"
(Job Queue) ile "Macrotask
Queue" (Task Queue) arasındaki fark nedir?
Hangisi daha önceliklidir? Bir örnekle açıklayınız.
Cevap: İki kuyruk da asenkron
işlemlerin geri çağırım fonksiyonlarını tutar ancak öncelikleri tamamen
farklıdır.
Öncelik
Kuralı: Event Loop, Macrotask
kuyruğundaki tek bir görevi çalıştırmadan önce Microtask
kuyruğundaki tüm görevlerin tamamen bitmesini bekler. Microtask
kuyruğu boşalmadan Macrotask aşamasına asla geçilmez.
Örnek
Kod Çıktı Analizi:
setTimeout(() => console.log('Macrotask: Timeout'), 0);
Promise.resolve().then(()
=> console.log('Microtask:
Promise'));
console.log('Senkron
Kod');
Çıktı
Sırası:
Soru 3: Üretim (Production)
ortamında çalışan büyük ölçekli bir Node.js uygulamasında "Memory Leak" (Bellek Sızıntısı) olduğunu nasıl anlarsınız,
bunu tespit etmek ve çözmek için hangi stratejileri/araçları kullanırsınız?
Cevap: Bellek sızıntısı, Garbage Collector'ın (Çöp
Toplayıcı) artık kullanılmayan nesneleri hafızadan (Memory Heap)
temizleyememesi durumudur. Zamanla sunucunun RAM kullanımı sürekli artar ve en
sonunda Out of Memory (OOM) hatasıyla
süreç çöker.
Tespit
ve İzleme Stratejileri:
En
Sık Yaşanan Sebepler ve Çözümler: Unutulan global değişkenler, temizlenmeyen setInterval zamanlayıcıları ve kapatılmayan WebSocket/Stream bağlantılarıdır. Kod kalitesini artırmak için event listener'ların işi
bittiğinde removeListener ile temizlenmesi gerekir.
Soru 4: REST mimarisi ile GraphQL
mimarisini kurumsal bir API projesi ölçeğinde karşılaştırınız. Hangi senaryoda
hangisini tercih etmelisiniz?
Cevap: İki mimarinin kurumsal
kıyaslaması şu kriterlere dayanır:
|
Kriter |
REST
API |
GraphQL |
|
Veri
Taşınması |
Sabit
uç noktalar (endpoints) tüm veri modelini döner. Over-fetching (gereksiz fazla veri) sık yaşanır. |
İstemci
sadece ihtiyacı olan alanları talep eder. Ağ trafiği optimize edilir. |
|
Ağ
İstek Sayısı |
İlişkili
veriler için ardışık istekler gerekir (Under-fetching). |
Tek
bir sorgu (Query) ile iç içe geçmiş tüm ilişkili veriler tek seferde çekilir. |
|
Önbellekleme (Caching) |
HTTP
seviyesinde (URL bazlı) CDN ve tarayıcı önbelleklemesi
çok kolaydır. |
Tüm
istekler tek bir uç noktaya (/graphql) POST olarak atıldığı için
HTTP önbelleklemesi zordur; Apollo Client
gibi uygulama seviyesi caching çözümleri gerekir. |
Tercih
Senaryoları:
Soru 5: Node.js kümeleme (Clustering) mimarisi ile Worker Threads (İş Parçacıkları)
arasındaki fark nedir? Bir e-ticaret sisteminde "Fatura PDF'i Oluşturma" ve "Gelen HTTP İsteklerini
Karşılama" görevlerini bu iki yapıya nasıl dağıtırsınız?
Cevap: İki yapı tamamen farklı
katmanları ölçeklemek için tasarlanmıştır:
E-Ticaret
Senaryo Dağıtımı: