Yüklemiş olduğunuz müfredat dosyası oldukça kapsamlı bir MERN Stack (MongoDB, Express, React, Node.js) eğitim içeriğini barındırmaktadır. İstekleriniz doğrultusunda (dakika/saniye, "Önizleme", "ders" ve anlamsız satırların temizlenmesi, özet yapılmaması, detaylı konu açıklamaları, örnekler ve uygulamalar eklenmesi) bu uzun dosyayı yapısal bütünlüğünü bozmadan parça parça ele alacağız.

Eğer süreç boyunca üretim sınırlarına takılıp yarıda kalırsam, kaldığım yeri belirterek sizden onay isteyeceğim.

İlk olarak, talep ettiğiniz üzere "Öğrenecekleriniz" başlığı altındaki maddelerin ne anlama geldiğini detaylıca açıklayarak başlayıp, ardından müfredatın ilk ana bölümlerini tasarlayıp derinlemesine işleyelim.


🔎 "Öğrenecekleriniz" Bölümünün Detaylı Açıklaması

Bu bölüm, modern bir full-stack (uçtan uca) web geliştiricisinin ihtiyaç duyduğu tüm katmanları içeren MERN ekosisteminin yol haritasıdır:


📅 BÖLÜM 1: Değişken Tanımlama, Kapsam (Scope) ve Modern JavaScript (ES6+)

MERN Stack geliştirmeye başlamadan önce, temel motor olan JavaScript'in modern standartlarını (ES6) çok iyi anlamak gerekir.

1. Let & Const ve Scope Kavramı

JavaScript'te değişkenlerin görünürlüğü ve erişilebilirliği Scope (Kapsam) kurallarına bağlıdır. ES6 öncesinde sadece var kullanılırken, günümüzde let ve const standart hale gelmiştir.

💡 Kod Örneği: Scope Farkları

// Global Scope

let globalDegisken = "Ben her yerdeyim";

 

function scopeTest() {

    // Function Scope

    var fonksiyonIci = "Sadece fonksiyon içindeyim";

   

    if (true) {

        // Block Scope

        let blokIciLet = "Sadece bu if bloğundayım";

        const blokIciConst = "Ben de sadece bu if bloğundayım";

        var blokIciVar = "Bana if dışından da erişebilirsin!"; // Çünkü var blok dinlemez!

    }

   

    console.log(blokIciVar); // Çıktı: "Bana if dışından da erişebilirsin!"

    // console.log(blokIciLet); // HATA! let blok dışına çıkamaz.

}

💡 Kod Örneği: Const İncelemesi

const pi = 3.14;

// pi = 3.15; // HATA! Sabit değer değiştirilemez.

 

const kullanici = { ad: "Ahmet", rol: "Admin" };

kullanici.ad = "Mehmet"; // Geçerlidir! Bellekteki referansı değişmedi, sadece içeriği güncellendi.

2. Arrow Function (Ok Fonksiyonları)

Fonksiyon tanımlamayı daha kısa ve okunabilir hale getiren modern bir sözdizimidir. En büyük farkı, kendi this bağlamına (context) sahip olmaması, doğduğu yerdeki this değerini miras almasıdır.

💡 Kod Örneği: Geleneksel vs Arrow Function

// Geleneksel Yöntem

function karesiniAl(sayi) {

    return sayi * sayi;

}

 

// Arrow Function Yöntemi

const karesiniAlArrow = sayi => sayi * sayi; // Tek satırsa return ve süslü paranteze gerek yoktur.

3. Class Yapısı ve Nesne Yönelimli Programlama (OOP)

JavaScript tabanında prototip (prototype) tabanlı bir dildir ancak ES6 ile gelen class sözdizimi, nesne üretmeyi ve miras almayı çok daha kolaylaştırmıştır.

🛠️ Uygulama: Personel Yönetim Sistemi

class Personel {

    constructor(ad, maas) {

        this._ad = ad;

        this.maas = maas;

    }

 

    // Getter

    get ad() {

        return this._ad.toUpperCase();

    }

 

    // Setter

    set maasGuncelle(yeniMaas) {

        if (yeniMaas < 17002) {

            console.log("Hata: Maaş asgari ücretten düşük olamaz!");

        } else {

            this.maas = yeniMaas;

        }

    }

 

    bilgiVer() {

        return `${this.ad} isimli personelin maaşı: ${this.maas} TL.`;

    }

}

 

// Miras Alma (Inheritance)

class Yonetici extends Personel {

    constructor(ad, maas, departman) {

        super(ad, maas); // Üst sınıfın constructor'ını çağırır

        this.departman = departman;

    }

 

    // Method Overriding (Metodu ezme)

    bilgiVer() {

        return `${super.bilgiVer()} Departman: ${this.departman}`;

    }

}

 

// Test Etme

const mudur = new Yonetici("Ahmet Çakıcı", 45000, "Yazılım");

console.log(mudur.bilgiVer()); // Çıktı: AHMET ÇAKICI isimli personelin maaşı: 45000 TL. Departman: Yazılım

mudur.maasGuncelle = 12000; // Çıktı: Hata: Maaş asgari ücretten düşük olamaz!

4. JavaScript'te Asenkron Programlama

JavaScript single-threaded (tek iş parçacıklı) çalışır. Yani aynı anda tek bir işlem yapabilir. Zaman alan işlemlerin (API istekleri, dosya okuma vb.) uygulamayı dondurmaması için asenkron mimari kullanılır.

🛠️ Uygulama: Mock API'den Veri Çekme ve Hata Yönetimi

// Dışarıdan veri getiren asenkron bir fonksiyon simülasyonu (Promise tabanlı)

const veriGetir = (id) => {

    return new Promise((resolve, reject) => {

        setTimeout(() => {

            if (id === 1) {

                resolve({ id: 1, urun: "MERN Kursu", durum: "Aktif" });

            } else {

                reject(new Error("Ürün bulunamadı!"));

            }

        }, 1500); // 1.5 saniye sonra yanıt döner

    });

};

 

// Async/Await Kullanarak Tüketim ve Try-Catch Yönetimi

async function uygulamaCalistir() {

    try {

        console.log("İstek gönderiliyor...");

        const sonuc = await veriGetir(1); // resolve olana kadar kodu burada bekletir

        console.log("Başarılı Sonuç:", sonuc.urun);

       

        console.log("İkinci istek gönderiliyor (Hatalı senaryo)...");

        const hataliSonuc = await veriGetir(2); // reject olacağı için catch bloğuna düşecek

    } catch (hata) {

        console.error("Yakalanan Hata:", hata.message);

    }

}

 

uygulamaCalistir();


📅 BÖLÜM 2: NodeJS'e Giriş ve Temelleri

Node.js, JavaScript'i tarayıcı dışından çıkarıp sunucu tarafında koşturmamızı sağlayan V8 motoru tabanlı bir çalışma ortamıdır (runtime).

1. Yerel Web Sunucusunu Çalıştırma & createServer

Node.js'in yerleşik http modülünü kullanarak dışarıdan gelen tarayıcı isteklerini dinleyen temel bir web sunucusu ayağa kaldırabiliriz.

2. Modüller ve Kendi Modülümüzü Oluşturma

Node.js kodlarımızı organize etmek için modüler bir yapı kullanır. Bir dosyadaki fonksiyon veya nesneyi module.exports ile dışa aktarır, başka dosyada require() ile projeye dahil ederiz.

3. writeHead ve URL İsteğini Yönetme

Gelen isteğin hangi adresten (URL path) geldiğini url modülü ile analiz ederek, kullanıcıya doğru içerik türünü (Content-Type) ve HTTP durum kodunu (200 OK, 404 Not Found) writeHead ile bildiririz.

4. File System (fs) Modülüyle Dosya İşlemleri

Sunucu üzerindeki dosyaları okumak, yazmak, güncellemek veya silmek için kullanılan senkron/asenkron metotlar içeren yerleşik modüldür.

🛠️ Uygulama: HTTP Sunucusu, URL Yönetimi ve FS Entegrasyonu (Localhost:8080)

Bu uygulama, gelen istek dizinine göre bilgisayardaki index.html veya 404.html dosyalarını okuyarak tarayıcıya yanıt verir.

Adım 1: Kendi oluşturduğumuz zaman modülü (zamanModulu.js):

// Kendi modülümüzü yazıyoruz

function su AnkiZamaniGetir() {

    return new Date().toLocaleString();

}

 

module.exports = { suAnkiZamaniGetir }; // Dışa aktarım

Adım 2: Ana sunucu dosyası (server.js):

const http = require('http');

const fs = require('fs');

const url = require('url');

const zaman = require('./zamanModulu'); // Kendi modülümüzü dahil ettik

 

const server = http.createServer((req, res) => {

    // URL parse işlemi ile gelen path yolunu alıyoruz

    const parsedUrl = url.parse(req.url, true);

    const path = parsedUrl.pathname;

 

    console.log(`[${zaman.suAnkiZamaniGetir()}] İstek geldi: ${path}`);

 

    if (path === '/' || path === '/home') {

        // File System ile html dosyasını okuyoruz

        fs.readFile('index.html', (err, data) => {

            if (err) {

                res.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8'});

                res.end("Sunucu Hatası: Dosya okunamadı.");

                return;

            }

            res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});

            res.write(data);

            res.end();

        });

    } else {

        // Sayfa bulunamadığında 404 yanıtı üretiyoruz

        res.writeHead(404, {'Content-Type': 'text/html; charset=utf-8'});

        res.write("<h1>404 Sayfa Bulunamadı</h1>");

        res.end();

    }

});

 

// Sunucu 8080 portunda dinlemeye başlıyor

server.listen(8080, () => {

    console.log("Sunucu http://localhost:8080 üzerinde aktif!");

});

5. NPM, Nodemon ve Nodemailer Modülü

🛠️ Uygulama: Nodemailer ile Sunucudan E-Posta Gönderimi

const nodemailer = require('nodemailer');

 

// Mail sunucusu (SMTP) ayarlarının yapılandırılması

const transporter = nodemailer.createTransport({

    host: "smtp.ethereal.email", // Test amaçlı sanal smtp adresi

    port: 587,

    secure: false,

    auth: {

        user: "test.user@ethereal.email",

        pass: "testPassword123"

    }

});

 

// E-posta içeriğinin hazırlanması

const mailSecenekleri = {

    from: '"MERN Geliştirici" <kurs@mern.com>',

    to: "ogrenci@gmail.com, yonetici@gmail.com", // Çoklu alıcı

    subject: "NodeJS Sizi Çağırıyor! 🚀",

    text: "Bu bir sistem test e-postasıdır.",

    html: "<b>Tebrikler!</b> NodeJS üzerinden başarılı bir şekilde mail gönderdiniz."

};

 

// Gönderim operasyonu

transporter.sendMail(mailSecenekleri, (hata, bilgi) => {

    if (hata) {

        return console.log("Mail Gönderim Hatası:", hata);

    }

    console.log("Mail başarıyla gönderildi! Mesaj ID:", bilgi.messageId);

});

 

 

 

Müfredatımızın bir sonraki aşaması olan veri tabanı entegrasyonları ile devam ediyoruz. Bu bölümde backend dünyasının iki büyük oyuncusunu ele alacağız: İlişkisel veri tabanı MySQL (SQL) ve doküman tabanlı veri tabanı MongoDB (NoSQL).


📅 BÖLÜM 3: NodeJS ve MySQL (İlişkisel Veri Tabanı Dünyası)

MySQL, verileri satırlar ve sütunlardan oluşan tablolar halinde saklayan, tablolar arasında ilişkiler (bire çok, çoğa çok vb.) kurmamızı sağlayan yapılandırılmış bir sistemdir. Node.js tarafında bu bağlantıyı yönetmek için mysql veya mysql2 NPM paketleri kullanılır.

1. MySQL ile NodeJS Bağlantısı ve Yapılandırma

Süreç ilk olarak yerel bilgisayarımıza MySQL Server kurmakla başlar. Ardından projemize npm install mysql komutu ile kütüphaneyi dahil eder ve bağlantı ayarlarını (host, user, password) içeren bir havuz (pool) veya tekil bağlantı nesnesi oluştururuz.

2. SQL Sorgularının (CRUD) NodeJS Üzerinde Yönetimi

🛠️ Uygulama: MySQL Öğrenci Yönetim Sistemi API Altyapısı

Bu senaryoda bir veri tabanı oluşturup, tablo kuracak ve içerisine güvenli bir şekilde veri ekleyip filtreleyerek güncelleyeceğiz.

const mysql = require('mysql');

 

// 1. Veri Tabanı Bağlantı Ayarları

const baglanti = mysql.createConnection({

    host: "localhost",

    user: "root",

    password: "password123",

    database: "okul_otomasyonu" // İlk etapta database oluşturulana kadar bu satır yazılmaz

});

 

baglanti.connect((hata) => {

    if (hata) {

        console.error("MySQL Bağlantı Hatası: " + hata.stack);

        return;

    }

    console.log("MySQL Veri Tabanına Başarıyla Bağlanıldı! ID: " + baglanti.threadId);

});

 

// 2. Tablo Oluşturma (CREATE TABLE)

const tabloOlusturSorgusu = `

    CREATE TABLE IF NOT EXISTS ogrenciler (

        id INT AUTO_INCREMENT PRIMARY KEY,

        ad_soyad VARCHAR(100) NOT NULL,

        adres VARCHAR(255),

        not_ortalamasi DECIMAL(4,2)

    )

`;

 

baglanti.query(tabloOlusturSorgusu, (hata, sonuc) => {

    if (hata) throw hata;

    console.log("Ogrenciler tablosu başarıyla oluşturuldu veya zaten mevcut.");

});

 

// 3. Güvenli Veri Ekleme (INSERT - SQL Injection Korumalı)

const yeniOgrenciSorgu = "INSERT INTO ogrenciler (ad_soyad, adres, not_ortalamasi) VALUES (?, ?, ?)";

const ogrenciVerileri = ["Ahmet Çakıcı", "Malatya", 85.50];

 

baglanti.query(yeniOgrenciSorgu, ogrenciVerileri, (hata, sonuc) => {

    if (hata) throw hata;

    console.log("Kayıt Başarılı! Eklenen Öğrenci ID:", sonuc.insertId);

});

 

// 4. Veri Güncelleme ve Limit Belirleme (UPDATE & LIMIT)

// Belirli bir adresteki öğrencilerin not ortalamasını transaction mantığıyla güvenli güncelleyelim

baglanti.beginTransaction((hata) => {

    if (hata) throw hata;

 

    const guncelleSorgu = "UPDATE ogrenciler SET adres = ? WHERE ad_soyad = ? LIMIT 1";

   

    baglanti.query(guncelleSorgu, ["İstanbul", "Ahmet Çakıcı"], (hata, sonuc) => {

        if (hata) {

            return baglanti.rollback(() => { throw hata; });

        }

       

        baglanti.commit((hata) => {

            if (hata) {

                return baglanti.rollback(() => { throw hata; });

            }

            console.log(`Güncelleme Tamamlandı. Etkilenen Satır Sayısı: ${sonuc.affectedRows}`);

        });

    });

});

 

// 5. Veri Çekme ve Sıralama (SELECT & WHERE & ORDER BY)

const listeleSorgu = "SELECT ad_soyad, not_ortalamasi FROM ogrenciler WHERE not_ortalamasi >= ? ORDER BY not_ortalamasi DESC";

 

baglanti.query(listeleSorgu, [50.00], (hata, sonuclar) => {

    if (hata) throw hata;

    console.log("--- Başarılı Öğrenciler Listesi ---");

    sonuclar.forEach(ogrenci => {

        console.log(`Öğrenci: ${ogrenci.ad_soyad} - Notu: ${ogrenci.not_ortalamasi}`);

    });

   

    // Bağlantıyı kapatıyoruz

    baglanti.end();

});


📅 BÖLÜM 4: NodeJS ve MongoDB (NoSQL Dünyası)

MongoDB, ilişkisel tablolardan uzak, verileri esnek BSON (Binary JSON) biçiminde dokümanlar (documents) halinde saklayan NoSQL tabanlı bir veri tabanıdır. MERN yığınının "M" harfini temsil eder. Şema zorunluluğu olmaması büyük esneklik sağlar.

1. Cloud (Atlas) ve Local Ortam Kurulumları

MongoDB'yi bilgisayarımıza kurabileceğimiz gibi MongoDB Atlas üzerinden bulut (cloud) tabanlı ücretsiz bir küme (cluster) oluşturabiliriz. Atlas panelinden aldığımız gizli bağlantı dizesini (Connection String) kullanıcı adı ve şifre bilgilerimizle projemize bağlarız.

2. CRUD Operasyonları ve Projeksiyon (Projection)

🛠️ Uygulama: Bulut Tabanlı MongoDB Müşteri Yönetimi

Bu uygulamada yerleşik mongodb resmi sürücüsünü kullanarak asenkron mimariyle bulut veri tabanına bağlanacak, veri ekleyip gelişmiş filtreleme ve projeksiyon uygulayacağız.

const { MongoClient } = require('mongodb');

 

// MongoDB Atlas Bağlantı Adresi (Gizli bilgiler içerir)

const uri = "mongodb+srv://kursKullanicisi:GuvenliSifre123@cluster0.abcde.mongodb.net/?retryWrites=true&w=majority";

const istemci = new MongoClient(uri);

 

async function mongodbUygulamasi() {

    try {

        // Bulut sunucusuna bağlanıyoruz

        await istemci.connect();

        console.log("MongoDB Cloud Atlas Bağlantısı Başarıyla Sağlandı!");

 

        const veriTabani = istemci.db("ticaret_merkezi");

        const musteriler = veriTabani.collection("musteri_listesi");

 

        // 1. Doküman Ekleme (INSERT)

        const yeniMusteri = {

            isim: "Mehmet",

            soyisim: "Yılmaz",

            il: "Malatya",

            yas: 34,

            rol: "Veli"

        };

        const eklemeSonucu = await musteriler.insertOne(yeniMusteri);

        console.log(`Yeni müşteri ekleme başarılı. ID: ${eklemeSonucu.insertedId}`);

 

        // 2. Gelişmiş Filtreleme, Sıralama ve Projeksiyon (FIND & QUERY & SORT)

        // Yaşı 18'den büyük olanları bul, isme göre alfabetik sırala, sadece isim ve il bilgisini getir

        const sorguKriteri = { yas: { $gt: 18 } };

        const siralamaAyari = { isim: 1 }; // Alfabetik artan

        const alanSecimi = { projection: { isim: 1, il: 1, _id: 0 } }; // 1: Göster, 0: Gizle

 

        const aktifMusteriler = await musteriler

            .find(sorguKriteri, alanSecimi)

            .sort(siralamaAyari)

            .toArray();

 

        console.log("--- Filtrelenmiş Müşteri Listesi ---", aktifMusteriler);

 

        // 3. Doküman Güncelleme ($set operatörü ile)

        // Rolü "Veli" olan kaydı bulup ilini Malatya olarak güncelle

        const guncellenecekKriter = { rol: "Veli" };

        const yeniDegerler = { $set: { il: "Malatya" } };

 

        const guncellemeSonucu = await musteriler.updateOne(guncellenecekKriter, yeniDegerler);

        console.log(`Güncellenen doküman sayısı: ${guncellemeSonucu.modifiedCount}`);

 

        // 4. Doküman Silme (DELETE)

        // İsmi Mehmet olan veya ismi "A" harfi ile başlayanları silmek için regex kullanımı

        const silmeKriteri = { isim: /^A/ }; // A ile başlayanlar (Düzenli ifade)

        const silmeSonucu = await musteriler.deleteMany(silmeKriteri);

        console.log(`Sistemden ${silmeSonucu.deletedCount} adet kayıt temizlendi.`);

 

    } catch (hata) {

        console.error("Operasyon sırasında hata meydana geldi:", hata);

    } finally {

        // Bağlantıyı güvenli kapatma

        await istemci.close();

    }

}

 

mongodbUygulamasi();


📅 BÖLÜM 5: ExpressJS ile Restful API Geliştirme

ExpressJS, Node.js üzerine inşa edilmiş, HTTP isteklerini, yönlendirmelerini (routing) ve ara yazılımları (middleware) yönetmeyi son derece basitleştiren minimal ve esnek bir backend framework'üdür. Saf Node.js ile onlarca satır süren sunucu kodlarını birkaç satıra indirger.

1. ExpressJS Kurulumu ve Esnek Port Yapısı

Sunucuyu lokalde test ederken belirli bir port (örn: 3000) kullanırken, projeyi canlı sunucuya (hosting/cloud) yüklediğimizde sunucunun bize atayacağı dinamik portu okumak zorundayız. Bu yüzden process.env.PORT yapısı kurgulanır.

2. HTTP Metotları ve İstek Yönetimi (Request Handling)

3. URL Params ve Query Strings Farkı

4. Joi Paketi ile Validation (Veri Kontrolü)

İstemciden gelen verilerin (POST/PUT isteklerinde body içeriklerinin) güvenli, doğru tipte ve eksiksiz olduğunu doğrulamak için kullanılan güçlü bir şema doğrulama kütüphanesidir. Hatalı veri daha veri tabanına ulaşmadan engellenir.

🛠️ Uygulama: ExpressJS Tabanlı Gelişmiş Öğrenci RESTful API'si

Bu uygulama, bir öğrenci dizisini (bellek üzerinde) yöneten; şema doğrulamalı, hata yakalamalı tam donanımlı bir RESTful API sunar. Postman ile test edilmeye hazırdır.

const express = require('express');

const Joi = require('joi'); // npm install joi

 

const uygulama = express();

uygulama.use(express.json()); // Gelen JSON verilerini (body) ayrıştırmak için middleware

 

// Bellek üzerinde örnek veri seti

let ogrenciler = [

    { id: 1, ad: "Ahmet Çakıcı", bolum: "Yazılım" },

    { id: 2, ad: "Elif Yılmaz", bolum: "Siber Güvenlik" }

];

 

// Joi Doğrulama Şeması Tanımlama

const ogrenciSema = Joi.object({

    ad: Joi.string().min(3).max(50).required(),

    bolum: Joi.string().min(2).required()

});

 

// 1. TÜM ÖĞRENCİLERİ GETİR (GET) - Query ile limit desteği

uygulama.get('/api/ogrenciler', (req, res) => {

    const limit = parseInt(req.query.limit);

    if (limit && limit > 0) {

        return res.json(ogrenciler.slice(0, limit));

    }

    res.json(ogrenciler);

});

 

// 2. ID'YE GÖRE TEK ÖĞRENCİ GETİR (GET - URL Params)

uygulama.get('/api/ogrenciler/:id', (req, res) => {

    const arananId = parseInt(req.params.id);

    const ogrenci = ogrenciler.find(o => o.id === arananId);

   

    if (!ogrenci) {

        return res.status(404).send({ hata: "Belirtilen ID'ye sahip öğrenci bulunamadı!" });

    }

    res.json(ogrenci);

});

 

// 3. YENİ ÖĞRENCİ EKLE (POST - Validation Dahil)

uygulama.post('/api/ogrenciler', (req, res) => {

    // Joi ile gelen gövdeyi doğrula

    const { error } = ogrenciSema.validate(req.body);

    if (error) {

        return res.status(400).send({ hata: error.details[0].message });

    }

 

    const yeniOgrenci = {

        id: ogrenciler.length + 1, // Otomatik artan ID simülasyonu

        ad: req.body.ad,

        bolum: req.body.bolum

    };

 

    ogrenciler.push(yeniOgrenci);

    res.status(201).json(yeniOgrenci); // 201: Created

});

 

// 4. ÖĞRENCİ GÜNCELLE (PUT)

uygulama.put('/api/ogrenciler/:id', (req, res) => {

    const arananId = parseInt(req.params.id);

    const ogrenci = ogrenciler.find(o => o.id === arananId);

   

    if (!ogrenci) return res.status(404).send({ hata: "Öğrenci bulunamadı!" });

 

    // Veri kontrolü

    const { error } = ogrenciSema.validate(req.body);

    if (error) return res.status(400).send({ hata: error.details[0].message });

 

    ogrenci.ad = req.body.ad;

    ogrenci.bolum = req.body.bolum;

    res.json(ogrenci);

});

 

// 5. ÖĞRENCİ SİL (DELETE)

uygulama.delete('/api/ogrenciler/:id', (req, res) => {

    const arananId = parseInt(req.params.id);

    const ogrenci = ogrenciler.find(o => o.id === arananId);

   

    if (!ogrenci) return res.status(404).send({ hata: "Öğrenci bulunamadı!" });

 

    const index = ogrenciler.indexOf(ogrenci);

    ogrenciler.splice(index, 1); // Diziden silme operasyonu

 

    res.json({ mesaj: "Öğrenci başarıyla silindi", silinen: ogrenci });

});

 

// ESNEK PORT YAPILANDIRMASI

const PORT = process.env.PORT || 3000;

uygulama.listen(PORT, () => {

    console.log(`Sunucu ${PORT} portunda başarıyla ayağa kalktı.`);

});

 

 

Müfredatımızın bir sonraki aşaması olan sunucu taraflı şablonlama sistemleri, veri modelleme standartları ve gerçek senaryolu Mini Projelerin (URL Shorter & Gelişmiş To-Do) arka uç (backend) mimarileriyle derinlemesine devam ediyoruz.


📅 BÖLÜM 6: Node ve Frontend İlişkisi (Template Engine & EJS)

React gibi modern kütüphanelere geçmeden önce, sunucuda üretilen dinamik verileri HTML sayfalarına doğrudan gömmek için Template Engine (Şablon Motorları) kullanılır. Kurs kapsamında EJS (Embedded JavaScript) motoru ele alınmaktadır.

1. EJS Nedir, Kurulumu ve JavaScript İşlemleri

EJS, standart HTML dosyaları içerisine özel etiketler (<%= %>, <% %>) yardımıyla doğrudan JavaScript kodları (döngüler, koşullar) yazmanızı sağlar.

2. Partial Templates (Şablonları Dahil Etme)

Web sitelerindeki menü (Navbar) ve alt bilgi (Footer) gibi alanlar her sayfada ortaktır. EJS mimarisinde bu ortak alanlar ayrı dosyalara ayrılır ve <%- include('parca/navbar') %> komutuyla ana sayfalara dahil edilerek kod tekrarı önlenir.

3. Statik Dosyalara Erişim & Body Parser Kontrolü

Resimler, CSS dosyaları ve istemci taraflı JavaScript kodları statik dosyalardır. Express sunucusuna bu dosyaların yerini bildirmek için express.static('public') ara yazılımı (middleware) kullanılır. Gelen klasik HTML form POST isteklerini yakalayıp okumak için ise express.urlencoded() yapısı devreye alınır.

🛠️ Uygulama: EJS Tabanlı Dinamik Portfolyo ve İletişim Sayfası

Adım 1: Ortak alt bilgi dosyası (views/parca/footer.ejs):

<footer>

    <p>&copy; 2026 MERN Stack Geliştirici Kursu - Tüm Hakları Saklıdır.</p>

</footer>

Adım 2: Ana sayfa dosyası (views/index.ejs):

<!DOCTYPE html>

<html lang="tr">

<head>

    <meta charset="UTF-8">

    <link rel="stylesheet" href="/css/stil.css"> <!-- Statik CSS -->

    <title>Yönetim Paneli</title>

</head>

<body>

    <h1>Sistemdeki Kayıtlı Kursiyerler</h1>

    <ul>

        <!-- EJS ile döngü kuruyoruz -->

        <% kursiyerler.forEach(function(kisi) { %>

            <li>

                <strong><%= kisi.ad %></strong>

                <% if(kisi.admin) { %>

                    <span style="color: red;">(Yönetici)</span>

                <% } %>

            </li>

        <% }) %>

    </ul>

 

    <h2>İletişim Formu</h2>

    <form action="/iletisim" method="POST">

        <input type="text" name="mesaj" placeholder="Mesajınız" required>

        <button type="submit">Gönder</button>

    </form>

 

    <%- include('parca/footer') %> <!-- Partial şablon dahil etme -->

</body>

</html>

Adım 3: Sunucu dosyası (app.js):

const express = require('express');

const app = express();

 

app.set('view engine', 'ejs'); // Görünüm motoru olarak EJS ayarlandı

app.use(express.static('assets')); // 'assets' klasörü statik dosyalara açıldı

app.use(express.urlencoded({ extended: true })); // Form verilerini okuma middleware

 

let veriSeti = [

    { ad: "Ahmet Çakıcı", admin: true },

    { ad: "Mehmet Yılmaz", admin: false }

];

 

app.get('/', (req, res) => {

    // index.ejs dosyasına verileri parametre olarak render ediyoruz

    res.render('index', { kursiyerler: veriSeti });

});

 

app.post('/iletisim', (req, res) => {

    console.log("Formdan Gelen Veri:", req.body.mesaj); // Konsola yazdırma

    res.send("Mesajınız sunucu tarafından başarıyla alındı!");

});

 

app.listen(4000);


📅 BÖLÜM 7: Model & Schema Kavramları ve Mongoose Entegrasyonu

MongoDB saf sürücüsü esneklik sunsa da büyük projelerde verilerin belirli bir kurala, tipe ve şemaya (Schema) bağlı kalması istenir. Mongoose, Node.js ile MongoDB arasında bir köprü görevi gören nesne veri modelleme (ODM) kütüphanesidir.

1. Şema (Schema) ve Model Kavramı

2. Nested Schema (İç İçe Şema) Mantığı

NoSQL mimarisinin en büyük avantajı, ilişkisel veri tabanlarındaki gibi sürekli tabloları birleştirmek (JOIN) yerine, bir dökümanın içerisine başka bir şemayı dizi veya nesne olarak doğrudan gömebilmesidir (Embedding). Örneğin, bir "Kullanıcı" dökümanının altında o kullanıcıya ait "Notlar" dizisi iç içe saklanabilir.

🛠️ Uygulama: Mongoose ile Gelişmiş Nested To-Do Veri Modeli ve CRUD

Aşağıdaki kod mimarisinde, tek bir kullanıcı dökümanı içerisinde alt şemalar barındıran tam donanımlı bir Mongoose akışı yer almaktadır.

const mongoose = require('mongoose');

 

// MongoDB Bağlantısı (.env dosyasından çekilmesi önerilir)

mongoose.connect('mongodb://localhost:27017/notebookDB')

    .then(() => console.log("Mongoose ile MongoDB Bağlantısı Kuruldu."))

    .catch(hata => console.error("Bağlantı hatası:", hata));

 

// Alt Şema (Nested Schema) - Görevler/Notlar

const NotSema = new mongoose.Schema({

    baslik: { type: String, required: true },

    icerik: String,

    olusturulmaTarihi: { type: Date, default: Date.now } // Otomatik tarih

});

 

// Ana Şema - Kullanıcı

const KullaniciSema = new mongoose.Schema({

    kullaniciAdi: { type: String, required: true, unique: true },

    sifre: { type: String, required: true },

    gorevler: [NotSema] // İç içe şema yapısı (Nested array)

});

 

// Model Tanımlama

const Kullanici = mongoose.model('Kullanici', KullaniciSema);

 

// --- OPERASYONLAR ---

 

// 1. Yeni Kullanıcı ve Nested Not Kaydı (INSERT)

async function kullaniciVeNotEkle() {

    try {

        const yeniKullanici = new Kullanici({

            kullaniciAdi: "ahmetcakici",

            sifre: "123456",

            gorevler: [

                { baslik: "MERN Kursu İzlenecek", icerik: "ExpressJS bölümüne geçiş yapılacak." }

            ]

        });

        const sonuc = await yeniKullanici.save();

        console.log("Kullanıcı ve iç içe görevi başarıyla kaydedildi:", sonuc._id);

    } catch (err) {

        console.error("Kayıt hatası:", err.message);

    }

}

 

// 2. Nested Şema İçine Yeni Not Ekleme (UPDATE - Array Push)

async function kullanıcıyaYeniNotEkle(kullaniciId, yeniNotObjesi) {

    try {

        // İlgili kullanıcıyı bulup dizisine yeni eleman push ediyoruz

        const guncellenen = await Kullanici.findByIdAndUpdate(

            kullaniciId,

            { $push: { gorevler: yeniNotObjesi } },

            { new: true } // Güncellenmiş halini geri döndür

        );

        console.log("Kullanıcıya yeni not eklendi. Güncel Görev Sayısı:", guncellenen.gorevler.length);

    } catch (err) {

        console.error(err);

    }

}

 

// 3. Nested Şema İçinden Not Silme (DELETE - Array Pull)

async function notSil(kullaniciId, notId) {

    try {

        // $pull operatörü alt dizideki belirli bir ID'ye sahip elemanı tamamen söker atar

        await Kullanici.findByIdAndUpdate(

            kullaniciId,

            { $pull: { gorevler: { _id: notId } } }

        );

        console.log("Alt dökümandaki ilgili not başarıyla silindi.");

    } catch (err) {

        console.error(err);

    }

}


📅 BÖLÜM 8: Mini Proje: URL Shorter (Link Kısaltma Operasyonu)

Bu mini proje; backend mantığını, veri tabanı ilişkilerini ve mantıksal algoritmaları kavramak için mükemmel bir tam senaryodur. Projenin amacı: Kullanıcıdan gelen uzun bir URL'yi benzersiz, kısa bir kod haline getirip veri tabanına yazmak ve kısa kod çağrıldığında kullanıcıyı asıl linke yönlendirmektir.

🗺️ Mimari ve Teorik Mantık

  1. Kullanıcı arayüzden (frontend) uzun linki gönderir.
  2. Backend bu linki alır, benzersiz bir zaman damgası (Timestamp) veya rastgele karakter havuzu üreterek kısa kod (Short Code) oluşturur.
  3. Veri tabanında (Örn: MySQL) 3 ana sütun tasarlanır: id, orjinal_url, kisa_kod.
  4. Kullanıcı tarayıcıdan http://localhost:4000/git/XYZ123 adresine gittiğinde sunucu XYZ123 kodunu yakalar, veri tabanından orijinal adresi sorgular ve HTTP 302 koduyla yönlendirme (Redirect) işlemini gerçekleştirir.

🛠️ Uygulama: URL Shorter Sunucu ve Yönlendirme Altyapısı

const express = require('express');

const mysql = require('mysql');

const app = express();

 

app.use(express.json()); // JSON istekleri için

 

// Veri tabanı yapılandırması

const db = mysql.createConnection({

    host: "localhost",

    user: "root",

    password: "password123",

    database: "url_shorter_db"

});

 

// 1. LİNK KISALTMA ENDPOINT'İ (POST)

app.post('/api/kisalt', (req, res) => {

    const { orjinalUrl } = req.body;

    if (!orjinalUrl) return res.status(400).json({ hata: "Orijinal URL zorunludur." });

 

    // Benzersiz kısa kod üretme operasyonu (Timestamp tabanlı basit yöntem)

    const kisaKod = Date.now().toString(36).slice(-6); // Benzersiz 6 karakter

 

    const sorgu = "INSERT INTO linkler (orjinal_url, kisa_kod) VALUES (?, ?)";

    db.query(sorgu, [orjinalUrl, kisaKod], (hata, sonuc) => {

        if (hata) return res.status(500).json({ hata: "Veri tabanı hatası." });

       

        res.json({

            mesaj: "Link başarıyla kısaltıldı!",

            kisaLink: `http://localhost:4000/git/${kisaKod}`

        });

    });

});

 

// 2. YÖNLENDİRME OPERASYONU (GET)

app.get('/git/:kod', (req, res) => {

    const gelenKod = req.params.kod;

 

    const sorgu = "SELECT orjinal_url FROM linkler WHERE kisa_kod = ?";

    db.query(sorgu, [gelenKod], (hata, sonuclar) => {

        if (hata || sonuclar.length === 0) {

            return res.status(404).send("<h1>Hata: Kırık veya geçersiz kısa link!</h1>");

        }

 

        const asilLink = sonuclar[0].orjinal_url;

        // HTTP 302 Yönlendirmesi gerçekleştiriliyor

        res.redirect(asilLink);

    });

});

 

app.listen(4000, () => console.log("URL Shorter Sunucusu 4000 Portunda Aktif."));

 

MERN yığınının en güçlü ve popüler katmanı olan ön yüz (Frontend - ReactJS) ekosistemi, durum yönetimi mekanizmaları ve full-stack entegrasyon süreçleriyle derinlemesine devam ediyoruz.


📅 BÖLÜM 9: Projelerimizde Kullanmak Üzere Temel ReactJS

ReactJS, kullanıcı arayüzleri (UI) inşa etmek için Facebook tarafından geliştirilen, bileşen tabanlı (component-based) açık kaynaklı bir JavaScript kütüphanesidir. Sayfayı tamamen yenilemek yerine sadece değişen alanları güncelleyen Virtual DOM mimarisini kullanır.

1. Dosya Yapısı ve Çalışma Mantığı

2. State, Props ve Değişmezlik (Immutability) İlkesi

🛠️ Uygulama: Immutability ve Spread Operator Destekli To-Do Bileşeni

Aşağıdaki örnekte, sınıf tabanlı (Class Component) mimari kullanılarak React'ın temel çalışma prensipleri ve dizi operasyonları simüle edilmiştir.

import React, { Component } from 'react';

 

class KategoriListesi extends Component {

    render() {

        // Üst bileşenden gelen props verisini okuyoruz

        return <h5>Mevcut Kategori: {this.props.aktifKategori}</h5>;

    }

}

 

export default class ToDoApp extends Component {

    constructor(props) {

        super(props);

        // Bileşenin yerel durum yönetimi (State)

        this.state = {

            gorevler: ["ExpressJS API tamamlanacak", "Mongoose Şeması kurulacak"],

            yeniGorevMetni: ""

        };

    }

 

    // Input kutusuna her yazı yazıldığında tetiklenen fonksiyon

    girdiDegisimi = (etkinlik) => {

        this.setState({ yeniGorevMetni: etkinlik.target.value });

    }

 

    // Listeye eleman ekleme operasyonu (Immutability Uygunluğu)

    gorevEkle = () => {

        if (this.state.yeniGorevMetni.trim() === "") return;

 

        this.setState({

            // SPREAD OPERATOR: Mevcut dizinin elemanlarını açar ve yeni elemanı sona ekleyerek yeni bir dizi referansı oluşturur

            gorevler: [...this.state.gorevler, this.state.yeniGorevMetni],

            yeniGorevMetni: "" // Inputu temizleme

        });

    }

 

    // Listeden eleman silme operasyonu (Dizi İndeksi İle)

    gorevSil = (silinecekIndeks) => {

        // Mevcut state dizisini filtreleyerek kopyasını oluşturuyoruz

        const guncelListe = this.state.gorevler.filter((_, indeks) => indeks !== silinecekIndeks);

        this.setState({ gorevler: guncelListe });

    }

 

    render() {

        return (

            <div className="container mt-4">

                <h2>Yapılacaklar Listesi (React UI)</h2>

                {/* Alt bileşene props ile veri gönderme */}

                <KategoriListesi aktifKategori="Yazılım Geliştirme" />

 

                <div className="input-group my-3">

                    <input

                        type="text"

                        className="form-control"

                        value={this.state.yeniGorevMetni}

                        onChange={this.girdiDegisimi}

                        placeholder="Yeni görev yazın..."

                    />

                    <button className="btn btn-primary" onClick={this.gorevEkle}>Ekle</button>

                </div>

 

                <ul className="list-group">

                    {/* JavaScript map fonksiyonu ile diziyi JSX elemanlarına dönüştürme */}

                    {this.state.gorevler.map((gorev, indeks) => (

                        <li key={indeks} className="list-group-item d-flex justify-content-between align-items-center">

                            {gorev}

                            <button className="btn btn-danger btn-sm" onClick={() => this.gorevSil(indeks)}>Sil</button>

                        </li>

                    ))}

                </ul>

            </div>

        );

    }

}


📅 BÖLÜM 10: MERN Stack Mini Proje: "Fikir İlet" (Full-Stack Entegrasyonu)

Bu mini proje, bağımsız olarak çalışan React ön yüzü ile Node.js/Express arka yüzünü tam anlamıyla bir araya getiren, veri tabanı kayıtlarını ve API yanıt mekanizmalarını doğrulayan uçtan uca bir sistemdir.

🗺️ Mimari ve Haberleşme Teorisi

  1. Frontend Katmanı (React): Bootstrap CDN yardımıyla tasarlanmış hızlı bir form içerir. Kullanıcı adını, e-postasını ve fikrini forma girer. Form gönderildiğinde veriler fetch veya axios kütüphanesiyle backend sunucusuna HTTP POST isteği olarak fırlatılır.
  2. Backend Katmanı (Node.js & Express): İstekleri karşılar, Mongoose modeli aracılığıyla verileri MongoDB'de "fikirler" isimli koleksiyona kaydeder. İşlem başarılıysa veya hata aldıysa ön yüze anlamlı JSON yanıtları döner.
  3. Yönetici Paneli ve Giriş Sistemi: Sistemde kayıtlı olan fikirleri sadece yetkili kullanıcıların görebilmesi için bir admin paneli tasarlanır. Yönetici kullanıcı adı ve şifresini girdiğinde backend veriyi sorgular ve eşleşme durumuna göre panele erişim izni vererek fikirleri listeler.

🛠️ Sunucu Tarafı: Mongoose Modeli ve API Uç Noktaları (server.js)

const express = require('express');

const mongoose = require('mongoose');

const cors = require('cors'); // Farklı portlardaki (3000 -> 5000) haberleşme engelini (CORS) aşmak için

 

const app = express();

app.use(express.json());

app.use(cors());

 

mongoose.connect('mongodb://localhost:27017/fikirIletDB');

 

// Mongoose Şeması

const FikirSema = new mongoose.Schema({

    adSoyad: String,

    eposta: String,

    fikirMetni: String,

    tarih: { type: Date, default: Date.now }

});

const Fikir = mongoose.model('Fikir', FikirSema);

 

// 1. FORM VERİSİNİ ALIP VERİ TABANINA KAYDETME (POST)

app.post('/api/fikirler/ekle', async (req, res) => {

    try {

        const yeniFikir = new Fikir(req.body);

        await yeniFikir.save();

        // Ön yüze yeşil mesaj kutusu tetiklesin diye başarı flag'i dönüyoruz

        res.status(201).json({ durum: true, mesaj: "Fikriniz veri tabanına başarıyla iletildi!" });

    } catch (hata) {

        res.status(500).json({ durum: false, mesaj: "Sistem hatası, kayıt başarısız." });

    }

});

 

// 2. YÖNETİCİ GİRİŞİ DOĞRULAMA (POST)

app.post('/api/admin/giris', (req, res) => {

    const { kullaniciAdi, sifre } = req.body;

    if (kullaniciAdi === "admin" && sifre === "admin34") {

        res.json({ girisBasarili: true });

    } else {

        res.status(401).json({ girisBasarili: false, mesaj: "Kullanıcı adı veya şifre hatalı!" });

    }

});

 

// 3. FİKİRLERİ LİSTELEME (GET)

app.get('/api/admin/fikirler', async (req, res) => {

    const tumFikirler = await Fikir.find().sort({ tarih: -1 });

    res.json(tumFikirler);

});

 

app.listen(5000, () => console.log("Fikir İlet API Sunucusu 5000 portunda aktif."));

🛠️ İstemci Tarafı: React Form ve API Tüketim Entegrasyonu (FikirFormu.js)

import React, { useState } from 'react';

 

export default function FikirFormu() {

    // Hooks kullanımı ile durum yönetimleri (Alternatif modern yapı)

    const [formVerisi, setFormVerisi] = useState({ adSoyad: '', eposta: '', fikirMetni: '' });

    const [apiMesaj, setApiMesaj] = useState({ tip: '', icerik: '' });

 

    const formGonder = async (e) => {

        e.preventDefault(); // Sayfanın yenilenmesini engeller

 

        const yanıt = await fetch('http://localhost:5000/api/fikirler/ekle', {

            method: 'POST',

            headers: { 'Content-Type': 'application/json' },

            body: JSON.stringify(formVerisi)

        });

 

        const veri = await yanıt.json();

 

        if (veri.durum) {

            setApiMesaj({ tip: 'success', icerik: veri.mesaj });

            setFormVerisi({ adSoyad: '', eposta: '', fikirMetni: '' }); // Formu temizleme

        } else {

            setApiMesaj({ tip: 'danger', icerik: veri.mesaj });

        }

    };

 

    return (

        <div className="container mt-5" style={{ maxWidth: '500px' }}>

            <h3 className="mb-4">Fikir İlet Projesi</h3>

 

            {/* Dinamik geri bildirim mesaj kutuları (Kırmızı/Yeşil uyarılar) */}

            {apiMesaj.icerik && (

                <div className={`alert alert-${apiMesaj.tip}`} role="alert">

                    {apiMesaj.icerik}

                </div>

            )}

 

            <form onSubmit={formGonder}>

                <div className="mb-3">

                    <label className="form-label">Ad Soyad</label>

                    <input

                        type="text"

                        className="form-control"

                        value={formVerisi.adSoyad}

                        onChange={(e) => setFormVerisi({...formVerisi, adSoyad: e.target.value})}

                        required

                    />

                </div>

                <div className="mb-3">

                    <label className="form-label">E-Posta</label>

                    <input

                        type="email"

                        className="form-control"

                        value={formVerisi.eposta}

                        onChange={(e) => setFormVerisi({...formVerisi, eposta: e.target.value})}

                        required

                    />

                </div>

                <div className="mb-3">

                    <label className="form-label">Fikriniz</label>

                    <textarea

                        className="form-control"

                        rows="3"

                        value={formVerisi.fikirMetni}

                        onChange={(e) => setFormVerisi({...formVerisi, fikirMetni: e.target.value})}

                        required

                    ></textarea>

                </div>

                <button type="submit" className="btn btn-success w-100">Fikri Gönder</button>

            </form>

        </div>

    );

}


📅 BÖLÜM 11: Çevre Yönetimi (.env) ve Canlıya Alma (Deployment)

MERN Stack projeleri açık kaynak olarak GitHub gibi platformlarda yayınlanmadan veya canlı sunuculara yüklenmeden önce güvenlik önlemleri alınmalı ve ardından ölçeklenebilir altyapılara aktarılmalıdır.

1. Dotenv (.env) ve Gitignore Kullanımı

Veri tabanı bağlantı adresleri, API anahtarları, kullanıcı şifreleri ve özel port numaraları kaynak kodların içinde yalın halde yazılmamalıdır.

2. Uygulamayı Yayınlama (Deploying) Süreçleri


 

"Sıfırdan Zirveye Javascript ve NodeJS Kursu 2020" müfredat dosyasını, verdiğin tüm talimatlar (parça parça ele alma, dakika/saniye/önizleme/ders satırlarını silme, anlamsız satırları temizleme, özet yapmama, konu açıklamalarına önem verme ve detaylı örnekler/uygulamalar ekleme) doğrultusunda işlemeye başlıyorum.

Dosya çok kapsamlı ve uzun olduğu için, eğitim kalitesini en üst düzeyde tutmak adına içeriği parça parça (bölüm bölüm) ele alacağım. İlk olarak talep ettiğin "Öğrenecekleriniz" paragrafının detaylı açıklamasıyla başlıyoruz.


🌟 "Öğrenecekleriniz" Paragrafının Detaylı Açıklaması

Bu kurs, modern web geliştirmenin hem ön yüzünü (Frontend) hem de arka yüzünü (Backend) kapsayan, yani tam donanımlı bir geliştirici (Full-Stack Developer) olmanızı hedefleyen bir yapıya sahiptir. Maddelerin detaylı analizi şu şekildedir:


🛠 BÖLÜM 1: Geliştirme Ortamı ve Hızlı Kod Yazım Teknikleri

📌 Visual Studio Code Kurulumu ve Kısayollar

JavaScript ve web teknolojileri geliştirmek için günümüzdeki en popüler kod editörü Visual Studio Code (VS Code) aracıdır. Hızlı kod yazmak ve verimliliği artırmak için editörün sunduğu temel mekanizmalar şunlardır:


🚀 BÖLÜM 2: JavaScript Temelleri, Veri Tipleri ve Operatörler

🔎 Değişken ve Sabit Kavramları (var, let, const)

JavaScript'te verileri hafızada tutmak için kutular (değişkenler) oluştururuz. Bu işlem için üç farklı anahtar kelime kullanılır:

  1. var: Eski JavaScript standardına aittir. Fonksiyon kapsamlıdır (function-scoped) ve aynı isimle tekrar tanımlanabilir. Günümüz projelerinde güvenlik ve karmaşa yaratmaması adına tercih edilmez.
  2. let: Modern JavaScript (ES6+) ile gelmiştir. Blok kapsamlıdır (block-scoped). Yani sadece tanımlandığı {} parantezleri arasında yaşar. Değeri daha sonra değiştirilebilir.
  3. const: "Constant" yani sabit kelimesinden gelir. Blok kapsamlıdır. Tanımlandığı an değer ataması yapılmalıdır ve bu değer daha sonra değiştirilemez.

🛠 Kod Örneği ve Uygulama:

// Değişken tanımlamaları

let kullaniciAdi = "Ahmet";

kullaniciAdi = "Mehmet"; // Geçerli, let değeri değiştirilebilir.

 

const piSayisi = 3.14;

// piSayisi = 3.15; // HATA! const değeri değiştirilemez.

 

// İsimlendirmede Camel Case kuralı (küçük harfle başlar, sonraki kelime büyük)

let dogumTarihiYili = 1995;

 

let tanimsizDegisken;

console.log(tanimsizDegisken); // Çıktı: undefined (Değer atanmadığı için tanımsız)

🔎 İlkel (Primitive) Veri Tipleri

JavaScript'te bellekte doğrudan değer tutan ilkel veri tipleri şunlardır:

🔎 Karşılaştırma ve Mantıksal Operatörler


🔄 BÖLÜM 3: Kontrol Yapıları ve Döngüler

🔎 Şart İfadeleri ve Kısa Kullanımlar (If-Else, Ternary If, Switch-Case)

Programın belirli durumlara göre farklı yollar izlemesini sağlar.

🛠 Kod Örneği ve Uygulama:

let yas = 20;

 

// Geleneksel If-Else

if (yas >= 18) {

    console.log("Ehliyet alabilirsiniz.");

} else {

    console.log("Yaşınız tutmuyor.");

}

 

// Ternary If Kullanımı (Kısa Yazım)

let mesaj = (yas >= 18) ? "Ehliyet alabilir." : "Ehliyet alamaz.";

console.log(mesaj);

🔎 Döngüler (For, While, Break, Continue)

Belirli bir kod bloğunu ardı ardına tekrar ettirmek için döngüler kullanılır.

🛠 Uygulama Projesi: Kullanıcı Sayısına Kadar Asal Sayı Bulma

İç içe iki adet for döngüsü kullanarak, belirlenen bir sayıya kadar olan asal sayıları bulan algoritma uygulaması:

let sinirSayisi = 20;

 

for (let i = 2; i <= sinirSayisi; i++) {

    let asalMi = true;

   

    for (let j = 2; j < i; j++) {

        if (i % j === 0) {

            asalMi = false; // Sayı tam bölündü, asal değil.

            break; // İçteki döngüyü sonlandır, sonraki sayıya geç.

        }

    }

   

    if (asalMi) {

        console.log(`${i} bir asal sayıdır.`);

    }

}


📦 BÖLÜM 4: Fonksiyonlar, Diziler (Array) ve Nesneler (Object)

🔎 Fonksiyon Kavramı ve Kapsam (Scope, Arrow Function)

Fonksiyonlar, belirli bir işi yapan ve tekrar tekrar çağrılabilen kod bloklarıdır.

🛠 Kod Örneği ve Uygulama:

// Geleneksel Fonksiyon Tanımlama

function kareAl(sayi) {

    return sayi * sayi;

}

 

// Arrow Function Yapısı (Kısa Yazım)

// Tek parametre varsa paranteze gerek yoktur, tek satır return varsa süslü paranteze gerek yoktur.

const arrowKareAl = sayi => sayi * sayi;

 

console.log(arrowKareAl(5)); // Çıktı: 25

🔎 Referans Tiplerin Çalışma Mantığı

İlkel veri tipleri (let x = 5) belleğin Stack bölgesinde değerleriyle tutulurken; Diziler (Array) ve Nesneler (Object) Heap bölgesinde tutulur ve bellekteki adreslerini (referanslarını) paylaşırlar.

let diziA =;

let diziB = diziA; // diziB, diziA'nın bellekteki adresini kopyaladı.

 

diziB.push(4);

console.log(diziA); // Çıktı: [1, 2, 3, 4] -> diziA da değişti! Çünkü ikisi de aynı adrese bakıyor.


 

🧠 BÖLÜM 5: Higher Order | Callback Functions ve Array Helper Metotları

🔎 Higher Order ve Callback Functions Mantığı

JavaScript'te fonksiyonlar birer "birinci sınıf vatandaştır" (first-class citizens). Yani bir fonksiyon, başka bir fonksiyona parametre olarak gönderilebilir veya bir fonksiyondan geriye başka bir fonksiyon döndürülebilir.

🛠 Kod Örneği ve Uygulama:

// Callback fonksiyonumuz

const ikiyleCarp = sayi => sayi * 2;

 

// Higher Order fonksiyonumuz (Dışarıdan fonksiyon kabul ediyor)

const islemiUygula = (sayi, callback) => {

    return callback(sayi);

};

 

console.log(islemiUygula(5, ikiyleCarp)); // Çıktı: 10

🔎 Modern Dizi Yardımcı Metotları (map, filter, reduce)

Bu metotlar, orijinal diziyi bozmadan (immutable) dizinin elemanları üzerinde döngüler kurarak işlem yapmamızı sağlar.

  1. map: Dizideki tüm elemanları bir callback fonksiyonundan geçirerek aynı uzunlukta yeni bir dizi üretir.
  2. filter: Callback fonksiyonundan true dönen elemanları seçerek daha kısa veya eşit uzunlukta yeni bir dizi oluşturur.
  3. reduce: Dizideki tüm elemanları tek bir değere (sayı, string, obje veya tek bir dizi) indirger. Bir kümülatif (akümülatör) mantığıyla çalışır.

🛠 Uygulama Projesi: Zincirleme Metot Kullanımı (Method Chaining)

Aşağıdaki örnekte bir kullanıcı dizisindeki çift sayıları filtreleyip, karelerini alacağız ve en son bu karelerin toplamını tek bir hamlede bulacağız:

const sayilar =;

 

const sonuc = sayilar

    .filter(sayi => sayi % 2 === 0)      // 1. Çiftleri filtrele: [2, 4, 6]

    .map(sayi => sayi * sayi)            // 2. Karelerini al: [4, 16, 36]

    .reduce((toplam, sayi) => toplam + sayi, 0); // 3. Topla: 4 + 16 + 36 = 56

 

console.log("Zincirleme İşlem Sonucu:", sonuc); // Çıktı: 56

🔎 Dizileri Kopyalamak ve Spread (...) Operatörü

Referans tiplerin birbirini ezmesini önlemek ve güvenli bir şekilde diziyi kopyalamak için ES6 Spread (...) operatörü kullanılır. Bu operatör dizinin elemanlarını kutusundan çıkarıp yeni bir dizi içerisine saçar.

const orijinalDizi =;

const kopyaDizi = [...orijinalDizi]; // Tamamen yeni bir bellek adresi oluşturuldu.

 

kopyaDizi.push(4);

console.log("Orijinal:", orijinalDizi); // Çıktı: [1, 2, 3] (Değişmedi)

console.log("Kopya:", kopyaDizi);       // Çıktı: [1, 2, 3, 4]


🌐 BÖLÜM 6: DOM Kavramı, Etkinlikler (Eventler) ve Veri Depolama

🔎 DOM (Document Object Model) Nedir?

DOM, tarayıcının HTML dökümanını bir ağaç yapısı nesnesine dönüştürmesidir. JavaScript bu nesne köprüsünü kullanarak HTML elementlerini seçebilir, silebilir, güncelleyebilir veya yeni elementler ekleyebilir.

🔎 Olaylar (Eventler), Bubbling ve Delegation

🔎 Veri Depolama (Local ve Session Storage)

Kullanıcı tarayıcısında verileri küçük string formatlarında saklama yöntemleridir.

🛠 Uygulama Projesi: Tarayıcı Hafızalı Not Sepeti Altyapısı

Bir arayüzde girilen notları localStorage üzerinde saklama ve sayfa yenilendiğinde kaybetmeme mantığının kurgulanması:

// LocalStorage'a veri yazma (Veriler string olmalıdır bu yüzden JSON.stringify kullanılır)

const notlariKaydet = (notlarDizisi) => {

    localStorage.setItem('kullaniciNotlari', JSON.stringify(notlarDizisi));

};

 

// LocalStorage'dan veri okuma (String veriyi tekrar diziye çevirmek için JSON.parse kullanılır)

const notlariGetir = () => {

    const hamVeri = localStorage.getItem('kullaniciNotlari');

    return hamVeri ? JSON.parse(hamVeri) : [];

};

 

// Uygulama Testi

let yapılacaklar = ["JavaScript Çalış", "NodeJS Kur"];

notlariKaydet(yapılacaklar);

 

let hafizadakiNotlar = notlariGetir();

console.log("Tarayıcıdan Okunan Notlar:", hafizadakiNotlar);


🏗 BÖLÜM 7: Nesne Yönelimli Programlama (OOP) Detayları ve ES6 Class Yapısı

🔎 Prototype Kavramı ve Kalıtım (Inheritance)

JavaScript, sınıf tabanlı (Class-based) dillerden farklı olarak prototip tabanlı (Prototype-based) bir dildir. Bir kurucu fonksiyondan (Constructor) üretilen tüm nesneler (instance'lar), bellekte gereksiz yer kaplamasın diye ortak metotlarını prototip nesnesi üzerinden miras alırlar ve paylaşırlar.

🔎 ES6 Class Yapısı

ES6 ile birlikte gelen class anahtar kelimesi, JavaScript'in prototip yapısını arka planda gizleyen ve diğer dillerdeki nesne yönelimli sözdizimine benzemesini sağlayan bir yapıdır (Syntactic Sugar).

🛠 Kod Örneği ve Uygulama (Rehber Uygulaması Temeli):

// Üst Sınıf (Base Class)

class Kisi {

    constructor(ad, soyad) {

        this.ad = ad;

        this.soyad = soyad;

    }

   

    // Prototip metodu (Tüm nesneler ortak kullanır)

    kendiniTanit() {

        return `Merhaba, ben ${this.ad} ${this.soyad}.`;

    }

}

 

// Kalıtım Alan Alt Sınıf (Subclass)

class Ogrenci extends Kisi {

    constructor(ad, soyad, okulNumarasi) {

        super(ad, soyad); // Ata sınıfın constructor'ını tetikler.

        this.okulNumarasi = okulNumarasi;

    }

   

    // Polymorphism (Çok Biçimlilik) - Üstteki metodu ezme

    kendiniTanit() {

        return `${super.kendiniTanit()} Okul numaram: ${this.okulNumarasi}`;

    }

}

 

const yeniOgrenci = new Ogrenci("Emre", "Altunbilek", "12345");

console.log(yeniOgrenci.kendiniTanit());

// Çıktı: Merhaba, ben Emre Altunbilek. Okul numaram: 12345


💥 BÖLÜM 8: Gelişmiş JavaScript Terimleri ve Motorun Çalışma Yapısı

🔎 Execution Context, Lexical Environment ve Hoisting

🔎 this Anahtar Kelimesi ve Bağlama Yöntemleri

this kelimesi her zaman fonksiyonun nasıl çağrıldığına bağlı olarak çalışma zamanında (runtime) anlam kazanır.

🔎 Closure (Kapsama) Kavramı

Bir fonksiyonun, kendi dışındaki kapsamda (Lexical Environment) bulunan değişkenleri, o dış kapsamın çalışması bitmiş olsa dahi hatırlaması ve onlara erişebilmesi durumudur.

🛠 Kod Örneği ve Uygulama:

function sayacOlustur() {

    let sayac = 0; // Dış fonksiyona ait lokal bir değişken

   

    return function() {

        sayac++; // İçteki fonksiyon dıştaki değişkeni hafızada tutuyor.

        return sayac;

    };

}

 

const tiklamaSayaci = sayacOlustur();

console.log(tiklamaSayaci()); // Çıktı: 1

console.log(tiklamaSayaci()); // Çıktı: 2 -> sayac değişkeni yok edilmedi, yaşıyor!


 

⏳ BÖLÜM 9: Asenkron JavaScript Mimarisi, Web API ve Veri Çekme (Axios & Fetch)

🔎 Asenkron Çalışma Mantığı ve Single Thread Yapısı

JavaScript, Single Thread (tek iş parçacıklı) çalışan bir dildir; yani aynı anda sadece tek bir satır kod yürütebilir. Ancak dosya okuma, veri tabanına bağlanma veya harici bir API'den veri çekme gibi işlemler zaman alır. JavaScript bu uzun süren işlerin ana işlem hattını (Call Stack) kilitlemesini engellemek için asenkron mimariyi kullanır.

🔎 Callback Hell (Geri Çağırma Cehennemi) ve Çözümü: Promises

Asenkron işlemler sıraya konulmak istendiğinde, eski yöntem olan iç içe geçmiş callback fonksiyonları kodu okunamaz hale getirir. Buna Callback Hell denir. Bu problemi çözmek için ES6 ile Promise (Söz) yapısı geliştirilmiştir. Bir Promise 3 durumda bulunabilir:

  1. Pending: İşlem henüz başlatıldı, bekliyor.
  2. Fulfilled: İşlem başarıyla tamamlandı (.then() bloğu tetiklenir).
  3. Rejected: İşlem sırasında hata oluştu (.catch() bloğu tetiklenir).

🔎 Async / Await Sözdizimi

Promises yapısını daha da sadeleştirmek ve asenkron kodları tıpkı senkron kod yazar gibi düz bir satır halinde yazabilmek için geliştirilen modern ES8 sözdizimidir. async ile işaretlenen bir fonksiyon otomatik olarak Promise döner; await ise o satırdaki asenkron işlem çözülene kadar kodun yürütülmesini durdurur.

🛠 Uygulama Projesi: Gerçek Bir API Kaynağından Veri Çekme ve Yönetme (Axios Örneği)

Aşağıdaki örnekte HTTP metodlarını (GET, POST, PUT, DELETE) ve API istekleri öncesinde araya girip loglama yapmamızı sağlayan Interceptors mimarisini kurguluyoruz:

// Axios instance (Özel Yapılandırma) Oluşturma

const apiServisi = axios.create({

    baseURL: 'https://ornek.com',

    timeout: 5000,

    headers: {'X-Custom-Header': 'KursUygulamasi'}

});

 

// Axios Interceptor (Araya Girici) Tanımlama

apiServisi.interceptors.request.use(config => {

    console.log(`${config.method.toUpperCase()} isteği atılıyor: ${config.url}`);

    return config;

}, error => {

    return Promise.reject(error);

});

 

// Async/Await ile Veri Yönetimi Fonksiyonları

async function kullaniciGorevleriniYonet() {

    try {

        // 1. GET İsteği: Veri Çekme

        const veriCekmeCevap = await apiServisi.get('/kullanicilar');

        console.log("Kullanıcı Listesi:", veriCekmeCevap.data);

 

        // 2. POST İsteği: Yeni Veri Ekleme

        const yeniKullanici = { ad: "Emre", rol: "Eğitmen" };

        const eklemeCevap = await apiServisi.post('/kullanicilar', yeniKullanici);

        console.log("Eklendi:", eklemeCevap.data);

 

        // 3. PUT İsteği: Tüm Veriyi Güncelleme

        const guncelVeri = { ad: "Emre Altunbilek", rol: "Baş Eğitmen" };

        const guncellemeCevap = await apiServisi.put('/kullanicilar/1', guncelVeri);

        console.log("Güncellendi:", guncellemeCevap.data);

 

    } catch (hata) {

        console.error("API İşlem Hatası:", hata.message);

    }

}


📦 BÖLÜM 10: Modüler Yapı ve Proje Araçları (ES6 Modules, Webpack & Parcel)

🔎 ES6 Module Sistemi (Import ve Export)

Büyük projelerde tüm kodları tek dosyaya yazmak karmaşaya yol açar. Dosyalar arası veri ve fonksiyon akışını sağlamak için modül yapısı kullanılır.

🛠 Kod Örneği ve Uygulama:

// matematik.js (Modül Dosyası)

export const topla = (a, b) => a + b; // Named Export

export const carp = (a, b) => a * b;  // Named Export

 

const hesapMakinesiIsmi = "JS_Hesaplayici";

export default hesapMakinesiIsmi;     // Default Export

 

// ana_uygulama.js (Ana Giriş Dosyası)

import HerhangiBirIsim, { topla, carp } from './matematik.js';

 

console.log(HerhangiBirIsim); // Çıktı: JS_Hesaplayici

console.log(topla(5, 3));      // Çıktı: 8

🔎 Neden Webpack ve Düzenleyiciler (Bundlers) Kullanmalıyız?

Yazdığımız modüler JavaScript kodlarını, Sass/Scss dosyalarını ve görselleri tarayıcıların doğrudan anlaması zordur ya da performans kaybına yol açar. Webpack ve Parcel gibi araçlar, projedeki tüm farklı bağımlılıkları tarayarak bunları tarayıcının tek bir hamlede okuyabileceği optimize edilmiş birkaç dosyaya (bundle.js, style.css) dönüştürür.


🎛 BÖLÜM 11: Sunucu Tarafı (Node.js Başlangıç ve NPM Paket Yönetimi)

🔎 Node.js Nedir ve Global Nesnesi

Node.js, JavaScript'in sadece tarayıcılarda değil, bilgisayarlarda ve sunucularda da çalışmasını sağlayan bir çalışma zamanı (runtime) ortamıdır. Google Chrome'un V8 JavaScript motoru üzerine inşa edilmiştir.

🔎 NPM (Node Package Manager) ve Semantic Versioning

NPM, dünyanın en büyük yazılım kayıt defteridir ve Node.js ile birlikte hazır gelir. Projelerimize harici kütüphaneler eklememizi sağlar.

🛠 Uygulama Projesi: CLI (Komut Satırı) Üzerinden Çalışan Rehber Uygulaması

yargs ve chalk paketlerini kullanarak komut satırından parametre alan ve verileri JSON dosyasında saklayan bir rehber uygulaması kurgusu:

// rehber.js Dosyası

const fs = require('fs'); // Node.js yerleşik dosya sistemi modülü

const chalk = require('chalk'); // Konsolu renklendirme paketi

 

// Dosyadan verileri okuma fonksiyonu

const rehberiYukle = () => {

    try {

        const veriBuffer = fs.readFileSync('rehber.json');

        return JSON.parse(veriBuffer.toString());

    } catch (e) {

        return []; // Dosya yoksa boş dizi dön

    }

};

 

// Yeni kişi ekleme ve JSON dosyasına kaydetme fonksiyonu

const kisiEkle = (ad, telefon) => {

    const rehber = rehberiYukle();

   

    // Aynı isimde biri var mı kontrolü

    const mukerrerKontrol = rehber.find(kisi => kisi.ad === ad);

   

    if (!mukerrerKontrol) {

        rehber.push({ ad, telefon });

        fs.writeFileSync('rehber.json', JSON.stringify(rehber));

        console.log(chalk.green.inverse('Kişi başarıyla kaydedildi!'));

    } else {

        console.log(chalk.red.inverse('Bu isimde bir kayıt zaten var!'));

    }

};

 

module.exports = { kisiEkle };


🌐 BÖLÜM 12: Express.js ile Web Sunucusu ve RESTful API Tasarımı

🔎 Express Modülü ve HTTP İstekleri

Node.js'in yerleşik http modülüyle sunucu yazmak çok fazla kod hammallığı gerektirir. Express.js, yönlendirmeleri (routing), istekleri ve cevapları çok daha temiz yönetmemizi sağlayan minimalist bir web çatısıdır (framework).

🔎 Middleware (Ara Katman) Zinciri

Express'in kalbini Middleware yapıları oluşturur. Bir middleware, sunucuya gelen istek (request) ile istemciye gönderilecek cevap (response) arasında çalışan fonksiyonlardır. İstek verilerini doğrulamak, log tutmak veya oturum kontrolü yapmak için zincirleme şekilde sırayla çalıştırılırlar. next() fonksiyonu çağrılarak zincirdeki bir sonraki middleware'e geçiş sağlanır.

🛠 Uygulama Projesi: Express, Joi ve RESTful API CRUD Operasyonları

Joi modülü ile gelen verilerin doğrulanmasını (validation) sağlayan ve tüm veri akışını yöneten RESTful kullanıcı API'si uygulaması:

const express = require('express');

const Joi = require('joi'); // Veri doğrulama paketi

const app = express();

 

app.use(express.json()); // Built-in Middleware: Gelen JSON gövdelerini ayrıştırır.

 

let kullanicilar = [

    { id: 1, ad: "Ahmet", yas: 25 },

    { id: 2, ad: "Ayşe", yas: 30 }

];

 

// 1. GET ALL: Tüm kullanıcıları listeleme

app.get('/api/kullanicilar', (req, res) => {

    res.send(kullanicilar);

});

 

// 2. POST: Yeni kullanıcı ekleme ve Joi Validation

app.post('/api/kullanicilar', (req, res) => {

    // Doğrulama kuralları şeması

    const sema = Joi.object({

        ad: Joi.string().min(3).required(),

        yas: Joi.number().integer().min(18).required()

    });

 

    const { error } = sema.validate(req.body);

    if (error) return res.status(400).send({ hata: error.details[0].message });

 

    const yeniKullanici = {

        id: kullanicilar.length + 1,

        ad: req.body.ad,

        yas: req.body.yas

    };

    kullanicilar.push(yeniKullanici);

    res.status(201).send(yeniKullanici);

});

 

// 3. PUT: Kullanıcı Güncelleme

app.put('/api/kullanicilar/:id', (req, res) => {

    const kullanici = kullanicilar.find(k => k.id === parseInt(req.params.id));

    if (!kullanici) return res.status(404).send({ hata: "Kullanıcı bulunamadı!" });

 

    kullanici.ad = req.body.ad || kullanici.ad;

    kullanici.yas = req.body.yas || kullanici.yas;

    res.send(kullanici);

});

 

// 4. DELETE: Kullanıcı Silme

app.delete('/api/kullanicilar/:id', (req, res) => {

    const kullaniciIndex = kullanicilar.findIndex(k => k.id === parseInt(req.params.id));

    if (kullaniciIndex === -1) return res.status(404).send({ hata: "Kullanıcı bulunamadı!" });

 

    const silinenKullanici = kullanicilar.splice(kullaniciIndex, 1);

    res.send(silinenKullanici[0]);

});

 

app.listen(3000, () => console.log('Sunucu 3000 portunda ayakta...'));


 

🗄️ BÖLÜM 13: MongoDB Veritabanı ve Mongoose ile Veri Modelleme

🔎 SQL ve NoSQL Arasındaki Farklar

🔎 Mongoose Şeması (Schema), Modeller ve Virtual Alanlar

Mongoose, Node.js ile MongoDB arasında veri doğrulamayı, ilişki yönetimini ve şema tabanlı modellemeyi kolaylaştıran bir ODM (Object Document Mapper) kütüphanesidir.

🛠 Uygulama Projesi: Mongoose ile Esnek Şema ve CRUD Operasyonları

Gelişmiş veri tipleri, validasyonlar, sanal alanlar ve şema metotlarını barındıran kullanıcı modeli uygulaması:

const mongoose = require('mongoose');

 

// MongoDB Bağlantısı Kurulması

mongoose.connect('mongodb://localhost:27017/kursVeritabani')

    .then(() => console.log('MongoDB bağlantısı başarılı.'))

    .catch(hata => console.error('Bağlantı hatası:', hata));

 

// Şema Tanımlanması

const KullaniciSchema = new mongoose.Schema({

    ad: { type: String, required: true, trim: true },

    soyad: { type: String, required: true, trim: true },

    email: { type: String, required: true, unique: true, lowercase: true },

    sifre: { type: String, required: true, minlength: 6 },

    yas: { type: Number, min: 18, max: 100 },

    roller: { type: Array, default: ['kullanici'] }

}, { timestamps: true }); // Otomatik createdAt ve updatedAt alanları ekler.

 

// Virtual (Sanal) Alan Tanımlama

KullaniciSchema.virtual('tamAd').get(function() {

    return `${this.ad} ${this.soyad}`;

});

 

// Modelin Oluşturulması

const Kullanici = mongoose.model('Kullanici', KullaniciSchema);

 

// Sayfalama (Pagination) Mantığı ile Veri Sorgulama Fonksiyonu

async function kullanicilariListele(sayfaNo, limit) {

    try {

        const atlanacakVeriSayisi = (sayfaNo - 1) * limit;

       

        const sonuclar = await Kullanici.find()

            .skip(atlanacakVeriSayisi) // Belirtilen sayı kadar veriyi atla

            .limit(limit)              // Belirtilen sayı kadar veri getir

            .sort({ createdAt: -1 });  // En yeniye göre sırala

           

        console.log(`Sayfa ${sayfaNo} Verileri:`, sonuclar);

    } catch (hata) {

        console.error("Sorgu Hatası:", hata);

    }

}


🔐 BÖLÜM 14: RESTful API Güvenliği, Şifreleme ve Yetkilendirme (JWT & Bcrypt)

🔎 Şifre Güvenliği (Bcrypt)

Veritabanları hacklenebilir. Bu yüzden kullanıcı şifreleri veritabanına asla düz metin (plain text) olarak kaydedilmemelidir. Bcrypt kütüphanesi, şifreyi tek yönlü bir karma fonksiyonundan geçirir ve üzerine rastgele karakterler ekleyerek (Salt) geri dönüştürülemez bir şifre bloğu oluşturur.

🔎 JWT (JSON Web Token) ile Kimlik Doğrulama Mantığı

Geleneksel web sitelerinde oturumlar sunucu tarafında (Session) tutulur. Ancak RESTful API'ler Stateless (durumsuz) olmalıdır; yani sunucu hangi kullanıcının istek attığını hafızasında tutmaz. Bunun yerine JWT kullanılır.

🛠 Uygulama Projesi: JWT ve Bcrypt Entegreli Kimlik Doğrulama Middleware Yapısı

Kullanıcının güvenli şekilde şifrelenerek kaydedilmesi, giriş yapması ve korunan rotalara erişimini yöneten API katmanı:

const express = require('express');

const bcrypt = require('bcrypt');

const jwt = require('jsonwebtoken');

const app = express();

app.use(express.json());

 

const JWT_SECRET = 'cok_gizli_anahtar_cumlesi';

 

// 1. REGISTER (Kayıt Olma Rotaları)

app.post('/api/auth/kayit', async (req, res) => {

    try {

        // Şifreyi Bcrypt ile tuzlayıp şifreleme (Salt & Hash)

        const tuzlanmisSifre = await bcrypt.hash(req.body.sifre, 10);

       

        const yeniKullanici = {

            email: req.body.email,

            sifre: tuzlanmisSifre,

            rol: req.body.rol || 'kullanici'

        };

        // (Burada veritabanına kaydetme işlemi yapılır)

        res.status(201).send({ mesaj: "Kullanıcı oluşturuldu." });

    } catch (hata) {

        res.status(500).send(hata.message);

    }

});

 

// 2. MIDDLEWARE: Token Doğrulama ve Rol Yetki Kontrolü

const yetkiKontrolu = (kabulEdilenRoller) => {

    return (req, res, next) => {

        const authHeader = req.headers['authorization'];

        const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN ayıklama

 

        if (!token) return res.status(401).send({ hata: "Erişim engellendi. Token eksik." });

 

        jwt.verify(token, JWT_SECRET, (hata, kullaniciVerisi) => {

            if (hata) return res.status(403).send({ hata: "Geçersiz veya süresi dolmuş token." });

           

            // Kullanıcı rollerini kontrol etme

            const yetkiVarMi = kullaniciVerisi.roller.some(r => kabulEdilenRoller.includes(r));

            if (!yetkiVarMi) return res.status(403).send({ hata: "Bu işlem için yetkiniz yok." });

           

            req.kullanici = kullaniciVerisi; // İstek nesnesine kullanıcıyı ekle

            next(); // Güvenli rotaya geçiş izni ver

        });

    };

};

 

// 3. KORUNAN ROTA: Sadece Adminlerin Erişebileceği Rota

app.get('/api/admin/paneli', yetkiKontrolu(['admin']), (req, res) => {

    res.send({ mesaj: "Hoş geldiniz Admin. Güvenli veriler aşağıdadır..." });

});


🖼️ BÖLÜM 15: Sunucu Tarafı Arayüz Motoru (EJS Template Engine) ve Oturum Yönetimi

🔎 Template Engine (Şablon Motoru) Nedir?

Sunucu tarafında hazırlanan dinamik verilerin, HTML şablonları içerisine gömülerek tarayıcıya hazır HTML çıktısı olarak gönderilmesini sağlayan yapılardır. EJS (Embedded JavaScript Templates) sözdizimi olarak doğrudan standart HTML kodlarının arasına <% %> etiketleri yardımıyla JavaScript döngüleri, şart ifadeleri ve değişkenleri yazmamıza imkan tanır.

🔎 Dosya Yükleme İşlemleri (Multer)

HTML formları üzerinden sunucuya resim, PDF gibi fiziksel dosyalar gönderilirken formun enctype="multipart/form-data" özelliğine sahip olması gerekir. Express bu veriyi doğrudan okuyamaz. Gelen dosyaları yakalamak, boyut sınırlandırması yapmak ve sunucu klasörlerine güvenli isimlerle kaydetmek için Multer middleware kütüphanesi kullanılır.

🛠 Uygulama Projesi: Multer ile Güvenli Profil Resmi Yükleme Altyapısı

Yüklenen dosyaların uzantılarını kontrol eden ve benzersiz isimlerle sunucuya kaydeden yapılandırma:

const multer = require('multer');

const path = require('path');

 

// Disk Depolama Yapılandırması (Storage Configuration)

const diskDepolama = multer.diskStorage({

    destination: function (req, file, cb) {

        cb(null, 'uploads/profil_resimleri'); // Dosyaların kaydedileceği klasör

    },

    filename: function (req, file, cb) {

        // Çakışma olmaması için benzersiz dosya adı üretme: benzersiz_id + orijinal_uzanti

        const benzersizEk = Date.now() + '-' + Math.round(Math.random() * 1E9);

        cb(null, file.fieldname + '-' + benzersizEk + path.extname(file.originalname));

    }

});

 

// Dosya Türü Filtreleme (Sadece Resim Kabul Etme)

const resimFiltresi = (req, file, cb) => {

    if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {

        cb(null, true); // Dosyayı kabul et

    } else {

        cb(new Error('Yalnızca JPEG veya PNG formatında resim yükleyebilirsiniz!'), false);

    }

};

 

const upload = multer({

    storage: diskDepolama,

    fileFilter: resimFiltresi,

    limits: { fileSize: 1024 * 1024 * 2 } // Maksimum 2 MB boyut sınırı

});

 

// Express Rotasında Kullanımı

// 'profilResmi' formdaki input alanının 'name' özelliğidir.

app.post('/api/kullanici/profil-guncelle', upload.single('profilResmi'), (req, res) => {

    if (!req.file) return res.status(400).send({ hata: "Lütfen bir dosya seçin." });

   

    // Veritabanına kaydedilecek dosya yolu

    const dosyaYolu = req.file.path;

    res.send({ mesaj: "Profil resmi başarıyla yüklendi.", yol: dosyaYolu });

});


☁️ BÖLÜM 16: Canlıya Alma ve Sunucu Yönetimi (Deployment & VPS Configuration)

🔎 MongoDB Atlas ve Bulut Veritabanı Taşınması

Uygulama canlıya alınırken yerel bilgisayardaki veritabanı (localhost) erişilebilir olmayacaktır. Bu yüzden veriler, MongoDB firmasının bulut hizmeti olan MongoDB Atlas üzerine taşınır ve güvenli ağ erişim izinleri (IP Whitelisting) tanımlanarak sunucu bağlantı cümlesi (Connection String) bulut adresiyle güncellenir.

🔎 VPS (Virtual Private Server) Üzerinde Linux Sunucu Yapılandırması

Digital Ocean gibi servislerden bir Linux (Örn: Ubuntu) bulut sunucusu (Droplet) kiralandığında, sistem güvenliği ve uygulama yönetimi için aşağıdaki adımlar uygulanır:

  1. Güvenli Kullanıcı Oluşturma ve SSH Koruması: root kullanıcısı ile doğrudan işlem yapmak risklidir. Sudo yetkilerine sahip yeni bir sistem kullanıcısı oluşturulur ve şifre yerine SSH anahtarlarıyla kriptografik olarak güvenli bağlantı sağlanır.
  2. Güvenlik Duvarı (UFW): Sunucunun dış dünyaya açık portları sınırlandırılır. Sadece SSH (22), HTTP (80) ve HTTPS (443) portlarına izin verilir.
  3. Nginx ve Reverse Proxy (Ters Vekil Sunucu): Node.js uygulamaları genellikle dahili bir portta (Örn: 3000) çalışır. Ancak kullanıcıların web sitenize erişirken adres çubuğuna port yazmaması gerekir. Nginx, sunucunun en önünde duran çok güçlü bir web sunucusudur. 80 (HTTP) portundan gelen istekleri karşılar ve arka planda çalışan Node.js uygulamasının 3000 portuna şeffaf bir şekilde yönlendirir (Proxy Pass).
  4. SSL Sertifikası (Certbot / Let's Encrypt): Sitedeki tüm veri trafiğini (şifreler, kredi kartları) uçtan uca şifrelemek için Let's Encrypt altyapısı kullanılarak sunucuya ücretsiz SSL sertifikası kurulur ve trafik HTTPS protokolüne zorlanır.

 

🌐 BÖLÜM 17: Modern Web Arayüzü Geliştirme (HTML5 Yapısı)

🔎 HTML5 Standartları ve Sayfa İskeleti

HTML5, web sayfalarının metinsel yapısını, hiyerarşisini ve içeriğini tarayıcıya bildiren bir işaretleme dilidir. Sayfanın arama motorları (SEO) ve ekran okuyucular tarafından doğru anlaşılabilmesi için anlamsal (semantic) etiketlerin kullanılması modern web standartlarının temelini oluşturur.

🔎 Anlamsal (Semantic) Elementler ve Öznitelikler (Attributes)

Eski web standartlarında tüm sayfalar anlam ayrımı olmaksızın <div> kutularıyla bölünürken, HTML5 ile her bölümün kendine has anlamsal bir karşılığı gelmiştir:

🛠 Uygulama Projesi: Modern ve Anlamsal Bir HTML5 Sayfa İskeleti

<!DOCTYPE html>

<html lang="tr">

<head>

    <!-- Karakter kodlaması ve responsive uyumluluk ayarları -->

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Sıfırdan Zirveye Kurs Arayüzü</title>

</head>

<body>

 

    <!-- Sayfa Başlığı ve Navigasyon -->

    <header>

        <div class="logo">YazılımAkademisi</div>

        <nav>

            <ul>

                <li><a href="#anasayfa">Anasayfa</a></li>

                <li><a href="#kurslar">Kurslar</a></li>

                <li><a href="#iletisim">İletişim</a></li>

            </ul>

        </nav>

    </header>

 

    <!-- Ana İçerik Alanı -->

    <main>

        <section id="kurslar">

            <h2>Aktif Eğitimlerimiz</h2>

           

            <!-- Bağımsız bir kart içeriği -->

            <article class="kurs-kart">

                <h3>Node.js & MongoDB RESTful API Geliştirme</h3>

                <p>Bu eğitimde backend mimarilerini uçtan uca öğreniyoruz.</p>

            </article>

        </section>

    </main>

 

    <!-- Alt Bilgi Alanı -->

    <footer>

        <p>&copy; 2026 Yazılım Akademisi. Tüm Hakları Saklıdır.</p>

    </footer>

 

</body>

</html>


🎨 BÖLÜM 18: Modern Mizanpaj ve Tasarım Dünyası (CSS3, Flexbox & Grid)

🔎 CSS Kutu Modeli (Box Model)

Tarayıcıdaki her HTML elementi, etrafında görünmez birer kutu barındırır. Bu kutu modelinin davranışlarını yönetmek, arayüz tasarımının temel taşıdır.

🔎 CSS Flexbox (Esnek Kutu Modeli)

Flexbox, tek boyutlu (satır bazlı ya da sütun bazlı) hizalama ve alan dağıtımı sağlayan modern bir mizanpaj aracıdır. Özellikle menüler, buton grupları ve yan yana dizilmesi gereken dinamik kartlar için biçilmiş kaftandır.

🔎 CSS Grid (Izgara Modeli)

Grid, iki boyutlu (aynı anda hem satırlar hem de sütunlar) karmaşık sayfa yerleşimleri tasarlamak için kullanılır. Sayfayı hayali bir matrise bölerek elementlerin bu matris üzerindeki koordinatlarını belirlememizi sağlar.

🛠 Uygulama Projesi: Flexbox ile Navigasyon ve Grid ile Foto Galeri Tasarımı

/* Genel Sıfırlama ve Kutu Modeli Ayarı */

* {

    margin: 0;

    padding: 0;

    box-sizing: border-box;

}

 

/* 1. FLEXBOX UYGULAMASI: Navigasyon Çubuğu */

.nav-bar {

    display: flex;

    justify-content: space-between; /* Sol logo ve sağ menüyü iki uca yaslar */

    align-items: center;            /* Dikeyde tam ortalar */

    background-color: #2c3e50;

    padding: 15px 30px;

}

 

.nav-links {

    display: flex;

    list-style: none;

    gap: 20px; /* Menü elemanlarının arasına boşluk bırakır */

}

 

/* 2. GRID UYGULAMASI: Responsive Foto Galeri */

.galeri-konteyner {

    display: grid;

    /* auto-fit ve minmax ile ekran küçüldükçe otomatik alt satıra geçen responsive yapı */

    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

    gap: 15px;

    padding: 30px;

}

 

.galeri-item {

    background-color: #ecf0f1;

    border: 1px solid #bdc3c7;

    border-radius: 8px;

    padding: 20px;

    text-align: center;

    transition: transform 0.3s ease; /* Yumuşak animasyon geçişi */

}

 

/* 3. ANIMASYON UYGULAMASI: Hover Efekti */

.galeri-item:hover {

    transform: translateY(-5px); /* Fareyle üzerine gelindiğinde yukarı kayar */

    box-shadow: 0 5px 15px rgba(0,0,0,0.1);

}


⚡ BÖLÜM 19: CSS Animasyonları ve Gelişmiş Özellikler

🔎 @keyframes ve Zamanlama Fonksiyonları

CSS ile statik bir arayüzü hareketlendirmek için @keyframes kuralı ile animasyonun başlangıç (0%) ve bitiş (100%) durumlarındaki stilleri tanımlanır. Ardından bu animasyon ilgili elemente bağlanarak çalışma süresi (duration) ve hız eğrisi (timing-function) ayarlanır.

🛠 Kod Örneği ve Uygulama: Animasyonlu Yükleniyor (Loader) Butonu

/* Döngüsel Dönme Animasyonunun Tanımlanması */

@keyframes dondurmeEfekti {

    0% {

        transform: rotate(0deg);

    }

    100% {

        transform: rotate(360deg);

    }

}

 

/* Animasyonlu Buton Stili */

.islem-butonu {

    position: relative;

    padding: 12px 24px;

    background-color: #3498db;

    color: white;

    border: none;

    border-radius: 4px;

    cursor: pointer;

    font-size: 16px;

}

 

/* Butonun üzerine gelindiğinde tetiklenecek efekt */

.islem-butonu:hover {

    background-color: #2980b9;

}

 

/* Yüklenme durumunda butona eklenecek dönme ikonu */

.islem-butonu.yukleniyor::after {

    content: "";

    position: absolute;

    width: 16px;

    height: 16px;

    top: 50%;

    right: 15px;

    margin-top: -8px;

    border: 2px solid transparent;

    border-top-color: white;

    border-radius: 50%;

    /* Tanımlanan animasyonu sonsuz döngüde lineer olarak çalıştırır */

    animation: dondurmeEfekti 0.8s linear infinite;

}


🛠️ BÖLÜM 20: CSS Ön İşlemcileri ve Mimari Yapılar (Sass / Scss & BEM)

🔎 Neden Sass/Scss Öğrenmeliyiz?

CSS projeleri büyüdükçe stiller binlerce satıra ulaşır ve yönetilemez hale gelir. Sass (Syntactic Awesome Stylesheets), CSS kodlarına programlama dili özellikleri (değişkenler, iç içe yapılar, fonksiyonlar, döngüler, modüller) kazandıran bir ön işlemcidir (precompiler). Yazılan .scss dosyaları bir derleyici vasıtasıyla standart .css dosyasına dönüştürülür.

  1. Değişkenler (Variables): Renk, font ve boyut gibi değerleri tek bir merkezde tanımlayıp projenin her yerinde kullanmayı sağlar ($ana-renk: #3498db;).
  2. İç İçe Yapılar (Nested Structures): HTML hiyerarşisini CSS koduna yansıtarak seçici kalabalığını önler.
  3. Mixins: Tekrar eden kod bloklarını tek sefer tanımlayıp parametre aktararak farklı yerlerde çağırmamıza imkan tanır.

🔎 BEM (Block, Element, Modifier) Standartları

BEM, büyük projelerde CSS sınıf isimlerinin karmaşıklaşmasını önlemek ve isimlendirme çakışmalarını engellemek için kullanılan dünya standardı bir metodolojidir:

🛠 Uygulama Projesi: Scss Yapısı ve BEM Metodolojisi Kombinasyonu

Aşağıdaki örnekte modern bir kart bileşeninin Scss özellikleri, ampersand (&) operatörü ve BEM kurallarına uygun olarak mimari mizanpajının çıkarılması gösterilmiştir:

// _degiskenler.scss (Kök Değişken Tanımlamaları)

$ana-renk: #e74c3c;

$yazi-rengi: #2c3e50;

$arkaplan-gri: #f9f9f9;

 

// _mixinler.scss (Yeniden Kullanılabilir Bloklar)

@mixin esnek-merkezleme {

    display: flex;

    justify-content: center;

    align-items: center;

}

 

// ana_stil.scss (Bileşen Kodlaması)

.kurs-karti { // [B]lock

    background-color: $arkaplan-gri;

    border-radius: 10px;

    padding: 20px;

   

    &__baslik { // [E]lement -> .kurs-karti__baslik halini alır

        font-size: 20px;

        color: $yazi-rengi;

       

        &--vurgulu { // [M]odifier -> .kurs-karti__baslik--vurgulu halini alır

            color: $ana-renk;

            font-weight: bold;

        }

    }

   

    &__buton-alani { // [E]lement -> .kurs-karti__buton-alani halini alır

        @include esnek-merkezleme; // Mixin yapısını dahil ettik

        margin-top: 15px;

    }

}


 

🚀 BÜYÜK ÖLÇEKLİ PROJE: Kurumsal Kurs Yönetim & Restful API Sistemi (Core API)

Müfredat boyunca parça parça işlediğimiz JavaScript, Node.js, Express, Mongoose, JWT, Bcrypt, Multer ve mimari tasarım kalıplarını (MVC, Middleware, Repository Pattern) bir araya getirerek, prodüksiyon (canlı yayın) ortamına hazır, ölçeklenebilir ve güvenli bir Kurumsal Kurs Yönetim Sistemi (LMS) API projesi inşa ediyoruz.

Projemiz klasör hiyerarşisinden başlayarak, en ince güvenlik önlemlerine, gelişmiş sorgu mimarilerine ve ilişkisel NoSQL şemalarına kadar gerçek bir kurumsal mimariyle kodlanmıştır.


📂 Proje Klasör Yapısı (Enterprise Folder Structure)

kurs-yonetim-sistemi/

├── config/

│   └── veritabani.js         # MongoDB Atlas / Mongoose bağlantı ayarı

├── middlewares/

│   ├── authMiddleware.js     # JWT ve Rol doğrulama ara katmanı

│   ├── hataMiddleware.js     # Global merkezi hata yakalama (Centralized Error Handler)

│   └── yuklemeMiddleware.js  # Multer ile profil/kurs görsel yükleme kuralları

├── models/

│   ├── Kullanici.js          # Kullanıcı, Şifreleme ve Rol Şeması

│   └── Kurs.js               # Kurs, Eğitmen ve İlişkisel Veri Şeması

├── controllers/

│   ├── authController.js     # Kayıt, Giriş ve Token üretimi yönetimi

│   └── kursController.js     # Gelişmiş CRUD, Filtreleme, Arama ve Sayfalama

├── routes/

│   ├── authRotalari.js       # Kimlik doğrulama uç noktaları

│   └── kursRotalari.js       # Kurs yönetim uç noktaları

├── app.js                    # Uygulama başlatma, Express konfigürasyonu ve ana hat

└── package.json              # Proje bağımlılıkları ve scriptleri


1. ⚙️ Yapılandırma Katmanı (config/)

📄 veritabani.js

Veritabanı bağlantı süreçlerini uygulamadan izole ederek güvenli bağlantı ve kopma durumlarında otomatik yeniden bağlanma stratejilerini kurguluyoruz.

const mongoose = require('mongoose');

 

const veritabaninaBaglan = async () => {

    try {

        const baglantiCumlesi = process.env.MONGO_URI || 'mongodb://localhost:27017/kurumsalLMS';

       

        await mongoose.connect(baglantiCumlesi, {

            autoIndex: true, // Üretim ortamında performans için false çekilebilir

        });

       

        console.log('⚡ [Veritabanı] MongoDB bağlantısı başarıyla kuruldu.');

    } catch (hata) {

        console.error('❌ [Veritabanı] Bağlantı hatası oluştu:', hata.message);

        process.exit(1); // Sunucuyu kapat

    }

};

 

module.exports = veritabaninaBaglan;


2. 🗄️ Veri Modelleme Katmanı (models/)

📄 Kullanici.js

Kullanıcı kayıtlarında şifre güvenliğini otomatize etmek için Mongoose Pre-save hook yapısını entegre ediyoruz. Şifre veritabanına yazılmadan hemen önce otomatik olarak bcrypt ile hashlenir.

const mongoose = require('mongoose');

const bcrypt = require('bcrypt');

const jwt = require('jsonwebtoken');

 

const KullaniciSchema = new mongoose.Schema({

    adSoyad: { type: String, required: [true, 'Ad soyad zorunludur'], trim: true },

    email: {

        type: String,

        required: [true, 'E-posta zorunludur'],

        unique: true,

        lowercase: true,

        match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Lütfen geçerli bir e-posta girin']

    },

    sifre: { type: String, required: [true, 'Şifre zorunludur'], minlength: [6, 'Şifre en az 6 karakter olmalıdır'], select: false },

    rol: { type: String, enum: ['ogrenci', 'egitmen', 'admin'], default: 'ogrenci' },

    profilResmi: { type: String, default: 'uploads/varsayilan.png' }

}, { timestamps: true });

 

// Pre-Save Hook: Şifre değiştirildiyse veya yeni eklendiyse otomatik şifrele

KullaniciSchema.pre('save', async function(next) {

    if (!this.isModified('sifre')) return next();

   

    try {

        const salt = await bcrypt.genSalt(10);

        this.sifre = await bcrypt.hash(this.sifre, salt);

        next();

    } catch (hata) {

        next(hata);

    }

});

 

// Instance Method: Şifre karşılaştırma mekanizması

KullaniciSchema.methods.sifreElesiyorMu = async function(girilenSifre) {

    return await bcrypt.compare(girilenSifre, this.sifre);

};

 

// Instance Method: Kullanıcıya özel JWT Token üretme

KullaniciSchema.methods.tokenUret = function() {

    return jwt.sign(

        { id: this._id, rol: this.rol },

        process.env.JWT_SECRET || 'kurumsal_gizli_anahtar_55',

        { expiresIn: process.env.JWT_EXPIRES_IN || '1d' }

    );

};

 

module.exports = mongoose.model('Kullanici', KullaniciSchema);

📄 Kurs.js

İlişkisel NoSQL mantığını kurgulamak için egitmen alanını Kullanici modeline bağlıyoruz (ref). slug yapısı ile SEO uyumlu URL'lerin önünü açıyoruz.

const mongoose = require('mongoose');

 

const KursSchema = new mongoose.Schema({

    baslik: { type: String, required: [true, 'Kurs başlığı zorunludur'], trim: true, unique: true },

    slug: { type: String, lowercase: true },

    aciklama: { type: String, required: [true, 'Kurs açıklaması zorunludur'] },

    fiyat: { type: Number, required: [true, 'Kurs fiyatı zorunludur'], min: [0, 'Fiyat negatif olamaz'] },

    kategori: { type: String, required: [true, 'Kategori seçimi zorunludur'], enum: ['Yazılım', 'Tasarım', 'Pazarlama'] },

    gorsel: { type: String, default: 'uploads/kurs-varsayilan.png' },

    egitmen: {

        type: mongoose.Schema.Types.ObjectId,

        ref: 'Kullanici',

        required: [true, 'Bir kursun mutlaka eğitmeni olmalıdır']

    },

    aktif: { type: Boolean, default: true }

}, { timestamps: true });

 

// Pre-Save Hook: Kurs başlığından otomatik URL dostu slug üretme

KursSchema.pre('save', function(next) {

    this.slug = this.baslik

        .toLowerCase()

        .replace(/[^a-z0-9 -]/g, '')

        .replace(/\s+/g, '-')

        .replace(/-+/g, '-');

    next();

});

 

module.exports = mongoose.model('Kurs', KursSchema);


3. 🛡️ Ara Katman (Middleware) Yapıları (middlewares/)

📄 authMiddleware.js

Token doğrulama adımıyla kalmayıp, rol tabanlı erişim kontrolünü esnek hale getiren bir yapı kurguluyoruz.

const jwt = require('jsonwebtoken');

const Kullanici = require('../models/Kullanici');

 

// 1. Token ve Oturum Koruma Middleware

const koru = async (req, res, next) => {

    let token;

   

    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {

        token = req.headers.authorization.split(' ')[1];

    }

   

    if (!token) {

        return res.status(401).json({ basari: false, hata: 'Bu uç noktaya erişim yetkiniz yok, token bulunamadı.' });

    }

   

    try {

        const cozulenToken = jwt.verify(token, process.env.JWT_SECRET || 'kurumsal_gizli_anahtar_55');

       

        // Tokenın ait olduğu kullanıcıyı bul ve şifresiz şekilde isteğe ekle

        req.kullanici = await Kullanici.findById(cozulenToken.id);

        if (!req.kullanici) {

            return res.status(401).json({ basari: false, hata: 'Tokenın ait olduğu kullanıcı artık mevcut değil.' });

        }

       

        next();

    } catch (hata) {

        return res.status(401).json({ basari: false, hata: 'Geçersiz veya süresi dolmuş token.' });

    }

};

 

// 2. Rol Yetkilendirme Filtresi (Rol Listesi Kabul Eder)

const rolYetkiVer = (...roller) => {

    return (req, res, next) => {

        if (!roller.includes(req.kullanici.rol)) {

            return res.status(403).json({

                basari: false,

                hata: `Hesap rolünüz (${req.kullanici.rol}) bu işlemi gerçekleştirmek için yetersiz.`

            });

        }

        next();

    };

};

 

module.exports = { koru, rolYetkiVer };

📄 yuklemeMiddleware.js

Multer altyapısını dosya tipi doğrulamaları ve boyut limitleriyle güvenlik zırhına alıyoruz.

const multer = require('multer');

const path = require('path');

 

const depolamaSecenekleri = multer.diskStorage({

    destination: (req, file, cb) => {

        cb(null, './uploads/');

    },

    filename: (req, file, cb) => {

        const benzersizIsim = `${Date.now()}-${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`;

        cb(null, benzersizIsim);

    }

});

 

const dosyaFiltrele = (req, file, cb) => {

    const kabulEdilenTipler = /jpeg|jpg|png|webp/;

    const mimeTipiDogru = kabulEdilenTipler.test(file.mimetype);

    const uzantiDogru = kabulEdilenTipler.test(path.extname(file.originalname).toLowerCase());

   

    if (mimeTipiDogru && uzantiDogru) {

        return cb(null, true);

    }

    cb(new Error('Yalnızca görsel dosyaları (jpeg, jpg, png, webp) yüklenebilir.'));

};

 

const yukle = multer({

    storage: depolamaSecenekleri,

    fileFilter: dosyaFiltrele,

    limits: { fileSize: 1024 * 1024 * 3 } // Maksimum 3 Megabayt

});

 

module.exports = yukle;

📄 hataMiddleware.js

Uygulama genelinde fırlatılan tüm hataları (MongoDB hata kodları dahil) yakalayıp istemciye düzgün formatta hata mesajı dönen merkezi kriz yönetim merkezi.

const merkeziHataYakalayici = (hata, req, res, next) => {

    let error = { ...hata };

    error.message = hata.message;

   

    console.error('🔥 [Hata Detayı]:', hata);

 

    // MongoDB Kötü Biçimlendirilmiş ID Hatası (CastError)

    if (hata.name === 'CastError') {

        error.message = `Belirtilen ID formatı geçersiz: ${hata.value}`;

        error.statusCode = 400;

    }

 

    // MongoDB Mükerrer Kayıt Hatası (Duplicate Key)

    if (hata.code === 11000) {

        error.message = 'Girdiğiniz veri sistemde zaten mevcut, benzersiz olmalıdır.';

        error.statusCode = 400;

    }

 

    // Mongoose Şema Validasyon Hatası (ValidationError)

    if (hata.name === 'ValidationError') {

        error.message = Object.values(hata.errors).map(val => val.message).join(', ');

        error.statusCode = 400;

    }

 

    res.status(error.statusCode || 500).json({

        basari: false,

        hata: error.message || 'Sunucu iç hatası oluştu.'

    });

};

 

module.exports = merkeziHataYakalayici;


4. 🎛️ Denetleyici Katmanı (controllers/)

📄 authController.js

Kullanıcı giriş-çıkış, kayıt ve profil resmi entegrasyon süreçlerinin iş mantığı.

const Kullanici = require('../models/Kullanici');

 

// @desc    Yeni Kullanıcı Kaydı

// @route   POST /api/v1/auth/kayit

exports.kayitOl = async (req, res, next) => {

    try {

        const { adSoyad, email, sifre, rol } = req.body;

       

        const kullanici = await Kullanici.create({

            adSoyad,

            email,

            sifre,

            rol,

            profilResmi: req.file ? req.file.path : undefined

        });

 

        const token = kullanici.tokenUret();

        res.status(201).json({ basari: true, token, veri: { id: kullanici._id, adSoyad, email, rol } });

    } catch (hata) {

        next(hata);

    }

};

 

// @desc    Kullanıcı Girişi (Login)

// @route   POST /api/v1/auth/giris

exports.girisYap = async (req, res, next) => {

    try {

        const { email, sifre } = req.body;

 

        if (!email || !sifre) {

            return res.status(400).json({ basari: false, hata: 'Lütfen e-posta ve şifre giriniz.' });

        }

 

        // Şema ayarlarında şifreyi gizlediğimiz için burada açıkça talep ediyoruz (.select)

        const kullanici = await Kullanici.findOne({ email }).select('+sifre');

        if (!kullanici) {

            return res.status(401).json({ basari: false, hata: 'Geçersiz kimlik bilgileri.' });

        }

 

        const sifreDogruMi = await kullanici.sifreElesiyorMu(sifre);

        if (!sifreDogruMi) {

            return res.status(401).json({ basari: false, hata: 'Geçersiz kimlik bilgileri.' });

        }

 

        const token = kullanici.tokenUret();

        res.status(200).json({ basari: true, token, mesaj: 'Giriş başarılı.' });

    } catch (hata) {

        next(hata);

    }

};

📄 kursController.js

Burada sadece basit bir CRUD yazmıyoruz; gerçek üretim ortamlarının ihtiyacı olan Regex tabanlı metin arama, Kategori filtreleme, Fiyat aralığı sorgulama ve Sayfalama (Pagination) mekanizmalarını tek bir API ucunda birleştiriyoruz.

const Kurs = require('../models/Kurs');

 

// @desc    Tüm Kursları Listele (Arama, Filtreleme ve Sayfalama Entegreli)

// @route   GET /api/v1/kurslar

exports.kurslariListele = async (req, res, next) => {

    try {

        let sorgu;

        const kopyasorgu = { ...req.query };

 

        // Sayfalama ve sıralama parametrelerini ayıkla

        const cikarilacakAlanlar = ['arama', 'sirala', 'sayfa', 'limit'];

        cikarilacakAlanlar.forEach(param => delete kopyasorgu[param]);

 

        let sorguCumlesi = JSON.stringify(kopyasorgu);

        // Fiyat aralığı filtreleri için dönüştürme (Örn: fiyat[gt]=100 -> $gt: 100)

        sorguCumlesi = sorguCumlesi.replace(/\b(gt|gte|lt|lte|in)\b/g, match => `$${match}`);

 

        let anaSorguNesnesi = JSON.parse(sorguCumlesi);

 

        // Regex ile Kurs Başlığında Arama Yapma Özelliği

        if (req.query.arama) {

            anaSorguNesnesi.baslik = { $regex: req.query.arama, $options: 'i' };

        }

 

        sorgu = Kurs.find(anaSorguNesnesi).populate({

            path: 'egitmen',

            select: 'adSoyad email'

        });

 

        // Sıralama Mantığı (Sort)

        if (req.query.sirala) {

            const siralanacakKriter = req.query.sirala.split(',').join(' ');

            sorgu = sorgu.sort(siralanacakKriter);

        } else {

            sorgu = sorgu.sort('-createdAt'); // Varsayılan en yeni kurslar

        }

 

        // Sayfalama (Pagination) Algoritması

        const sayfa = parseInt(req.query.sayfa, 10) || 1;

        const limit = parseInt(req.query.limit, 10) || 5;

        const atlanacakVeri = (sayfa - 1) * limit;

        const toplamBelgeSayisi = await Kurs.countDocuments(anaSorguNesnesi);

 

        sorgu = sorgu.skip(atlanacakVeri).limit(limit);

 

        const kurslar = await sorgu;

 

        res.status(200).json({

            basari: true,

            sayim: kurslar.length,

            toplamKayıt: toplamBelgeSayisi,

            mevcutSayfa: sayfa,

            toplamSayfa: Math.ceil(toplamBelgeSayisi / limit),

            veri: kurslar

        });

    } catch (hata) {

        next(hata);

    }

};

 

// @desc    Yeni Kurs Oluştur (Sadece Eğitmen ve Admin)

// @route   POST /api/v1/kurslar

exports.kursOlustur = async (req, res, next) => {

    try {

        // Kursu oluşturan eğitmenin id bilgisini authMiddleware'den yakalıyoruz

        req.body.egitmen = req.kullanici.id;

 

        if (req.file) {

            req.body.gorsel = req.file.path;

        }

 

        const yeniKurs = await Kurs.create(req.body);

        res.status(201).json({ basari: true, veri: yeniKurs });

    } catch (hata) {

        next(hata);

    }

};


5. 🗺️ Yönlendirme Katmanı (routes/)

📄 authRotalari.js

const express = require('express');

const { kayitOl, girisYap } = require('../controllers/authController');

const yukle = require('../middlewares/yuklemeMiddleware');

 

const router = express.Router();

 

router.post('/kayit', yukle.single('profilResmi'), kayitOl);

router.post('/giris', girisYap);

 

module.exports = router;

📄 kursRotalari.js

const express = require('express');

const { kurslariListele, kursOlustur } = require('../controllers/kursController');

const { koru, rolYetkiVer } = require('../middlewares/authMiddleware');

const yukle = require('../middlewares/yuklemeMiddleware');

 

const router = express.Router();

 

// Herkes kursları listeleyebilir

router.get('/', kurslariListele);

 

// Sadece oturum açmış Eğitmen ve Admin yetkisine sahip kullanıcılar yeni kurs ekleyebilir

router.post('/', koru, rolYetkiVer('egitmen', 'admin'), yukle.single('kursGorseli'), kursOlustur);

 

module.exports = router;


6. 🚀 Ana Giriş Noktası (app.js)

Tüm mimariyi ayağa kaldıran, güvenlik paketlerini (helmet, cors) ve sunucu portunu dinleyen merkezi dosya.

const express = require('express');

const path = require('path');

const veritabaninaBaglan = async () => {

    const mongoose = require('mongoose');

    await mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/kurumsalLMS');

};

const authRotalari = require('./routes/authRotalari');

const kursRotalari = require('./routes/kursRotalari');

const merkeziHataYakalayici = require('./middlewares/hataMiddleware');

 

const app = express();

 

// Gövde Ayrıştırıcı Middleware'ler

app.use(express.json());

app.use(express.urlencoded({ extended: true }));

 

// Statik Dosya Paylaşımı (Yüklenen profil ve kurs resimlerine dışarıdan erişim için)

app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

 

// Veritabanı Bağlantısını Tetikle

veritabaninaBaglan();

 

// API Rotalarının Bağlanması (Versiyonlama Standartlarında)

app.use('/api/v1/auth', authRotalari);

app.use('/api/v1/kurslar', kursRotalari);

 

// Merkezi Hata Yakalama Sistemi Entegrasyonu (Tüm rotaların altında olmalıdır!)

app.use(merkeziHataYakalayici);

 

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {

    console.log(`🚀 Sunucu canavar gibi ${PORT} portu üzerinde çalışıyor...`);

});


💡 Bu Gelişmiş API Nasıl Test Edilir? (Postman / Rest Client)

  1. Kayıt Olma (POST /api/v1/auth/kayit): form-data formatında adSoyad, email, sifre, rol (egitmen) ve bir adet dosya gönderin.
  2. Giriş Yapma (POST /api/v1/auth/giris): Bilgilerinizle giriş yapıp size dönen token değerini kopyalayın.
  3. Kurs Ekleme (POST /api/v1/kurslar): Postman'de Header kısmına Authorization: Bearer <KOPYALANAN_TOKEN> ekleyerek baslik, aciklama, fiyat ve kategori verileriyle istek atın.
  4. Gelişmiş Arama Filtreleme (GET /api/v1/kurslar?arama=Node&fiyat[gte]=100&sirala=-fiyat&sayfa=1&limit=2): Bu istek başlığında "Node" geçen, fiyatı 100 TL'den büyük olan kursları fiyata göre azalan sırada sayfalar halinde getirecektir.

 

🖥️ PROJENİN ÖN YÜZ KATMANI (Frontend Layer)

Bir önceki adımda hazırladığımız kurumsal LMS RESTful API altyapısına bağlanacak; Axios ile asenkron veri çeken, EJS (Template Engine) mimarisine uygun dinamik arayüz şablonlarını yazıyoruz. Hemen ardından bu arayüzü modern, ölçeklenebilir ve kurumsal standartlara ulaştırmak için Sass/Scss mimarisi ve BEM (Block Element Modifier) metodolojisi ile sıfırdan giydiriyoruz.


🏗️ 1. KATMAN: EJS Şablonları & Axios Entegrasyonu

Sayfaların tekrar eden alanlarını bölerek yönetmek amacıyla Partials mimarisini kullanıyoruz. Projenin ön yüz giriş noktası ve alt şablonları şu şekildedir:

📄 views/partials/header.ejs

<!DOCTYPE html>

<html lang="tr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Kurumsal LMS - Kurs Yönetim Sistemi</title>

    <!-- Derlenmiş CSS dosyamız -->

    <link rel="stylesheet" href="/css/main.css">

    <!-- Axios Kütüphanesi -->

    <script src="https://jsdelivr.net"></script>

</head>

<body>

    <header class="lms-header">

        <div class="lms-header__container">

            <div class="lms-header__logo">LMS<span>Akademi</span></div>

            <nav class="lms-header__nav">

                <ul class="lms-header__menu">

                    <li class="lms-header__item"><a href="/" class="lms-header__link lms-header__link--aktif">Kurslar</a></li>

                    <li class="lms-header__item"><a href="/kurs-ekle" class="lms-header__link">Kurs Ekle</a></li>

                </ul>

            </nav>

        </div>

    </header>

📄 views/kurslar.ejs (Dinamik Kurs Listeleme ve Arama Sayfası)

Bu sayfada HTML yüklendikten sonra Axios devreye girer, API'den verileri asenkron olarak çeker, filtreleme ve arama kriterlerine göre DOM'u dinamik olarak günceller.

<%- include('partials/header') %>

 

<main class="lms-main">

    <!-- Filtreleme ve Arama Bloğu (BEM: lms-filtre) -->

    <section class="lms-filtre">

        <div class="lms-filtre__arama-grubu">

            <input type="text" id="aramaInput" class="lms-filtre__input" placeholder="Kurs adı ile ara...">

            <button id="araButon" class="lms-filtre__buton lms-filtre__buton--ara">Ara</button>

        </div>

        <div class="lms-filtre__secenekler">

            <select id="kategoriFiltre" class="lms-filtre__seçim">

                <option value="">Tüm Kategoriler</option>

                <option value="Yazılım">Yazılım</option>

                <option value="Tasarım">Tasarım</option>

                <option value="Pazarlama">Pazarlama</option>

            </select>

            <select id="siralaFiltre" class="lms-filtre__seçim">

                <option value="-createdAt">En Yeniler</option>

                <option value="fiyat">Fiyat (Artan)</option>

                <option value="-fiyat">Fiyat (Azalan)</option>

            </select>

        </div>

    </section>

 

    <!-- Kurs Kartları Yükleme Alanı (BEM: lms-izgara) -->

    <section id="kurslarKonteyner" class="lms-izgara">

        <!-- Axios verileri buraya enjekte edecek, yükleniyor loader animasyonu başlangıçta görünür -->

        <div class="lms-yukleyici"></div>

    </section>

</main>

 

<script>

    // Asenkron Veri Çekme ve DOM Simülasyonu Fonksiyonu

    async function kurslariGetir() {

        const konteyner = document.getElementById('kurslarKonteyner');

       

        // Dinamik sorgu parametrelerini toplama

        const arama = document.getElementById('aramaInput').value;

        const kategori = document.getElementById('kategoriFiltre').value;

        const sirala = document.getElementById('siralaFiltre').value;

 

        try {

            konteyner.innerHTML = '<div class="lms-yukleyici"></div>'; // Loader göster

 

            // Yazdığımız Node.js Core API ucuna Axios ile istek atma

            const cevap = await axios.get('/api/v1/kurslar', {

                params: { arama, kategori, sirala }

            });

 

            const kurslar = cevap.data.veri;

            konteyner.innerHTML = ''; // Temizle

 

            if (kurslar.length === 0) {

                konteyner.innerHTML = '<p class="lms-izgara__bos">Aradığınız kriterlere uygun kurs bulunamadı.</p>';

                return;

            }

 

            // Gelen verileri BEM standartlarındaki HTML şablonuna haritalama

            kurslar.forEach(kurs => {

                const kartHtml = `

                    <article class="kurs-karti">

                        <div class="kurs-karti__gorsel-alani">

                            <img src="/${kurs.gorsel}" alt="${kurs.baslik}" class="kurs-karti__resim">

                            <span class="kurs-karti__etiket">${kurs.kategori}</span>

                        </div>

                        <div class="kurs-karti__icerik">

                            <h3 class="kurs-karti__baslik">${kurs.baslik}</h3>

                            <p class="kurs-karti__aciklama">${kurs.aciklama}</p>

                            <div class="kurs-karti__alt-bilgi">

                                <span class="kurs-karti__egitmen">👨‍🏫 ${kurs.egitmen.adSoyad}</span>

                                <span class="kurs-karti__fiyat kurs-karti__fiyat--vurgulu">${kurs.fiyat} TL</span>

                            </div>

                        </div>

                    </article>

                `;

                konteyner.insertAdjacentHTML('beforeend', kartHtml);

            });

 

        } catch (hata) {

            konteyner.innerHTML = `<p class="lms-izgara__hata">Veriler alınırken hata oluştu: ${hata.message}</p>`;

        }

    }

 

    // Olay Dinleyicileri (Event Listeners) Bağlama

    document.getElementById('araButon').addEventListener('click', kurslariGetir);

    document.getElementById('kategoriFiltre').addEventListener('change', kurslariGetir);

    document.getElementById('siralaFiltre').addEventListener('change', kurslariGetir);

 

    // Sayfa ilk açıldığında otomatik yükle

    document.addEventListener('DOMContentLoaded', kurslariGetir);

</script>

 

</body>

</html>


🎨 2. KATMAN: Sass/Scss & BEM Modern CSS Kütüphanesi Tasarımı

Kütüphane kodlarının spagetti CSS'e dönüşmemesi için kurumsal Sass mimarisi (7-1 Pattern basitleştirilmiş sürümü) ile dosyaları parçalara ayırarak kurguluyoruz.

📁 CSS Kütüphanesi Dosya Dağılımı


📄 scss/soyutlar/_degiskenler.scss

// Renk Paleti (Kurumsal Tasarım)

$renk-birincil: #4f46e5;      // İndigo modern mor tonu

$renk-birincil-koyu: #4338ca; // Hover durumları için

$renk-ikincil: #0f172a;      // Koyu lacivert / Gece mavisi (Yazılar için)

$renk-metin: #475569;         // Gri tonu (Açıklamalar için)

$renk-arkaplan: #f8fafc;      // Yumuşak açık gri zemin

$renk-beyaz: #ffffff;

$renk-etiket: #10b981;        // Canlı yeşil (Kategoriler için)

 

// Tipografi ve Gölgeler

$font-ana: 'Inter', system-ui, sans-serif;

$golge-hafif: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);

$golge-derin: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);

 

// Sınırlar

$kenar-yariçap: 12px;

$gecis-hizi: 0.3s ease;

📄 scss/soyutlar/_mixinler.scss

// Esnek Kutu Mizanpaj Kısayolu

@mixin esnek-kutu($yon: row, $dağılım: flex-start, $hizalama: stretch) {

    display: flex;

    flex-direction: $yon;

    justify-content: $dağılım;

    align-items: $hizalama;

}

 

// Medya Sorgusu (Responsive) Kısayolu - Mobil Öncelikli Tasarım İçin

@mixin tablet {

    @media (min-width: 768px) { @content; }

}

 

@mixin masaustu {

    @media (min-width: 1024px) { @content; }

}

📄 scss/temel/_sifirlama.scss

* {

    margin: 0;

    padding: 0;

    box-sizing: border-box;

}

 

body {

    font-family: $font-ana;

    background-color: $renk-arkaplan;

    color: $renk-ikincil;

    line-height: 1.6;

}

 

a {

    text-decoration: none;

    color: inherit;

}

📄 scss/bileşenler/_header.scss

.lms-header {

    background-color: $renk-beyaz;

    border-bottom: 1px solid #e2e8f0;

    position: sticky;

    top: 0;

    z-index: 100;

    box-shadow: $golge-hafif;

 

    &__container {

        @include esnek-kutu(row, space-between, center);

        max-width: 1200px;

        margin: 0 auto;

        padding: 15px 20px;

    }

 

    &__logo {

        font-size: 24px;

        font-weight: 800;

        color: $renk-ikincil;

 

        span {

            color: $renk-birincil;

        }

    }

 

    &__menu {

        @include esnek-kutu(row, flex-end, center);

        list-style: none;

        gap: 25px;

    }

 

    &__link {

        font-weight: 600;

        color: $renk-metin;

        transition: color $gecis-hizi;

 

        &:hover {

            color: $renk-birincil;

        }

 

        &--aktif {

            color: $renk-birincil;

            border-bottom: 2px solid $renk-birincil;

            padding-bottom: 4px;

        }

    }

}

📄 scss/bileşenler/_filtre.scss

.lms-filtre {

    max-width: 1200px;

    margin: 40px auto 20px auto;

    padding: 0 20px;

    @include esnek-kutu(column, space-between, stretch);

    gap: 15px;

 

    @include tablet {

        flex-direction: row; // Mobilde alt alta, tablette yan yana geçiş

    }

 

    &__arama-grubu {

        @include esnek-kutu(row, flex-start, center);

        flex: 1;

        gap: 10px;

    }

 

    &__input {

        width: 100%;

        padding: 12px 16px;

        border: 1px solid #cbd5e1;

        border-radius: $kenar-yariçap;

        font-size: 14px;

        outline: none;

        transition: border-color $gecis-hizi;

 

        &:focus {

            border-color: $renk-birincil;

        }

    }

 

    &__seçim {

        padding: 12px 16px;

        border: 1px solid #cbd5e1;

        border-radius: $kenar-yariçap;

        background-color: $renk-beyaz;

        color: $renk-ikincil;

        font-weight: 500;

        outline: none;

        cursor: pointer;

    }

 

    &__secenekler {

        @include esnek-kutu(row, flex-start, center);

        gap: 10px;

    }

 

    &__buton {

        padding: 12px 24px;

        border: none;

        border-radius: $kenar-yariçap;

        font-weight: 600;

        cursor: pointer;

        transition: background-color $gecis-hizi, transform $gecis-hizi;

 

        &--ara {

            background-color: $renk-birincil;

            color: $renk-white;

 

            &:hover {

                background-color: $renk-birincil-koyu;

                transform: translateY(-1px);

            }

        }

    }

}

📄 scss/bileşenler/_kartlar.scss

// Grid Yapısı ve Genel Düzenleyiciler

.lms-main {

    max-width: 1200px;

    margin: 0 auto;

    padding: 20px;

}

 

.lms-izgara {

    display: grid;

    grid-template-columns: 1fr;

    gap: 30px;

    margin-top: 20px;

 

    @include tablet {

        grid-template-columns: repeat(2, 1fr); // Tablette 2 kolon

    }

 

    @include masaustu {

        grid-template-columns: repeat(3, 1fr); // Masaüstünde 3 kolon

    }

}

 

// BEM Elemanı: Kurs Kartı Bileşeni

.kurs-karti {

    background-color: $renk-beyaz;

    border-radius: $kenar-yariçap;

    overflow: hidden;

    box-shadow: $golge-hafif;

    transition: transform $gecis-hizi, box-shadow $gecis-hizi;

    @include esnek-kutu(column, space-between, stretch);

 

    &:hover {

        transform: translateY(-8px);

        box-shadow: $golge-derin;

    }

 

    &__gorsel-alani {

        position: relative;

        width: 100%;

        height: 200px;

        background-color: #e2e8f0;

    }

 

    &__resim {

        width: 100%;

        height: 100%;

        object-fit: cover;

    }

 

    &__etiket {

        position: absolute;

        top: 15px;

        left: 15px;

        background-color: $renk-etiket;

        color: $renk-beyaz;

        padding: 4px 12px;

        font-size: 12px;

        font-weight: 700;

        border-radius: 20px;

        text-transform: uppercase;

    }

 

    &__icerik {

        padding: 20px;

        flex: 1;

        @include esnek-kutu(column, space-between, stretch);

    }

 

    &__baslik {

        font-size: 18px;

        font-weight: 700;

        color: $renk-ikincil;

        margin-bottom: 10px;

    }

 

    &__aciklama {

        font-size: 14px;

        color: $renk-metin;

        margin-bottom: 20px;

        display: -webkit-box;

        -webkit-line-clamp: 3; // 3 satırdan sonrasını kırpıp üç nokta ekler

        -webkit-box-orient: vertical;

        overflow: hidden;

    }

 

    &__alt-bilgi {

        @include esnek-kutu(row, space-between, center);

        border-top: 1px solid #f1f5f9;

        padding-top: 15px;

        margin-top: auto;

    }

 

    &__egitmen {

        font-size: 14px;

        font-weight: 500;

        color: $renk-ikincil;

    }

 

    &__fiyat {

        font-size: 18px;

        font-weight: 800;

       

        &--vurgulu {

            color: $renk-birincil;

        }

    }

}

 

// CSS Animasyonlu Yükleniyor Döngüsü (Loader)

.lms-yukleyici {

    width: 50px;

    height: 50px;

    border: 5px solid #e2e8f0;

    border-top-color: $renk-birincil;

    border-radius: 50%;

    margin: 50px auto;

    grid-column: 1 / -1; // Grid satırını tamamen kaplasın

    animation: lms-donme 1s linear infinite;

}

 

@keyframes lms-donme {

    to { transform: rotate(360deg); }

}

📄 scss/main.scss (Orkestrasyon Dosyası)

Tüm küçük Scss parçalarını hiyerarşik kurallara göre tek bir dosyada birleştirip derlenmeye hazır hale getiriyoruz.

// 1. Soyut Yapılar ve Ayarlar

@import 'soyutlar/degiskenler';

@import 'soyutlar/mixinler';

 

// 2. Temel Kurallar ve Resetler

@import 'temel/sifirlama';

 

// 3. Bileşen (UI Component) Tasarımları

@import 'bileşenler/header';

@import 'bileşenler/filtre';

@import 'bileşenler/kartlar';


🚀 Bu Yapı Nasıl Çalışır ve Çalıştırılır?

  1. Sass Derleme: Komut satırına sass scss/main.scss public/css/main.css --watch yazarak hazırladığımız modern kütüphaneyi standart tarayıcı CSS'ine canlı olarak derleyin.
  2. Sayfa Yüklenmesi: Kullanıcı views/kurslar.ejs şablonuna girdiğinde backend sunucusu HTML iskeletini ve derlenmiş kurumsal CSS'imizi (main.css) tarayıcıya fırlatır.
  3. Asenkron Tetiklenme: Tarayıcıda sayfa hazır olduğunda (DOMContentLoaded), Axios arka planda sessizce /api/v1/kurslar adresine asenkron bir HTTP GET isteği uçurur.
  4. Dinamik Arayüz İnşası: API'den dönen JSON verileri döngüye sokulur, BEM yapısındaki .kurs-karti blokları üretilerek CSS Loader (.lms-yukleyici) kaldırılır ve sayfadaki grid yapısına saniyeler içinde akıcı bir şekilde eklenir.