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>© 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
🛠️ 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
🛠️ 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:
🛠 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.
🛠 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:
🔎 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:
🌐 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>© 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.
🔎 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)
🖥️ 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?