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:

  1. Tek Dil (Full-Stack): Hem ön yüzü (Frontend) hem de arka yüzü (Backend) JavaScript ile yazarak tek bir dille tüm ekosisteme hakim olabilirsiniz.
  2. I/O Yoğun Uygulamalarda Üstünlük: Çok fazla veri giriş çıkışı gerektiren anlık mesajlaşma, canlı yayın ve e-ticaret sistemlerinde rakipsizdir.

➡️ 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:

  1. Helmet: HTTP başlıklarını (headers) otomatik olarak manipüle ederek uygulamanızı XSS (Cross-Site Scripting), Clickjacking gibi yaygın web saldırılarına karşı korur. Sunucu kimliğinizi gizler (X-Powered-By: Express başlığını siler).
  2. Morgan: Gelen isteklerin detaylarını, yanıt sürelerini ve durum kodlarını konsola profesyonelce renkli formatta yazdıran bir loglama aracıdır.

💻 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:

  1. Gömülü İlişki (Embed / Denormalization): İlgili veriyi doğrudan ana dokümanın içine bir nesne veya dizi olarak yazmaktır.
  2. Referans İlişkisi (Reference / Normalization): Verileri ayrı koleksiyonlarda tutup, ObjectId ile birbirine bağlamaktır. SQL tablolama mantığına benzer.

💻 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:

  1. Access Token: Kısa ömürlüdür (Örn: 15 dakika). API isteklerini doğrulamak için kullanılır. Çalınsa bile ömrü hızlı biter.
  2. Refresh Token: Uzun ömürlüdür (Örn: 7 gün). Veri tabanında güvenli saklanır. Access token'ın süresi bittiğinde, kullanıcıya tekrar şifre sordurmadan arka planda yeni bir Access Token üretmek için 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.

  1. Kullanıcı "Google ile Giriş Yap" butonuna basar ve Google'ın sayfasına yönlendirilir.
  2. Kullanıcı onay verince, Google backend uygulamanıza geçici bir Authorization Code gönderir.
  3. Uygulamanız arka planda bu kodu alıp Google sunucularına doğrudan istek atarak kullanıcı profil bilgilerini (id, email, avatar) teslim alır.
  4. Eğer bu e-posta veri tabanınızda yoksa yeni kullanıcı açılır, varsa mevcut kullanıcı bulunur ve sunucunuz kendi iç JWT sistemini üreterek tarayıcıya teslim eder.

🖼️ 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:

  1. Benzersiz İsimlendirme: Kullanıcıların yüklediği dosyaların birbirinin üzerine yazılmaması için dosya adlarının başına Date.now() veya UUID eklenmelidir.
  2. Uzantı Filtreleme (Mime-Type): Sunucuya tehlikeli bir .exe veya .js dosyası yüklenmesini önlemek için sadece image/jpeg veya image/png gibi görsel formatlarına izin verilmelidir.
  3. Boyut Sınırı: Sunucu disk alanını korumak adına dosya boyutu sınırlandırılmalıdır (Örn: Maksimum 5MB).

💻 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ığı

  1. Veri Ekleme/Güncelleme: Kullanıcı sepetine ürün eklediğinde, veri MongoDB'ye kalıcı olarak yazılır. Aynı anda güncel sepet verisi Redis Önbelleğine de yazılır (Cache Write-Through).
  2. Veri Okuma: Kullanıcı sepetini görmek istediğinde önce Redis Simülasyonuna bakılır. Veri bellekten mikrosaniyeler içinde döner. Bellek boşaltılmışsa MongoDB devreye girer.

💻 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:

  1. Uncaught Exceptions: Asenkron olmayan, kod blokları içinde fırlatılan ancak yakalanamayan hatalardır (Örn: Tanımlanmamış bir değişkeni okumaya çalışmak). Sunucuyu anında çökertir.
  2. Unhandled Promise Rejections: Asenkron operasyonlarda (Promise, Veri tabanı sorguları) .catch() veya try...catch ile yakalanmamış hatalardır. Sunucuyu çökertmez ama hafıza sızıntılarına yol açar.

💻 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):

  1. Sipariş Oluşturma: İstemci (Frontend), sepet tutarını sunucuya bildirir. Sunucu arka planda PayPal/Razorpay API'sine gizli anahtarlarıyla bağlanarak benzersiz bir Sipariş Kimliği (Order ID) talep eder.
  2. Ödeme Penceresi: Sunucudan gelen bu ID ile ön yüzde kart giriş ekranı açılır. Kullanıcı ödemeyi doğrudan aracı kurumun sunucularına güvenle yapar.
  3. Doğrulama (Verification): Ödeme başarılı olduğunda aracı kurum bir Payment ID ve dijital imza üretir. Bizim backend uygulamamız bu imzayı aracı kurum API'sinden doğrulayarak ödemenin sahte olmadığını kesinleştirir ve ardından MongoDB'de sipariş durumunu "Ödendi" olarak günceller.

💻 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ı

  1. Kod Refaktörü (Üretim Modu): Kod içerisindeki tüm hassas veriler (Veri tabanı şifreleri, JWT gizli anahtarları) .env dosyasına alınır. Başlangıç dosyası (package.json), bulut sunucusunun projeyi çalıştırabilmesi için uygun "scripts" komutlarıyla donatılır.
  2. Veri Tabanını Buluta Taşımak (MongoDB Atlas): Yerel bilgisayarımızdaki MongoDB canlı ortamda erişilemez olacağı için, MongoDB'nin resmi bulut servisi olan MongoDB Atlas üzerinde ücretsiz bir küme (Cluster) oluşturulur. Buradan alınan özel bağlantı dizesi (Connection String), sunucunun çevresel değişkenlerine enjekte edilir.
  3. Bulut Sunucu Tercihi (Render / PaaS Platformları): Küçük ve orta ölçekli projeleri veya backend API'lerini hızlıca ayağa kaldırmak için Render veya Heroku benzeri modern PaaS (Platform as a Service) yapıları kullanılır. Bu platformlar projenin kaynak kodlarını doğrudan GitHub deponuzdan çeker.

💻 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):

  1. Kodlarınızı GitHub üzerinde Gizli (Private) veya genel bir depoya (Repository) yükleyin.
  2. MongoDB Atlas hesabınıza giriş yapın, yeni bir veritabanı kullanıcısı ve şifresi oluşturup mongodb+srv://... ile başlayan bağlantı linkinizi kopyalayın.
  3. Render.com adresine gidin, ücretsiz üye olun ve New -> Web Service seçeneğine tıklayın.
  4. GitHub hesabınızı Render'a bağlayıp ilgili depoyu (repository) seçin.
  5. Environment Variables (Çevresel Değişkenler) sekmesine gelerek şu iki kritik anahtarı tanımlayın:
  6. Deploy Web Service butonuna basın. Render, projenizi GitHub'dan çekecek, paketleri kuracak ve size tüm dünyanın erişebileceği https://onrender.com şeklinde benzersiz, güvenli (HTTPS) bir canlı API linki teslim edecektir.

 

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.