Skip to content

📌 Ne Zaman Kullanilir?

  • ✅ Real-time API, microservices, full-stack JS, WebSocket, CLI araçları
  • ⚠️ CPU-intensive islemler için dikkatli ol (Worker Threads kullan)
  • ❌ Basit CRUD (Laravel daha hızlı baslangic)

Önerilen Kullanım: Express/Fastify + TypeScript + PostgreSQL/MongoDB Alternatifler: Laravel (PHP), Django (Python), ASP.NET Core (C#)

Node.js Backend Mastery Guide — Practical Reference (EN + TR)

Complete English + Turkish learning guide for Node.js backend development covering core modules, Express.js, REST APIs, databases, authentication, testing, and deployment. (Node.js backend geliştirme için kapsamli rehber: core moduller, Express.js, REST API, veritabani, kimlik doğrulama, test ve deploy.)

1) Node.js Nedir?

Node.js, Chrome'un V8 JavaScript motorunu kullanan, sunucu tarafinda (server-side) JavaScript calistiran bir runtime ortamidir.

Key Concepts (Temel Kavramlar):

  • V8 Engine: Google'in C++ ile yazilmis JavaScript motoru. JS kodunu makine koduna derler (compiles to machine code).
  • Non-blocking I/O: Dosya okuma, veritabani sorgusu, ag istekleri gibi islemler ana thread'i bloklamaz. (Main thread'i mesgul etmeden isler yapar.)
  • Event Loop: Tek thread uzerinde çalışan, callback kuyruklarini yoneten mekanizma. (Single-threaded but handles thousands of concurrent connections.)
  • Event-Driven Architecture: Olaylara dayali mimari — bir işlem tamamlandiginda callback cagrilir.

Neden Node.js? (Why Node.js?)

AvantajAçıklama
Tek dil (JS everywhere)Frontend ve backend ayni dil
Hızlı I/ONon-blocking yapiyla yüksek performans
npm ekosistemi2M+ paket, dev community
Real-time uygulamalarWebSocket, chat, live dashboard
Mikroservis mimarisiHafif, hızlı startup
Laravel biliyorsanMVC pattern benzer, route/middleware konsepti ayni

Event Loop Nasil Calisir:

   ┌───────────────────────────┐
┌─>│           timers          │  (setTimeout, setInterval)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │  (I/O callbacks)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │           poll             │  (incoming connections, data)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │           check           │  (setImmediate)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │      close callbacks      │  (socket.on('close'))
│  └─────────────┬─────────────┘
└─────────────────┘
javascript
// Event loop ornegi
console.log("1 - Sync");

setTimeout(() => {
  console.log("2 - setTimeout (macro task)");
}, 0);

Promise.resolve().then(() => {
  console.log("3 - Promise (micro task)");
});

console.log("4 - Sync");
// Cikti: 1, 4, 3, 2
// Micro task'lar (Promise) macro task'lardan (setTimeout) once calisir

Summary: Node.js = V8 + libuv + non-blocking I/O. Tek thread ama event loop sayesinde binlerce bağlantı yonetir.


2) Kurulum & Ortam

nvm (Node Version Manager)

bash
# nvm kurulumu (Linux/Mac)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

# Terminal'i yeniden ac veya:
source ~/.bashrc

# Node surumlerini yonet
nvm install 22           # LTS surumu kur
nvm install 20           # Baska bir surum
nvm use 22               # Aktif surumu degistir
nvm alias default 22     # Varsayilan surum ayarla
nvm ls                   # Kurulu surumleri listele
nvm ls-remote            # Mevcut tum surumleri gor

# Proje bazli surum (.nvmrc dosyasiyla)
echo "22" > .nvmrc
nvm use                  # .nvmrc'den okur

npm vs yarn vs pnpm

Özelliknpmyarnpnpm
HizOrtaHızlıEn hızlı
Disk kullanimiFazlaFazlaAz (hard link)
Monorepoworkspacesworkspacesworkspaces (en iyi)
Lock dosyasipackage-lock.jsonyarn.lockpnpm-lock.yaml
VarsayilanNode ile gelirAyri kurulumAyri kurulum
bash
# pnpm kurulumu (onerilen)
npm install -g pnpm

# yarn kurulumu
npm install -g yarn

# Temel komut karsilastirmasi
npm install          # yarn install       # pnpm install
npm install express  # yarn add express   # pnpm add express
npm run dev          # yarn dev           # pnpm dev
npx create-app       # yarn dlx create-app # pnpm dlx create-app

Proje Baslangici

bash
# Yeni proje olustur
mkdir my-api && cd my-api
npm init -y

# veya interaktif
npm init

# TypeScript ile baslamak istersen
npm init -y
npm install -D typescript @types/node ts-node nodemon
npx tsc --init

Summary: nvm ile sürüm yonet, pnpm hiz ve disk için ideal. Projeye .nvmrc ekle, takimla uyum sagla.


3) Node.js Core Modulleri

fs (File System)

javascript
const fs = require("fs");
const fsPromises = require("fs/promises"); // Promise-based API

// --- Senkron okuma (Sync - bloklayici, kucuk isler icin) ---
const data = fs.readFileSync("config.json", "utf-8");
console.log(JSON.parse(data));

// --- Asenkron okuma (Callback) ---
fs.readFile("config.json", "utf-8", (err, data) => {
  if (err) throw err;
  console.log(data);
});

// --- Promise-based (onerilen) ---
async function readConfig() {
  try {
    const data = await fsPromises.readFile("config.json", "utf-8");
    return JSON.parse(data);
  } catch (err) {
    console.error("Dosya okunamadi:", err.message);
  }
}

// --- Dosya yazma ---
await fsPromises.writeFile("output.txt", "Merhaba Node.js!", "utf-8");

// --- Dosya ekleme (append) ---
await fsPromises.appendFile("log.txt", `[${new Date().toISOString()}] Event\n`);

// --- Klasor islemleri ---
await fsPromises.mkdir("uploads/images", { recursive: true });
const files = await fsPromises.readdir("./src");
const stats = await fsPromises.stat("app.js");
console.log(stats.isFile(), stats.size, stats.mtime);

// --- Dosya silme ---
await fsPromises.unlink("temp.txt");
await fsPromises.rm("old-folder", { recursive: true, force: true });

// --- Dosya/klasor varligini kontrol et ---
try {
  await fsPromises.access("config.json");
  console.log("Dosya mevcut");
} catch {
  console.log("Dosya bulunamadi");
}

path

javascript
const path = require("path");

path.join("/users", "fahri", "docs", "file.txt");
// => /users/fahri/docs/file.txt

path.resolve("src", "index.js");
// => /absolute/path/to/src/index.js

path.basename("/users/fahri/app.js");        // => app.js
path.basename("/users/fahri/app.js", ".js"); // => app
path.extname("photo.png");                   // => .png
path.dirname("/users/fahri/app.js");         // => /users/fahri

path.parse("/users/fahri/app.js");
// => { root: '/', dir: '/users/fahri', base: 'app.js', ext: '.js', name: 'app' }

// __dirname ve __filename (CommonJS)
console.log(__dirname);   // Bu dosyanin klasoru
console.log(__filename);  // Bu dosyanin tam yolu

// ES Modules icin:
// import { fileURLToPath } from 'url';
// const __filename = fileURLToPath(import.meta.url);
// const __dirname = path.dirname(__filename);

http

javascript
const http = require("http");

const server = http.createServer((req, res) => {
  const { method, url } = req;

  if (method === "GET" && url === "/") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ message: "Merhaba!" }));
  } else if (method === "GET" && url === "/health") {
    res.writeHead(200);
    res.end("OK");
  } else {
    res.writeHead(404);
    res.end("Not Found");
  }
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server ${PORT} portunda calisiyor`);
});

os

javascript
const os = require("os");

console.log("Platform:", os.platform());    // linux, darwin, win32
console.log("Mimari:", os.arch());          // x64, arm64
console.log("CPU Sayisi:", os.cpus().length);
console.log("Toplam RAM:", (os.totalmem() / 1024 / 1024 / 1024).toFixed(2), "GB");
console.log("Bos RAM:", (os.freemem() / 1024 / 1024 / 1024).toFixed(2), "GB");
console.log("Home:", os.homedir());
console.log("Hostname:", os.hostname());
console.log("Uptime:", (os.uptime() / 3600).toFixed(1), "saat");

events (EventEmitter)

javascript
const EventEmitter = require("events");

class OrderService extends EventEmitter {
  placeOrder(order) {
    // Siparis islemlerini yap
    console.log(`Siparis alindi: #${order.id}`);

    // Olaylari tetikle (emit)
    this.emit("order:created", order);
    this.emit("notification:send", {
      to: order.email,
      subject: `Siparis #${order.id} onaylandi`,
    });
  }
}

const orderService = new OrderService();

// Olay dinleyicileri (listeners)
orderService.on("order:created", (order) => {
  console.log(`Stok guncelleniyor: ${order.product}`);
});

orderService.on("notification:send", (notification) => {
  console.log(`Email gonderiliyor: ${notification.to}`);
});

// once: sadece bir kere dinle
orderService.once("order:created", () => {
  console.log("Ilk siparis kutlamasi!");
});

orderService.placeOrder({
  id: 1001,
  product: "Laptop",
  email: "fahri@example.com",
});

child_process

javascript
const { exec, execSync, spawn } = require("child_process");

// exec: Shell komutu calistir (kucuk ciktilar icin)
exec("ls -la", (err, stdout, stderr) => {
  if (err) {
    console.error("Hata:", err.message);
    return;
  }
  console.log(stdout);
});

// execSync: Senkron calistir
const result = execSync("node --version", { encoding: "utf-8" });
console.log("Node surumu:", result.trim());

// spawn: Buyuk ciktilar ve stream icin (onerilen)
const child = spawn("find", [".", "-name", "*.js"]);

child.stdout.on("data", (data) => {
  console.log(`stdout: ${data}`);
});

child.stderr.on("data", (data) => {
  console.error(`stderr: ${data}`);
});

child.on("close", (code) => {
  console.log(`Islem bitti, cikis kodu: ${code}`);
});

Summary: Core modulleri framework'suz bile cok sey yapabilirsin. fs/promises kullan, path.join ile platformlar-arasi yol oluştur.


4) npm & Paket Yönetimi

package.json Yapisi

json
{
  "name": "my-api",
  "version": "1.0.0",
  "description": "REST API projesi",
  "main": "src/index.js",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest --coverage",
    "test:watch": "jest --watch",
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "build": "tsc",
    "db:migrate": "prisma migrate dev",
    "db:seed": "node src/seeds/index.js"
  },
  "dependencies": {
    "express": "^4.21.0",
    "mongoose": "^8.5.0",
    "dotenv": "^16.4.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.0",
    "jest": "^29.7.0",
    "eslint": "^9.0.0"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "license": "MIT"
}

dependencies vs devDependencies

bash
# dependencies: Production'da gereken paketler
npm install express mongoose dotenv

# devDependencies: Sadece gelistirme icin
npm install -D nodemon jest eslint typescript

# Production kurulumu (devDependencies haric)
npm install --production
# veya
NODE_ENV=production npm install

Semantic Versioning (SemVer)

MAJOR.MINOR.PATCH
  ^      ^     ^
  |      |     └── Bug fix (hata duzeltme)
  |      └──────── Yeni ozellik (backward-compatible)
  └─────────────── Breaking change (kirilma degisikligi)

^4.21.0  →  >=4.21.0 <5.0.0   (minor ve patch guncellenebilir)
~4.21.0  →  >=4.21.0 <4.22.0  (sadece patch guncellenebilir)
4.21.0   →  tam bu surum (sabitlenmis)

Faydali npm Komutları

bash
# Paket yonetimi
npm install                  # Tum bagimliliklar kur
npm install express          # Paket ekle
npm install -D nodemon       # Dev dependency olarak ekle
npm uninstall express        # Paketi kaldir
npm update                   # Tum paketleri guncelle

# Bilgi ve kontrol
npm list --depth=0           # Kurulu paketleri gor
npm outdated                 # Guncellenebilir paketler
npm audit                    # Guvenlik taramasi
npm audit fix                # Guvenlik sorunlarini duzelt

# npx: Paket kurmadan calistir
npx create-react-app my-app
npx prisma migrate dev
npx eslint --init

# Script calistirma
npm run dev                  # package.json scripts
npm test                     # kisayol: npm run test
npm start                    # kisayol: npm run start

# Global paketler
npm install -g pm2 nodemon
npm list -g --depth=0

.npmrc Dosyasi

bash
# Proje kokune .npmrc ekle
save-exact=true        # Tam surum numaralariyla kaydet
engine-strict=true     # engines uyumsuzlugunda hata ver

Summary: package.json projenin kalbi. SemVer'i anla, npm audit ile güvenlik tara, scripts ile is akisini otomatiklestir.


5) Express.js

Kurulum ve Temel Yapi

bash
npm install express
npm install -D nodemon
javascript
// src/index.js
const express = require("express");
const app = express();

// --- Built-in Middleware ---
app.use(express.json());                         // JSON body parsing
app.use(express.urlencoded({ extended: true }));  // Form data parsing
app.use(express.static("public"));                // Statik dosyalar

// --- Basit Route ---
app.get("/", (req, res) => {
  res.json({ message: "API calisiyor", status: "ok" });
});

// --- Server baslat ---
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server http://localhost:${PORT} adresinde calisiyor`);
});

Routing

javascript
// --- Route parametreleri ---
app.get("/users/:id", (req, res) => {
  const { id } = req.params;
  res.json({ userId: id });
});

// --- Query parametreleri ---
// GET /search?q=node&page=2
app.get("/search", (req, res) => {
  const { q, page = 1, limit = 10 } = req.query;
  res.json({ query: q, page: Number(page), limit: Number(limit) });
});

// --- Route gruplama (Router) ---
const userRouter = express.Router();

userRouter.get("/", listUsers);
userRouter.get("/:id", getUser);
userRouter.post("/", createUser);
userRouter.put("/:id", updateUser);
userRouter.delete("/:id", deleteUser);

app.use("/api/users", userRouter);

// --- Route zincirleme ---
app.route("/api/posts")
  .get(listPosts)
  .post(createPost);

app.route("/api/posts/:id")
  .get(getPost)
  .put(updatePost)
  .delete(deletePost);

Middleware

javascript
// --- Custom middleware: Loglama ---
function requestLogger(req, res, next) {
  const start = Date.now();

  res.on("finish", () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`);
  });

  next(); // Sonraki middleware'e gec (ONEMLI!)
}
app.use(requestLogger);

// --- Auth middleware ---
function authenticate(req, res, next) {
  const token = req.headers.authorization?.split(" ")[1];

  if (!token) {
    return res.status(401).json({ error: "Token gerekli" });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: "Gecersiz token" });
  }
}

// Belirli route'lara middleware uygula
app.get("/api/profile", authenticate, (req, res) => {
  res.json({ user: req.user });
});

// Route grubuna uygula
const protectedRouter = express.Router();
protectedRouter.use(authenticate);
protectedRouter.get("/dashboard", getDashboard);
protectedRouter.get("/settings", getSettings);
app.use("/api", protectedRouter);

// --- Role-based middleware ---
function authorize(...roles) {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: "Yetkiniz yok (Forbidden)" });
    }
    next();
  };
}

app.delete("/api/users/:id", authenticate, authorize("admin"), deleteUser);

Request & Response

javascript
app.post("/api/users", (req, res) => {
  // --- Request ---
  console.log(req.body);          // POST body (JSON)
  console.log(req.params);        // URL parametreleri
  console.log(req.query);         // Query string
  console.log(req.headers);       // Basliklar
  console.log(req.cookies);       // Cookie'ler (cookie-parser gerekir)
  console.log(req.ip);            // Client IP
  console.log(req.method);        // GET, POST, PUT, DELETE
  console.log(req.path);          // URL path

  // --- Response ---
  res.status(201).json({ id: 1, name: "Fahri" });  // JSON yanit
  res.send("Plain text");                            // Text yanit
  res.sendFile("/path/to/file.pdf");                 // Dosya gonder
  res.download("/path/to/report.xlsx");              // Dosya indirt
  res.redirect("/api/users");                        // Yonlendir
  res.status(204).end();                             // Bos yanit (No Content)
  res.cookie("token", "abc123", { httpOnly: true }); // Cookie set et
});

Error Handling Middleware

javascript
// --- 404 handler (tum route'lardan sonra) ---
app.use((req, res) => {
  res.status(404).json({
    error: "Not Found",
    message: `${req.method} ${req.path} bulunamadi`,
  });
});

// --- Global error handler (4 parametre SART) ---
app.use((err, req, res, next) => {
  console.error(err.stack);

  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({
    error: err.message || "Internal Server Error",
    ...(process.env.NODE_ENV === "development" && { stack: err.stack }),
  });
});

Summary: Express = route + middleware + req/res. Middleware sirasi önemli! next() unutma. Error handler en sona.


6) REST API Geliştirme

Proje Yapisi (Klasor Organizasyonu)

my-api/
  src/
    index.js            # Entry point
    app.js              # Express app config
    config/
      database.js       # DB baglantisi
      index.js          # Tum config
    routes/
      index.js          # Route birlestirici
      userRoutes.js
      postRoutes.js
    controllers/
      userController.js
      postController.js
    models/
      User.js
      Post.js
    middleware/
      auth.js
      validate.js
      errorHandler.js
    utils/
      ApiError.js
      asyncHandler.js
    validators/
      userValidator.js
  tests/
    user.test.js
  .env
  .env.example
  package.json

app.js

javascript
const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
const morgan = require("morgan");
const routes = require("./routes");
const errorHandler = require("./middleware/errorHandler");

const app = express();

// --- Global Middleware ---
app.use(helmet());                  // Guvenlik basliklari
app.use(cors());                    // Cross-Origin izinleri
app.use(morgan("dev"));            // HTTP request loglama
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true }));

// --- Routes ---
app.use("/api", routes);

// --- Health Check ---
app.get("/health", (req, res) => {
  res.json({ status: "ok", timestamp: new Date().toISOString() });
});

// --- Error Handler ---
app.use(errorHandler);

module.exports = app;

Controller Pattern

javascript
// src/controllers/userController.js
const User = require("../models/User");
const ApiError = require("../utils/ApiError");
const asyncHandler = require("../utils/asyncHandler");

// Tum kullanicilari getir
exports.getUsers = asyncHandler(async (req, res) => {
  const { page = 1, limit = 10, sort = "-createdAt" } = req.query;
  const skip = (page - 1) * limit;

  const [users, total] = await Promise.all([
    User.find().sort(sort).skip(skip).limit(Number(limit)),
    User.countDocuments(),
  ]);

  res.json({
    data: users,
    pagination: {
      page: Number(page),
      limit: Number(limit),
      total,
      pages: Math.ceil(total / limit),
    },
  });
});

// Tek kullanici getir
exports.getUser = asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);

  if (!user) {
    throw new ApiError(404, "Kullanici bulunamadi");
  }

  res.json({ data: user });
});

// Kullanici olustur
exports.createUser = asyncHandler(async (req, res) => {
  const user = await User.create(req.body);
  res.status(201).json({ data: user });
});

// Kullanici guncelle
exports.updateUser = asyncHandler(async (req, res) => {
  const user = await User.findByIdAndUpdate(req.params.id, req.body, {
    new: true,        // Guncellenmis dokumani dondur
    runValidators: true,
  });

  if (!user) {
    throw new ApiError(404, "Kullanici bulunamadi");
  }

  res.json({ data: user });
});

// Kullanici sil
exports.deleteUser = asyncHandler(async (req, res) => {
  const user = await User.findByIdAndDelete(req.params.id);

  if (!user) {
    throw new ApiError(404, "Kullanici bulunamadi");
  }

  res.status(204).end();
});

Validation (Zod)

bash
npm install zod
javascript
// src/validators/userValidator.js
const { z } = require("zod");

const createUserSchema = z.object({
  name: z.string().min(2, "Isim en az 2 karakter olmali"),
  email: z.string().email("Gecerli bir email girin"),
  password: z.string().min(8, "Sifre en az 8 karakter olmali"),
  role: z.enum(["user", "admin"]).default("user"),
  age: z.number().int().min(18).max(120).optional(),
});

const updateUserSchema = createUserSchema.partial(); // Tum alanlar opsiyonel

module.exports = { createUserSchema, updateUserSchema };
javascript
// src/middleware/validate.js
function validate(schema) {
  return (req, res, next) => {
    const result = schema.safeParse(req.body);

    if (!result.success) {
      const errors = result.error.errors.map((e) => ({
        field: e.path.join("."),
        message: e.message,
      }));
      return res.status(400).json({ error: "Validation hatasi", details: errors });
    }

    req.body = result.data; // Temizlenmis veriyi kullan
    next();
  };
}

module.exports = validate;
javascript
// Route'ta kullanim
const validate = require("../middleware/validate");
const { createUserSchema } = require("../validators/userValidator");

router.post("/users", validate(createUserSchema), userController.createUser);

Validation (Joi Alternatifi)

javascript
const Joi = require("joi");

const createUserSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/).required(),
  role: Joi.string().valid("user", "admin").default("user"),
});

// Middleware
function validateJoi(schema) {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body, { abortEarly: false });
    if (error) {
      const details = error.details.map((d) => ({
        field: d.path.join("."),
        message: d.message,
      }));
      return res.status(400).json({ error: "Validation hatasi", details });
    }
    req.body = value;
    next();
  };
}

Summary: Route -> Validate -> Controller -> Model. Clean architecture = kolay test, kolay bakim. Laravel'deki FormRequest = buradaki validate middleware.


7) Veritabani Entegrasyonu

MongoDB + Mongoose

bash
npm install mongoose
javascript
// src/config/database.js
const mongoose = require("mongoose");

async function connectDB() {
  try {
    await mongoose.connect(process.env.MONGODB_URI, {
      // Mongoose 7+ icin bu opsiyonlar artik varsayilan
    });
    console.log("MongoDB baglandI");
  } catch (err) {
    console.error("MongoDB baglanti hatasi:", err.message);
    process.exit(1);
  }
}

// Baglanti olaylarini dinle
mongoose.connection.on("disconnected", () => {
  console.log("MongoDB baglantisi kesildi");
});

module.exports = connectDB;
javascript
// src/models/User.js
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");

const userSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: [true, "Isim zorunlu"],
      trim: true,
      minlength: [2, "Isim en az 2 karakter"],
    },
    email: {
      type: String,
      required: [true, "Email zorunlu"],
      unique: true,
      lowercase: true,
      match: [/^\S+@\S+\.\S+$/, "Gecerli email girin"],
    },
    password: {
      type: String,
      required: [true, "Sifre zorunlu"],
      minlength: 8,
      select: false, // Sorgularda sifre gelmez
    },
    role: {
      type: String,
      enum: ["user", "admin"],
      default: "user",
    },
    isActive: {
      type: Boolean,
      default: true,
    },
  },
  {
    timestamps: true, // createdAt, updatedAt otomatik
    toJSON: {
      transform(doc, ret) {
        delete ret.password;
        delete ret.__v;
        return ret;
      },
    },
  }
);

// --- Pre-save hook: Sifre hashleme ---
userSchema.pre("save", async function (next) {
  if (!this.isModified("password")) return next();
  this.password = await bcrypt.hash(this.password, 12);
  next();
});

// --- Instance method: Sifre karsilastirma ---
userSchema.methods.comparePassword = async function (candidatePassword) {
  return bcrypt.compare(candidatePassword, this.password);
};

// --- Static method: Email ile bul ---
userSchema.statics.findByEmail = function (email) {
  return this.findOne({ email }).select("+password");
};

// --- Index ---
userSchema.index({ email: 1 });
userSchema.index({ createdAt: -1 });

module.exports = mongoose.model("User", userSchema);

PostgreSQL + Prisma

bash
npm install prisma @prisma/client
npx prisma init
prisma
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  password  String
  role      Role     @default(USER)
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([email])
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

enum Role {
  USER
  ADMIN
}
bash
# Migration calistir
npx prisma migrate dev --name init

# Client olustur
npx prisma generate

# Studio (GUI)
npx prisma studio
javascript
// src/config/prisma.js
const { PrismaClient } = require("@prisma/client");

const prisma = new PrismaClient({
  log: process.env.NODE_ENV === "development" ? ["query", "warn", "error"] : ["error"],
});

module.exports = prisma;
javascript
// Prisma CRUD ornekleri
const prisma = require("../config/prisma");

// Olustur
const user = await prisma.user.create({
  data: { name: "Fahri", email: "fahri@example.com", password: hashedPw },
});

// Oku (iliski ile birlikte)
const userWithPosts = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true },
});

// Listele (filtreleme + sayfalama)
const users = await prisma.user.findMany({
  where: { role: "USER", name: { contains: "fah", mode: "insensitive" } },
  orderBy: { createdAt: "desc" },
  skip: 0,
  take: 10,
  select: { id: true, name: true, email: true, _count: { select: { posts: true } } },
});

// Guncelle
const updated = await prisma.user.update({
  where: { id: 1 },
  data: { name: "Fahri Aydin" },
});

// Sil
await prisma.user.delete({ where: { id: 1 } });

// Transaction
const [post, user] = await prisma.$transaction([
  prisma.post.create({ data: { title: "Yeni Yazi", authorId: 1 } }),
  prisma.user.update({ where: { id: 1 }, data: { name: "Updated" } }),
]);

Summary: MongoDB = esnek şema (Mongoose ile), PostgreSQL = ilişkisel (Prisma ile). Prisma type-safe + migration + studio sağlar. Laravel'deki Eloquent = Mongoose/Prisma.


8) Authentication

JWT Authentication

bash
npm install jsonwebtoken bcryptjs
javascript
// src/utils/jwt.js
const jwt = require("jsonwebtoken");

function generateTokens(user) {
  const accessToken = jwt.sign(
    { id: user.id, email: user.email, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: "15m" }
  );

  const refreshToken = jwt.sign(
    { id: user.id },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: "7d" }
  );

  return { accessToken, refreshToken };
}

function verifyToken(token, secret) {
  return jwt.verify(token, secret);
}

module.exports = { generateTokens, verifyToken };
javascript
// src/controllers/authController.js
const User = require("../models/User");
const { generateTokens, verifyToken } = require("../utils/jwt");
const ApiError = require("../utils/ApiError");
const asyncHandler = require("../utils/asyncHandler");

// Kayit (Register)
exports.register = asyncHandler(async (req, res) => {
  const { name, email, password } = req.body;

  // Email kontrolu
  const existing = await User.findOne({ email });
  if (existing) {
    throw new ApiError(409, "Bu email zaten kayitli");
  }

  const user = await User.create({ name, email, password });
  const tokens = generateTokens(user);

  res.status(201).json({
    message: "Kayit basarili",
    user: { id: user._id, name: user.name, email: user.email },
    ...tokens,
  });
});

// Giris (Login)
exports.login = asyncHandler(async (req, res) => {
  const { email, password } = req.body;

  // +password: select: false olan alani dahil et
  const user = await User.findByEmail(email);
  if (!user || !(await user.comparePassword(password))) {
    throw new ApiError(401, "Email veya sifre hatali");
  }

  const tokens = generateTokens(user);

  // Refresh token'i httpOnly cookie'de sakla
  res.cookie("refreshToken", tokens.refreshToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "strict",
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 gun
  });

  res.json({
    message: "Giris basarili",
    user: { id: user._id, name: user.name, role: user.role },
    accessToken: tokens.accessToken,
  });
});

// Token yenile
exports.refreshToken = asyncHandler(async (req, res) => {
  const token = req.cookies.refreshToken;
  if (!token) {
    throw new ApiError(401, "Refresh token bulunamadi");
  }

  const decoded = verifyToken(token, process.env.JWT_REFRESH_SECRET);
  const user = await User.findById(decoded.id);
  if (!user) {
    throw new ApiError(401, "Kullanici bulunamadi");
  }

  const tokens = generateTokens(user);

  res.cookie("refreshToken", tokens.refreshToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "strict",
    maxAge: 7 * 24 * 60 * 60 * 1000,
  });

  res.json({ accessToken: tokens.accessToken });
});

// Cikis (Logout)
exports.logout = (req, res) => {
  res.clearCookie("refreshToken");
  res.json({ message: "Cikis yapildi" });
};

Auth Middleware

javascript
// src/middleware/auth.js
const { verifyToken } = require("../utils/jwt");
const ApiError = require("../utils/ApiError");

function authenticate(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    throw new ApiError(401, "Giris yapmaniz gerekiyor");
  }

  try {
    const token = authHeader.split(" ")[1];
    req.user = verifyToken(token, process.env.JWT_SECRET);
    next();
  } catch (err) {
    if (err.name === "TokenExpiredError") {
      throw new ApiError(401, "Token suresi dolmus");
    }
    throw new ApiError(401, "Gecersiz token");
  }
}

function authorize(...roles) {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      throw new ApiError(403, "Bu islem icin yetkiniz yok");
    }
    next();
  };
}

module.exports = { authenticate, authorize };
javascript
// Route'larda kullanim
const { authenticate, authorize } = require("../middleware/auth");

router.post("/auth/register", authController.register);
router.post("/auth/login", authController.login);
router.post("/auth/refresh", authController.refreshToken);
router.post("/auth/logout", authController.logout);

// Korumali route'lar
router.get("/users", authenticate, authorize("admin"), userController.getUsers);
router.get("/profile", authenticate, userController.getProfile);

Summary: JWT = access token (kisa omurlu, header'da) + refresh token (uzun omurlu, httpOnly cookie'de). bcrypt ile sifre hashle, middleware ile koru.


9) Dosya Islemleri & Upload

fs/promises (Modern Dosya Islemleri)

javascript
const fs = require("fs/promises");
const path = require("path");

// --- Klasor icerigini recursive listele ---
async function listFilesRecursive(dir) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  const files = [];

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      files.push(...(await listFilesRecursive(fullPath)));
    } else {
      files.push(fullPath);
    }
  }

  return files;
}

// --- Dosya kopyalama ---
async function copyFile(src, dest) {
  await fs.mkdir(path.dirname(dest), { recursive: true });
  await fs.copyFile(src, dest);
  console.log(`Kopyalandi: ${src} -> ${dest}`);
}

// --- JSON dosyasi okuma/yazma ---
async function readJSON(filePath) {
  const data = await fs.readFile(filePath, "utf-8");
  return JSON.parse(data);
}

async function writeJSON(filePath, data) {
  await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
}

// --- Dosya boyutunu okunabilir formata cevir ---
async function getFileSize(filePath) {
  const stats = await fs.stat(filePath);
  const units = ["B", "KB", "MB", "GB"];
  let size = stats.size;
  let unitIndex = 0;

  while (size >= 1024 && unitIndex < units.length - 1) {
    size /= 1024;
    unitIndex++;
  }

  return `${size.toFixed(2)} ${units[unitIndex]}`;
}

// --- Gecici dosya olustur ve temizle ---
async function withTempFile(fn) {
  const tmpPath = path.join(
    require("os").tmpdir(),
    `tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`
  );

  try {
    await fn(tmpPath);
  } finally {
    try {
      await fs.unlink(tmpPath);
    } catch {
      // Dosya zaten silinmis olabilir
    }
  }
}

// --- Dosya izinlerini kontrol et ---
async function isReadable(filePath) {
  try {
    await fs.access(filePath, fs.constants.R_OK);
    return true;
  } catch {
    return false;
  }
}

async function isWritable(filePath) {
  try {
    await fs.access(filePath, fs.constants.W_OK);
    return true;
  } catch {
    return false;
  }
}

Dosya Upload (multer)

bash
npm install multer
javascript
// src/middleware/upload.js
const multer = require("multer");
const path = require("path");
const ApiError = require("../utils/ApiError");

// --- Disk storage ---
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "uploads/");
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
    const ext = path.extname(file.originalname);
    cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`);
  },
});

// --- Dosya filtresi ---
const fileFilter = (req, file, cb) => {
  const allowedTypes = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
  if (allowedTypes.includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new ApiError(400, "Desteklenmeyen dosya tipi"), false);
  }
};

const upload = multer({
  storage,
  fileFilter,
  limits: {
    fileSize: 5 * 1024 * 1024, // 5MB limit
    files: 5,                   // Maksimum 5 dosya
  },
});

module.exports = upload;
javascript
// Route'larda kullanim
const upload = require("../middleware/upload");

// Tek dosya
router.post("/upload/avatar", upload.single("avatar"), (req, res) => {
  res.json({
    message: "Dosya yuklendi",
    file: {
      filename: req.file.filename,
      size: req.file.size,
      mimetype: req.file.mimetype,
    },
  });
});

// Birden fazla dosya
router.post("/upload/gallery", upload.array("photos", 5), (req, res) => {
  res.json({
    message: `${req.files.length} dosya yuklendi`,
    files: req.files.map((f) => ({ filename: f.filename, size: f.size })),
  });
});

Memory Storage ve Buffer ile Upload

javascript
// Memory storage: dosyayi diske yazmadan bellekte tut
const memoryStorage = multer({ storage: multer.memoryStorage() });

router.post("/upload/process", memoryStorage.single("file"), async (req, res) => {
  // req.file.buffer ile dosya icerigine eris
  const content = req.file.buffer.toString("utf-8");
  const lineCount = content.split("\n").length;

  res.json({
    originalName: req.file.originalname,
    size: req.file.size,
    lineCount,
  });
});

// Sharp ile gorsel isleme
const sharp = require("sharp");

router.post("/upload/resize", memoryStorage.single("image"), async (req, res) => {
  const resized = await sharp(req.file.buffer)
    .resize(300, 300, { fit: "cover" })
    .webp({ quality: 80 })
    .toBuffer();

  // S3 veya diske yaz
  await fs.writeFile(`uploads/thumb-${Date.now()}.webp`, resized);

  res.json({ message: "Gorsel boyutlandirildi", size: resized.length });
});

Dosya İzleme (chokidar)

bash
npm install chokidar
javascript
const chokidar = require("chokidar");

// --- Klasor izleme ---
const watcher = chokidar.watch("./uploads", {
  ignored: /(^|[\/\\])\../,  // Gizli dosyalari yoksay
  persistent: true,
  ignoreInitial: true,        // Baslangictaki dosyalari yoksay
  depth: 3,                   // Maksimum derinlik
});

watcher
  .on("add", (filePath) => {
    console.log(`Yeni dosya: ${filePath}`);
    // Ornek: dosyayi isleme kuyruGuna ekle
  })
  .on("change", (filePath) => {
    console.log(`Degisen dosya: ${filePath}`);
  })
  .on("unlink", (filePath) => {
    console.log(`Silinen dosya: ${filePath}`);
  })
  .on("error", (error) => {
    console.error("Izleme hatasi:", error);
  });

// Izlemeyi durdur
// watcher.close();

// --- Konfigurasyon dosyasi izleme (hot reload) ---
const configWatcher = chokidar.watch("./config.json", { persistent: true });

let appConfig = {};

configWatcher.on("change", async () => {
  try {
    const data = await fs.readFile("./config.json", "utf-8");
    appConfig = JSON.parse(data);
    console.log("Konfigurasyon yeniden yuklendi");
  } catch (err) {
    console.error("Konfigurasyon okuma hatasi:", err.message);
  }
});

Summary: fs/promises modern Node.js'te standart. multer ile dosya upload, sharp ile gorsel işleme, chokidar ile dosya izleme. Memory storage küçük dosyalar için uygun, büyük dosyalarda disk storage kullan.


10) Streams

Stream Turleri

javascript
const { Readable, Writable, Transform, Duplex, pipeline } = require("stream");
const { pipeline: pipelineAsync } = require("stream/promises");
const fs = require("fs");
const zlib = require("zlib");

Readable Stream

javascript
// --- Ozel Readable stream ---
class CounterStream extends Readable {
  constructor(max) {
    super({ objectMode: true }); // objectMode: JS nesneleri gonderebilir
    this.max = max;
    this.current = 0;
  }

  _read() {
    if (this.current < this.max) {
      this.current++;
      this.push({ number: this.current, timestamp: Date.now() });
    } else {
      this.push(null); // Stream bitti
    }
  }
}

const counter = new CounterStream(5);
counter.on("data", (chunk) => console.log(chunk));
counter.on("end", () => console.log("Stream bitti"));

// --- Readable.from ile iterable'dan stream olustur ---
async function* generateLines() {
  for (let i = 1; i <= 100; i++) {
    yield `Satir ${i}\n`;
  }
}

const lineStream = Readable.from(generateLines());
lineStream.pipe(fs.createWriteStream("output.txt"));

Writable Stream

javascript
// --- Ozel Writable stream ---
class LogWriter extends Writable {
  constructor(filePath) {
    super({ objectMode: true });
    this.filePath = filePath;
    this.buffer = [];
    this.flushInterval = setInterval(() => this.flush(), 5000);
  }

  _write(chunk, encoding, callback) {
    this.buffer.push(
      `[${new Date().toISOString()}] ${JSON.stringify(chunk)}\n`
    );

    // Her 100 satirda bir disk'e yaz (batch write)
    if (this.buffer.length >= 100) {
      this.flush()
        .then(() => callback())
        .catch(callback);
    } else {
      callback();
    }
  }

  async flush() {
    if (this.buffer.length === 0) return;

    const data = this.buffer.join("");
    this.buffer = [];
    await require("fs/promises").appendFile(this.filePath, data);
  }

  _final(callback) {
    clearInterval(this.flushInterval);
    this.flush()
      .then(() => callback())
      .catch(callback);
  }
}

const logger = new LogWriter("app.log");
logger.write({ level: "info", message: "Uygulama basladi" });
logger.write({ level: "warn", message: "Disk dolmak uzere" });
logger.end();

Transform Stream

javascript
// --- CSV'yi JSON'a ceviren Transform stream ---
class CSVToJSON extends Transform {
  constructor() {
    super({ objectMode: true });
    this.headers = null;
    this.remainder = "";
  }

  _transform(chunk, encoding, callback) {
    const data = this.remainder + chunk.toString();
    const lines = data.split("\n");
    this.remainder = lines.pop(); // Son satir eksik olabilir

    for (const line of lines) {
      if (!line.trim()) continue;

      const values = line.split(",").map((v) => v.trim());

      if (!this.headers) {
        this.headers = values;
        continue;
      }

      const obj = {};
      this.headers.forEach((header, i) => {
        obj[header] = values[i];
      });

      this.push(obj);
    }

    callback();
  }

  _flush(callback) {
    if (this.remainder.trim()) {
      const values = this.remainder.split(",").map((v) => v.trim());
      const obj = {};
      this.headers.forEach((header, i) => {
        obj[header] = values[i];
      });
      this.push(obj);
    }
    callback();
  }
}

// Kullanim
fs.createReadStream("data.csv")
  .pipe(new CSVToJSON())
  .on("data", (row) => console.log(row))
  .on("end", () => console.log("CSV isleme tamamlandi"));

// --- Basit filtre Transform ---
class FilterStream extends Transform {
  constructor(filterFn) {
    super({ objectMode: true });
    this.filterFn = filterFn;
  }

  _transform(chunk, encoding, callback) {
    if (this.filterFn(chunk)) {
      this.push(chunk);
    }
    callback();
  }
}

Duplex Stream

javascript
// --- Duplex stream: hem okur hem yazar (bagimSiz) ---
class EchoStream extends Duplex {
  constructor() {
    super();
    this.data = [];
  }

  _read(size) {
    if (this.data.length > 0) {
      this.push(this.data.shift());
    } else {
      this.push(null);
    }
  }

  _write(chunk, encoding, callback) {
    // Yazilan veriyi buyuk harfe cevir ve okuma tarafina aktar
    this.data.push(chunk.toString().toUpperCase());
    callback();
  }
}

Pipeline

javascript
// --- pipeline ile guvenli stream zincirleme ---
// pipeline otomatik olarak hata yonetimi ve backpressure saglar

// Dosya sikistirma
async function compressFile(input, output) {
  await pipelineAsync(
    fs.createReadStream(input),
    zlib.createGzip(),
    fs.createWriteStream(output)
  );
  console.log("Sikistirma tamamlandi");
}

// Dosya acma
async function decompressFile(input, output) {
  await pipelineAsync(
    fs.createReadStream(input),
    zlib.createGunzip(),
    fs.createWriteStream(output)
  );
  console.log("Acma tamamlandi");
}

// Coklu transform pipeline
async function processLogFile(inputPath, outputPath) {
  await pipelineAsync(
    fs.createReadStream(inputPath),
    new Transform({
      transform(chunk, encoding, callback) {
        // Sadece ERROR satirlarini filtrele
        const lines = chunk.toString().split("\n");
        const errors = lines.filter((line) => line.includes("ERROR"));
        callback(null, errors.join("\n") + "\n");
      },
    }),
    zlib.createGzip(),
    fs.createWriteStream(outputPath)
  );
}

Backpressure Yönetimi

javascript
// --- Backpressure: Yazan taraf okuyan taraftan hizliysa ---

// YANLIS: Backpressure'i yoksayar, bellek tasar
const readable = fs.createReadStream("huge-file.dat");
const writable = fs.createWriteStream("output.dat");

// readable.on("data", (chunk) => {
//   writable.write(chunk); // TEHLIKELI: writable dolabilir
// });

// DOGRU: pipeline kullan (otomatik backpressure)
await pipelineAsync(readable, writable);

// VEYA: Manuel backpressure yonetimi
function copyWithBackpressure(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  readable.on("data", (chunk) => {
    const canContinue = writable.write(chunk);
    if (!canContinue) {
      // Writable buffer dolu, okumayı durdur
      readable.pause();
    }
  });

  writable.on("drain", () => {
    // Writable buffer bosaldi, okumaya devam et
    readable.resume();
  });

  readable.on("end", () => {
    writable.end();
  });
}

// --- HTTP response ile stream ---
app.get("/api/download/:filename", (req, res) => {
  const filePath = path.join(__dirname, "files", req.params.filename);

  const stat = fs.statSync(filePath);
  res.setHeader("Content-Length", stat.size);
  res.setHeader("Content-Type", "application/octet-stream");
  res.setHeader(
    "Content-Disposition",
    `attachment; filename="${req.params.filename}"`
  );

  const readStream = fs.createReadStream(filePath);
  readStream.pipe(res);

  readStream.on("error", (err) => {
    console.error("Stream hatasi:", err);
    if (!res.headersSent) {
      res.status(500).json({ error: "Dosya okunamadi" });
    }
  });
});

// --- HTTP request body stream (buyuk upload) ---
app.post("/api/upload/stream", (req, res) => {
  const filePath = path.join(__dirname, "uploads", `upload-${Date.now()}`);
  const writeStream = fs.createWriteStream(filePath);

  req.pipe(writeStream);

  writeStream.on("finish", () => {
    res.json({ message: "Upload tamamlandi", path: filePath });
  });

  writeStream.on("error", (err) => {
    res.status(500).json({ error: "Yazma hatasi" });
  });
});

Satir Satir Okuma (Büyük Dosyalar)

javascript
const readline = require("readline");

async function processLargeFile(filePath) {
  const rl = readline.createInterface({
    input: fs.createReadStream(filePath),
    crlfDelay: Infinity,
  });

  let lineCount = 0;
  for await (const line of rl) {
    lineCount++;
    // Her satiri isle
    if (line.includes("ERROR")) {
      console.log(`Satir ${lineCount}: ${line}`);
    }
  }
  console.log(`Toplam satir: ${lineCount}`);
}

// --- Stream ile HTTP response (CSV export) ---
app.get("/api/export/csv", authenticate, async (req, res) => {
  res.setHeader("Content-Type", "text/csv");
  res.setHeader("Content-Disposition", "attachment; filename=users.csv");

  res.write("id,name,email\n");

  const cursor = User.find().cursor();
  for await (const user of cursor) {
    res.write(`${user.id},${user.name},${user.email}\n`);
  }

  res.end();
});

Summary: Readable (kaynak), Writable (hedef), Transform (donusum), Duplex (cift yonlu). pipeline() backpressure ve hata yonetimini otomatik yapar. Büyük dosyalar için stream kullan, bellek tasmasindan kacin.


11) Environment & Config

dotenv Kurulumu

bash
npm install dotenv
bash
# .env (GIT'E EKLEME! .gitignore'a ekle)
NODE_ENV=development
PORT=3000

# Database
MONGODB_URI=mongodb://localhost:27017/myapp
DATABASE_URL=postgresql://user:password@localhost:5432/myapp

# JWT
JWT_SECRET=super-secret-key-change-in-production
JWT_REFRESH_SECRET=another-secret-key
JWT_EXPIRES_IN=15m

# External APIs
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USER=your-user
SMTP_PASS=your-pass

# Frontend
FRONTEND_URL=http://localhost:5173
bash
# .env.example (Bu GIT'e eklenir, takimla paylasilir)
NODE_ENV=development
PORT=3000
MONGODB_URI=mongodb://localhost:27017/myapp
JWT_SECRET=change-me
JWT_REFRESH_SECRET=change-me
javascript
// src/config/index.js
require("dotenv").config();

const config = {
  env: process.env.NODE_ENV || "development",
  port: parseInt(process.env.PORT, 10) || 3000,
  db: {
    uri: process.env.MONGODB_URI,
    postgresUrl: process.env.DATABASE_URL,
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    refreshSecret: process.env.JWT_REFRESH_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || "15m",
  },
  smtp: {
    host: process.env.SMTP_HOST,
    port: parseInt(process.env.SMTP_PORT, 10),
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
  frontendUrl: process.env.FRONTEND_URL || "http://localhost:5173",
  isDev: process.env.NODE_ENV === "development",
  isProd: process.env.NODE_ENV === "production",
};

// Zorunlu degiskenleri kontrol et
const requiredVars = ["JWT_SECRET", "MONGODB_URI"];
for (const varName of requiredVars) {
  if (!process.env[varName]) {
    throw new Error(`Environment degiskeni eksik: ${varName}`);
  }
}

module.exports = config;
javascript
// .gitignore
.env
.env.local
.env.production
node_modules/
dist/
uploads/
coverage/
*.log

Summary: .env gizli tut, .env.example paylAS. Zorunlu degiskenleri startup'ta kontrol et. config/index.js ile merkezi yönetim.


12) Error Handling

Custom Error Sinifi

javascript
// src/utils/ApiError.js
class ApiError extends Error {
  constructor(statusCode, message, errors = []) {
    super(message);
    this.statusCode = statusCode;
    this.errors = errors;
    this.isOperational = true; // Beklenen hata mi? (vs programmatic bug)

    Error.captureStackTrace(this, this.constructor);
  }

  static badRequest(message, errors) {
    return new ApiError(400, message, errors);
  }

  static unauthorized(message = "Giris yapmaniz gerekiyor") {
    return new ApiError(401, message);
  }

  static forbidden(message = "Yetkiniz yok") {
    return new ApiError(403, message);
  }

  static notFound(message = "Kaynak bulunamadi") {
    return new ApiError(404, message);
  }

  static conflict(message) {
    return new ApiError(409, message);
  }

  static internal(message = "Sunucu hatasi") {
    return new ApiError(500, message);
  }
}

module.exports = ApiError;

Async Handler Wrapper

javascript
// src/utils/asyncHandler.js
// try/catch yazmak yerine controller'lari sar (wrap et)
function asyncHandler(fn) {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

module.exports = asyncHandler;

// Kullanim:
// ONCE (her controller'da try/catch):
// exports.getUser = async (req, res, next) => {
//   try {
//     const user = await User.findById(req.params.id);
//     res.json(user);
//   } catch (err) {
//     next(err);
//   }
// };

// SONRA (asyncHandler ile):
// exports.getUser = asyncHandler(async (req, res) => {
//   const user = await User.findById(req.params.id);
//   res.json(user);
// });

Global Error Handler

javascript
// src/middleware/errorHandler.js
const ApiError = require("../utils/ApiError");

function errorHandler(err, req, res, next) {
  // Mongoose validation hatasi
  if (err.name === "ValidationError") {
    const errors = Object.values(err.errors).map((e) => ({
      field: e.path,
      message: e.message,
    }));
    err = ApiError.badRequest("Validation hatasi", errors);
  }

  // Mongoose duplicate key hatasi
  if (err.code === 11000) {
    const field = Object.keys(err.keyValue)[0];
    err = ApiError.conflict(`${field} zaten mevcut`);
  }

  // Mongoose invalid ObjectId
  if (err.name === "CastError") {
    err = ApiError.badRequest(`Gecersiz ${err.path}: ${err.value}`);
  }

  // JWT hatasi
  if (err.name === "JsonWebTokenError") {
    err = ApiError.unauthorized("Gecersiz token");
  }

  if (err.name === "TokenExpiredError") {
    err = ApiError.unauthorized("Token suresi dolmus");
  }

  const statusCode = err.statusCode || 500;
  const message = err.isOperational ? err.message : "Beklenmeyen sunucu hatasi";

  // Logla
  if (statusCode >= 500) {
    console.error("SERVER ERROR:", err);
  }

  res.status(statusCode).json({
    success: false,
    error: message,
    ...(err.errors?.length && { details: err.errors }),
    ...(process.env.NODE_ENV === "development" && { stack: err.stack }),
  });
}

module.exports = errorHandler;

Yakalanmamis Hataları Yakala

javascript
// src/index.js
const app = require("./app");
const connectDB = require("./config/database");

// Yakalanmamis promise rejection
process.on("unhandledRejection", (reason, promise) => {
  console.error("UNHANDLED REJECTION:", reason);
  // Graceful shutdown
  server.close(() => {
    process.exit(1);
  });
});

// Yakalanmamis exception
process.on("uncaughtException", (err) => {
  console.error("UNCAUGHT EXCEPTION:", err);
  process.exit(1);
});

async function start() {
  await connectDB();

  const server = app.listen(process.env.PORT || 3000, () => {
    console.log(`Server calisiyor: ${process.env.PORT || 3000}`);
  });

  // Graceful shutdown (SIGTERM / SIGINT)
  const shutdown = (signal) => {
    console.log(`${signal} alindi, kapatiliyor...`);
    server.close(() => {
      console.log("HTTP server kapatildi");
      process.exit(0);
    });
  };

  process.on("SIGTERM", () => shutdown("SIGTERM"));
  process.on("SIGINT", () => shutdown("SIGINT"));
}

start();

Summary: ApiError + asyncHandler + global errorHandler = temiz hata yönetimi. unhandledRejection ve uncaughtException yakala. Graceful shutdown uygula.


13) WebSocket & Real-time

Socket.io Kurulumu

bash
npm install socket.io
# Client tarafinda:
npm install socket.io-client
javascript
// --- Server tarafinda Socket.io kurulumu ---
const http = require("http");
const express = require("express");
const { Server } = require("socket.io");

const app = express();
const server = http.createServer(app);

const io = new Server(server, {
  cors: {
    origin: process.env.FRONTEND_URL || "http://localhost:5173",
    methods: ["GET", "POST"],
  },
  pingTimeout: 60000,       // Baglanti zaman asimi
  pingInterval: 25000,      // Ping araligi
  maxHttpBufferSize: 1e6,   // Maksimum mesaj boyutu (1MB)
});

server.listen(3000, () => {
  console.log("Server calisiyor: 3000");
});

Temel Bağlantı ve Olaylar

javascript
// --- Server ---
io.on("connection", (socket) => {
  console.log("Yeni baglanti:", socket.id);

  // Mesaj dinle
  socket.on("message", (data) => {
    console.log("Gelen mesaj:", data);

    // Gondericiye cevap
    socket.emit("message:response", { status: "alindi" });
  });

  // Baglanti kesilince
  socket.on("disconnect", (reason) => {
    console.log(`Baglanti kesildi: ${socket.id}, sebep: ${reason}`);
  });

  // Hata yakala
  socket.on("error", (err) => {
    console.error("Socket hatasi:", err);
  });
});

// --- Client ---
// import { io } from "socket.io-client";
// const socket = io("http://localhost:3000");
//
// socket.on("connect", () => {
//   console.log("Baglanti kuruldu:", socket.id);
//   socket.emit("message", { text: "Merhaba!" });
// });
//
// socket.on("message:response", (data) => {
//   console.log("Cevap:", data);
// });

Namespace

javascript
// Namespace: farkli endpoint'ler icin ayri iletisim kanallari
// Her namespace kendi connection event'ine sahiptir

const chatNamespace = io.of("/chat");
const adminNamespace = io.of("/admin");

// /chat namespace
chatNamespace.on("connection", (socket) => {
  console.log("Chat'e baglandi:", socket.id);

  socket.on("chat:message", (msg) => {
    // Sadece /chat namespace'indeki kullanicilara gonder
    chatNamespace.emit("chat:message", msg);
  });
});

// /admin namespace (ayri auth middleware)
adminNamespace.use((socket, next) => {
  const token = socket.handshake.auth.token;
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    if (decoded.role !== "admin") {
      return next(new Error("Yetki yok"));
    }
    socket.user = decoded;
    next();
  } catch {
    next(new Error("Gecersiz token"));
  }
});

adminNamespace.on("connection", (socket) => {
  console.log("Admin paneline baglandi:", socket.user.email);

  socket.on("admin:stats", async () => {
    const stats = await getServerStats();
    socket.emit("admin:stats", stats);
  });
});

// Client namespace kullanimi:
// const chatSocket = io("http://localhost:3000/chat");
// const adminSocket = io("http://localhost:3000/admin", {
//   auth: { token: "jwt-token-here" }
// });

Room (Oda)

javascript
io.on("connection", (socket) => {
  // --- Odaya katil ---
  socket.on("room:join", (roomId) => {
    socket.join(roomId);
    console.log(`${socket.id} odaya katildi: ${roomId}`);

    // Odadaki diger kullanicilara bildir
    socket.to(roomId).emit("room:user-joined", {
      userId: socket.id,
      roomId,
    });

    // Odadaki kullanici sayisini gonder
    const room = io.sockets.adapter.rooms.get(roomId);
    io.to(roomId).emit("room:count", room ? room.size : 0);
  });

  // --- Odadan ayril ---
  socket.on("room:leave", (roomId) => {
    socket.leave(roomId);
    socket.to(roomId).emit("room:user-left", { userId: socket.id });
  });

  // --- Odaya mesaj gonder ---
  socket.on("room:message", ({ roomId, message }) => {
    // Odadaki herkese (gondericiye de)
    io.to(roomId).emit("room:message", {
      sender: socket.id,
      message,
      timestamp: Date.now(),
    });
  });
});

Broadcast

javascript
io.on("connection", (socket) => {
  // Herkese gonder (gondericiye de)
  io.emit("announcement", "Yeni kullanici katildi");

  // Herkese gonder (gondericiye HARIC)
  socket.broadcast.emit("user:online", socket.id);

  // Belirli odaya gonder (gondericiye haric)
  socket.to("room-1").emit("room:update", { data: "..." });

  // Birden fazla odaya gonder
  socket.to("room-1").to("room-2").emit("multi-room", { data: "..." });

  // Belirli bir socket'e gonder (private mesaj)
  io.to(targetSocketId).emit("private:message", {
    from: socket.id,
    message: "Merhaba!",
  });
});

Acknowledgment (Onaylama)

javascript
// Server: callback ile onay gonder
io.on("connection", (socket) => {
  socket.on("order:create", async (orderData, callback) => {
    try {
      const order = await Order.create(orderData);
      // Client'a basari ile cevap gonder
      callback({ status: "ok", order });
    } catch (err) {
      callback({ status: "error", message: err.message });
    }
  });
});

// Client: callback ile cevap al
// socket.emit("order:create", { product: "Laptop", qty: 1 }, (response) => {
//   if (response.status === "ok") {
//     console.log("Siparis olusturuldu:", response.order);
//   } else {
//     console.error("Hata:", response.message);
//   }
// });

Socket.io Middleware (Auth)

javascript
// Baglanti oncesi authentication middleware
io.use((socket, next) => {
  const token = socket.handshake.auth.token;

  if (!token) {
    return next(new Error("Authentication gerekli"));
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    socket.user = decoded;
    next();
  } catch {
    next(new Error("Gecersiz token"));
  }
});

io.on("connection", (socket) => {
  console.log(`Kullanici baglandi: ${socket.user.email}`);

  // Kullaniciyi kendi odasina ekle (private mesajlar icin)
  socket.join(`user:${socket.user.id}`);
});

ws Kutuphanesi (Hafif Alternatif)

bash
npm install ws
javascript
const WebSocket = require("ws");
const http = require("http");

const server = http.createServer();
const wss = new WebSocket.Server({ server });

wss.on("connection", (ws, req) => {
  const ip = req.socket.remoteAddress;
  console.log(`Yeni baglanti: ${ip}`);

  ws.on("message", (data) => {
    const message = JSON.parse(data.toString());
    console.log("Gelen:", message);

    // Herkese broadcast
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify({
          type: "broadcast",
          data: message,
          timestamp: Date.now(),
        }));
      }
    });
  });

  ws.on("close", () => {
    console.log("Baglanti kapandi");
  });

  // Ping/Pong ile canli baglanti kontrolu
  ws.isAlive = true;
  ws.on("pong", () => { ws.isAlive = true; });
});

// Olmus baglantiları temizle
const heartbeat = setInterval(() => {
  wss.clients.forEach((ws) => {
    if (!ws.isAlive) return ws.terminate();
    ws.isAlive = false;
    ws.ping();
  });
}, 30000);

wss.on("close", () => clearInterval(heartbeat));

server.listen(3000);

Real-time Chat Ornegi

javascript
// --- Tam bir chat uygulamasi ---
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");

const app = express();
const server = http.createServer(app);
const io = new Server(server);

// Aktif kullanicilar
const activeUsers = new Map();

// Auth middleware
io.use((socket, next) => {
  const { username } = socket.handshake.auth;
  if (!username) {
    return next(new Error("Kullanici adi gerekli"));
  }
  socket.username = username;
  next();
});

io.on("connection", (socket) => {
  // Kullaniciyi kaydet
  activeUsers.set(socket.id, {
    username: socket.username,
    joinedAt: Date.now(),
  });

  // Herkese aktif kullanici listesini gonder
  io.emit("users:list", Array.from(activeUsers.values()));

  // Genel chat mesaji
  socket.on("chat:send", ({ message, room }) => {
    const payload = {
      id: Date.now().toString(36),
      username: socket.username,
      message,
      timestamp: new Date().toISOString(),
    };

    if (room) {
      io.to(room).emit("chat:receive", payload);
    } else {
      io.emit("chat:receive", payload);
    }
  });

  // Odaya katil
  socket.on("chat:join-room", (room) => {
    socket.join(room);
    socket.to(room).emit("chat:receive", {
      id: Date.now().toString(36),
      username: "Sistem",
      message: `${socket.username} odaya katildi`,
      timestamp: new Date().toISOString(),
      system: true,
    });
  });

  // Yazma gostergesi
  socket.on("chat:typing", (room) => {
    const target = room ? socket.to(room) : socket.broadcast;
    target.emit("chat:typing", { username: socket.username });
  });

  socket.on("chat:stop-typing", (room) => {
    const target = room ? socket.to(room) : socket.broadcast;
    target.emit("chat:stop-typing", { username: socket.username });
  });

  // Baglanti kesilince
  socket.on("disconnect", () => {
    activeUsers.delete(socket.id);
    io.emit("users:list", Array.from(activeUsers.values()));
    io.emit("chat:receive", {
      id: Date.now().toString(36),
      username: "Sistem",
      message: `${socket.username} ayrildi`,
      timestamp: new Date().toISOString(),
      system: true,
    });
  });
});

server.listen(3000, () => {
  console.log("Chat server calisiyor: 3000");
});

Summary: Socket.io = room + namespace + broadcast + acknowledgment ile guclu real-time iletisim. ws kutuphanesi daha hafif ama daha az özellik sunar. Auth middleware ile baglantiyi koru. Heartbeat ile olmus baglantiları temizle.


14) Caching (Redis)

Redis Baglantisi (ioredis)

bash
npm install ioredis
javascript
// src/config/redis.js
const Redis = require("ioredis");

const redis = new Redis({
  host: process.env.REDIS_HOST || "127.0.0.1",
  port: parseInt(process.env.REDIS_PORT, 10) || 6379,
  password: process.env.REDIS_PASSWORD || undefined,
  db: parseInt(process.env.REDIS_DB, 10) || 0,
  maxRetriesPerRequest: 3,
  retryStrategy(times) {
    const delay = Math.min(times * 50, 2000);
    return delay;
  },
  lazyConnect: true,
});

redis.on("connect", () => {
  console.log("Redis baglantisi kuruldu");
});

redis.on("error", (err) => {
  console.error("Redis hatasi:", err.message);
});

// Baglanti test
async function connectRedis() {
  try {
    await redis.connect();
    await redis.ping();
    console.log("Redis hazir");
  } catch (err) {
    console.error("Redis baglanti hatasi:", err.message);
    process.exit(1);
  }
}

module.exports = { redis, connectRedis };

Temel Redis Islemleri

javascript
const { redis } = require("../config/redis");

// --- String islemleri ---
await redis.set("user:1:name", "Fahri");
await redis.set("session:abc", "data", "EX", 3600); // 1 saat TTL
const name = await redis.get("user:1:name");

// --- Hash islemleri ---
await redis.hset("user:1", {
  name: "Fahri",
  email: "fahri@example.com",
  role: "admin",
});
const user = await redis.hgetall("user:1");
const email = await redis.hget("user:1", "email");

// --- Liste islemleri ---
await redis.lpush("queue:emails", JSON.stringify({ to: "user@test.com" }));
const job = await redis.rpop("queue:emails");

// --- Set islemleri ---
await redis.sadd("online:users", "user:1", "user:2", "user:3");
const isOnline = await redis.sismember("online:users", "user:1");
const onlineCount = await redis.scard("online:users");
await redis.srem("online:users", "user:1");

// --- Sorted Set (siralama, leaderboard) ---
await redis.zadd("leaderboard", 100, "player:1", 250, "player:2", 150, "player:3");
const top3 = await redis.zrevrange("leaderboard", 0, 2, "WITHSCORES");

// --- Key yonetimi ---
await redis.del("user:1:name");
await redis.expire("session:abc", 1800); // TTL guncelle
const ttl = await redis.ttl("session:abc");
const exists = await redis.exists("user:1");

Cache-Aside Pattern

javascript
// src/utils/cache.js
const { redis } = require("../config/redis");

class CacheService {
  // Cache-Aside: Once cache'e bak, yoksa DB'den al ve cache'e yaz
  async getOrSet(key, fetchFn, ttlSeconds = 300) {
    // 1. Cache'ten oku
    const cached = await redis.get(key);
    if (cached) {
      return JSON.parse(cached);
    }

    // 2. Cache'te yoksa veriyi getir
    const data = await fetchFn();

    // 3. Cache'e yaz
    if (data) {
      await redis.set(key, JSON.stringify(data), "EX", ttlSeconds);
    }

    return data;
  }

  // Cache'i gecersiz kil (invalidate)
  async invalidate(pattern) {
    const keys = await redis.keys(pattern);
    if (keys.length > 0) {
      await redis.del(...keys);
    }
  }

  // Belirli key'i sil
  async delete(key) {
    await redis.del(key);
  }

  // Cache'i tamamen temizle (dikkatli kullan!)
  async flush() {
    await redis.flushdb();
  }
}

module.exports = new CacheService();
javascript
// Controller'da kullanim
const cache = require("../utils/cache");

exports.getUser = asyncHandler(async (req, res) => {
  const { id } = req.params;

  const user = await cache.getOrSet(
    `user:${id}`,
    () => User.findById(id),
    600 // 10 dakika cache
  );

  if (!user) {
    throw new ApiError(404, "Kullanici bulunamadi");
  }

  res.json({ data: user });
});

exports.getUsers = asyncHandler(async (req, res) => {
  const { page = 1, limit = 10 } = req.query;
  const cacheKey = `users:page:${page}:limit:${limit}`;

  const result = await cache.getOrSet(
    cacheKey,
    async () => {
      const skip = (page - 1) * limit;
      const [users, total] = await Promise.all([
        User.find().skip(skip).limit(Number(limit)),
        User.countDocuments(),
      ]);
      return { users, total };
    },
    120 // 2 dakika cache
  );

  res.json({
    data: result.users,
    pagination: {
      page: Number(page),
      limit: Number(limit),
      total: result.total,
    },
  });
});

// Guncelleme sonrasi cache invalidation
exports.updateUser = asyncHandler(async (req, res) => {
  const user = await User.findByIdAndUpdate(req.params.id, req.body, {
    new: true,
    runValidators: true,
  });

  if (!user) {
    throw new ApiError(404, "Kullanici bulunamadi");
  }

  // Ilgili cache'leri temizle
  await cache.delete(`user:${req.params.id}`);
  await cache.invalidate("users:page:*");

  res.json({ data: user });
});

Cache Middleware

javascript
// src/middleware/cacheMiddleware.js
const { redis } = require("../config/redis");

function cacheResponse(ttlSeconds = 300) {
  return async (req, res, next) => {
    // Sadece GET isteklerini cache'le
    if (req.method !== "GET") return next();

    const key = `cache:${req.originalUrl}`;

    try {
      const cached = await redis.get(key);
      if (cached) {
        const data = JSON.parse(cached);
        return res.json(data);
      }
    } catch {
      // Cache hatasi olursa devam et
    }

    // Orijinal json metodunu sar
    const originalJson = res.json.bind(res);
    res.json = (data) => {
      // Basarili cevaplari cache'le
      if (res.statusCode >= 200 && res.statusCode < 300) {
        redis.set(key, JSON.stringify(data), "EX", ttlSeconds).catch(() => {});
      }
      originalJson(data);
    };

    next();
  };
}

module.exports = cacheResponse;
javascript
// Route'ta kullanim
const cacheResponse = require("../middleware/cacheMiddleware");

router.get("/posts", cacheResponse(60), postController.getPosts);
router.get("/posts/:id", cacheResponse(300), postController.getPost);

Session Store (connect-redis)

bash
npm install express-session connect-redis
javascript
const session = require("express-session");
const RedisStore = require("connect-redis").default;
const { redis } = require("./config/redis");

app.use(
  session({
    store: new RedisStore({
      client: redis,
      prefix: "sess:",       // Redis key prefix
      ttl: 86400,            // 1 gun (saniye)
    }),
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      sameSite: "strict",
      maxAge: 24 * 60 * 60 * 1000, // 1 gun (milisaniye)
    },
  })
);

// Session kullanimi
app.post("/login", async (req, res) => {
  const user = await authenticateUser(req.body);
  req.session.userId = user.id;
  req.session.role = user.role;
  res.json({ message: "Giris basarili" });
});

app.get("/profile", (req, res) => {
  if (!req.session.userId) {
    return res.status(401).json({ error: "Giris yapmaniz gerekiyor" });
  }
  res.json({ userId: req.session.userId, role: req.session.role });
});

app.post("/logout", (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      return res.status(500).json({ error: "Cikis yapilamadi" });
    }
    res.clearCookie("connect.sid");
    res.json({ message: "Cikis yapildi" });
  });
});

Rate Limiting ile Redis

javascript
const rateLimit = require("express-rate-limit");
const RedisStore = require("rate-limit-redis").default;
const { redis } = require("./config/redis");

const apiLimiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redis.call(...args),
  }),
  windowMs: 15 * 60 * 1000, // 15 dakika
  max: 100,                   // Pencere basina maksimum istek
  standardHeaders: true,
  legacyHeaders: false,
  message: {
    error: "Cok fazla istek gonderdiniz, lutfen bekleyin",
  },
});

app.use("/api", apiLimiter);

Summary: Redis = hızlı cache + session store + rate limiting. Cache-Aside pattern ile DB yukunu azalt. Cache invalidation unutma! ioredis ile baglan, connect-redis ile session sakla.


15) Clustering & Concurrency

Cluster Module

javascript
// src/cluster.js
const cluster = require("cluster");
const os = require("os");

const numCPUs = os.cpus().length;

if (cluster.isPrimary) {
  console.log(`Primary process ${process.pid} calisiyor`);
  console.log(`${numCPUs} worker baslatiliyor...`);

  // Her CPU cekirdegi icin bir worker olustur
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Worker olurse yenisini baslat
  cluster.on("exit", (worker, code, signal) => {
    console.log(
      `Worker ${worker.process.pid} oldu (code: ${code}, signal: ${signal})`
    );
    console.log("Yeni worker baslatiliyor...");
    cluster.fork();
  });

  // Worker'lar arasi iletisim
  cluster.on("message", (worker, message) => {
    console.log(`Worker ${worker.process.pid}'den mesaj:`, message);

    // Tum worker'lara broadcast
    for (const id in cluster.workers) {
      cluster.workers[id].send(message);
    }
  });

  // Graceful shutdown
  process.on("SIGTERM", () => {
    console.log("Primary SIGTERM alindi, worker'lar kapatiliyor...");
    for (const id in cluster.workers) {
      cluster.workers[id].process.kill("SIGTERM");
    }
  });
} else {
  // Worker process: HTTP server baslat
  const app = require("./app");
  const PORT = process.env.PORT || 3000;

  app.listen(PORT, () => {
    console.log(`Worker ${process.pid} port ${PORT}'da calisiyor`);
  });

  // Primary'den mesaj al
  process.on("message", (message) => {
    console.log(`Worker ${process.pid} mesaj aldi:`, message);
  });
}

Worker Threads

javascript
// Worker threads: CPU-intensive islemler icin
// Event loop'u bloklamadan agir hesaplamalar yapar

// --- Ana dosya: worker-manager.js ---
const { Worker, isMainThread, parentPort, workerData } = require("worker_threads");

if (isMainThread) {
  // Ana thread
  function runWorker(data) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, { workerData: data });

      worker.on("message", resolve);
      worker.on("error", reject);
      worker.on("exit", (code) => {
        if (code !== 0) {
          reject(new Error(`Worker ${code} koduyla durdu`));
        }
      });
    });
  }

  // Kullanim
  async function main() {
    console.log("Agir hesaplama basliyor...");
    const start = Date.now();

    // Paralel worker'lar calistir
    const results = await Promise.all([
      runWorker({ start: 0, end: 25000000 }),
      runWorker({ start: 25000000, end: 50000000 }),
      runWorker({ start: 50000000, end: 75000000 }),
      runWorker({ start: 75000000, end: 100000000 }),
    ]);

    const totalPrimes = results.reduce((sum, r) => sum + r.count, 0);
    console.log(`Toplam asal sayi: ${totalPrimes}`);
    console.log(`Sure: ${Date.now() - start}ms`);
  }

  main();
} else {
  // Worker thread
  const { start, end } = workerData;

  function countPrimes(start, end) {
    let count = 0;
    for (let i = Math.max(2, start); i < end; i++) {
      let isPrime = true;
      for (let j = 2; j <= Math.sqrt(i); j++) {
        if (i % j === 0) {
          isPrime = false;
          break;
        }
      }
      if (isPrime) count++;
    }
    return count;
  }

  const count = countPrimes(start, end);
  parentPort.postMessage({ count, start, end });
}

Worker Thread Pool

javascript
// src/utils/workerPool.js
const { Worker } = require("worker_threads");
const os = require("os");

class WorkerPool {
  constructor(workerScript, poolSize = os.cpus().length) {
    this.workerScript = workerScript;
    this.poolSize = poolSize;
    this.workers = [];
    this.queue = [];
    this.activeWorkers = 0;
  }

  execute(data) {
    return new Promise((resolve, reject) => {
      const task = { data, resolve, reject };

      if (this.activeWorkers < this.poolSize) {
        this._runTask(task);
      } else {
        this.queue.push(task);
      }
    });
  }

  _runTask(task) {
    this.activeWorkers++;
    const worker = new Worker(this.workerScript, { workerData: task.data });

    worker.on("message", (result) => {
      task.resolve(result);
      this.activeWorkers--;
      this._processQueue();
    });

    worker.on("error", (err) => {
      task.reject(err);
      this.activeWorkers--;
      this._processQueue();
    });
  }

  _processQueue() {
    if (this.queue.length > 0 && this.activeWorkers < this.poolSize) {
      const nextTask = this.queue.shift();
      this._runTask(nextTask);
    }
  }
}

module.exports = WorkerPool;
javascript
// Kullanim
const WorkerPool = require("./utils/workerPool");

const pool = new WorkerPool("./workers/imageProcessor.js", 4);

app.post("/api/process-images", async (req, res) => {
  const images = req.body.images; // ["img1.jpg", "img2.jpg", ...]

  const results = await Promise.all(
    images.map((img) => pool.execute({ imagePath: img }))
  );

  res.json({ processed: results.length });
});

Child Process (spawn, exec, fork)

javascript
const { spawn, exec, execSync, fork } = require("child_process");

// --- exec: Shell komutu, kucuk ciktilar ---
exec("df -h", (err, stdout, stderr) => {
  if (err) {
    console.error("Hata:", err.message);
    return;
  }
  console.log("Disk kullanimi:\n", stdout);
});

// --- execSync: Senkron, hizli komutlar ---
const nodeVersion = execSync("node --version", { encoding: "utf-8" }).trim();
console.log("Node:", nodeVersion);

// --- spawn: Buyuk ciktilar, stream tabanlI ---
function runCommand(command, args) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args);
    let stdout = "";
    let stderr = "";

    child.stdout.on("data", (data) => { stdout += data; });
    child.stderr.on("data", (data) => { stderr += data; });

    child.on("close", (code) => {
      if (code === 0) {
        resolve(stdout);
      } else {
        reject(new Error(`Komut ${code} ile bitti: ${stderr}`));
      }
    });

    child.on("error", reject);
  });
}

// Kullanim
const output = await runCommand("git", ["log", "--oneline", "-5"]);

// --- fork: Node.js child process (IPC ile iletisim) ---
// parent.js
const child = fork("./workers/heavy-task.js");

child.send({ type: "start", data: { items: 10000 } });

child.on("message", (result) => {
  console.log("Sonuc:", result);
  child.kill();
});

// workers/heavy-task.js
// process.on("message", (msg) => {
//   if (msg.type === "start") {
//     const result = doHeavyWork(msg.data);
//     process.send({ type: "done", result });
//   }
// });

spawn vs exec vs fork Karsilastirmasi

Özellikexecspawnfork
Shell kullanimiEvetHayirHayir
BufferTamamen bellekteStreamStream
IPC kanalYokYokOtomatik
KullanımKisa komutlarBüyük ciktilarNode.js child
PerformansDüşükYüksekYüksek
Örnekexec("ls")spawn("ffmpeg",...)fork("worker.js")

Summary: Cluster = çoklu HTTP server instance (load balance). Worker threads = CPU-intensive islemler için paralel hesaplama. fork = Node.js child process (IPC). spawn = büyük ciktili harici komutlar. Production'da PM2 cluster mode kullanmak daha pratik.


16) Güvenlik

helmet.js (HTTP Güvenlik Basliklari)

bash
npm install helmet
javascript
const helmet = require("helmet");

// Varsayilan yapilandirma (tum korumalar aktif)
app.use(helmet());

// Ozel yapilandirma
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"],
        styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
        imgSrc: ["'self'", "data:", "https:"],
        fontSrc: ["'self'", "https://fonts.gstatic.com"],
        connectSrc: ["'self'", process.env.API_URL],
      },
    },
    crossOriginEmbedderPolicy: false,
    crossOriginResourcePolicy: { policy: "cross-origin" },
  })
);

// Helmet'in ayarladigi basliklar:
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY (clickjacking koruması)
// X-XSS-Protection: 0 (tarayici XSS filtresi)
// Strict-Transport-Security (HSTS)
// Content-Security-Policy
// Referrer-Policy
// X-DNS-Prefetch-Control
// X-Download-Options
// X-Permitted-Cross-Domain-Policies

Rate Limiting

bash
npm install express-rate-limit
javascript
const rateLimit = require("express-rate-limit");

// Genel API rate limiter
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 dakika
  max: 100,                    // Pencere basina maks istek
  standardHeaders: true,       // RateLimit-* basliklarini ekle
  legacyHeaders: false,
  message: {
    error: "Cok fazla istek. 15 dakika sonra tekrar deneyin.",
  },
});

// Login icin daha siki limit
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,                     // 15 dakikada en fazla 5 giris denemesi
  skipSuccessfulRequests: true, // Basarili girisleri sayma
  message: {
    error: "Cok fazla giris denemesi. 15 dakika sonra tekrar deneyin.",
  },
});

// Kayit icin limit
const registerLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,  // 1 saat
  max: 3,                     // Saatte 3 kayit
  message: {
    error: "Cok fazla kayit denemesi.",
  },
});

app.use("/api", apiLimiter);
app.post("/api/auth/login", loginLimiter);
app.post("/api/auth/register", registerLimiter);

CORS Yapilandirmasi

javascript
const cors = require("cors");

// Detayli CORS yapilandirmasi
const corsOptions = {
  origin: function (origin, callback) {
    const allowedOrigins = [
      process.env.FRONTEND_URL,
      "https://myapp.com",
      "https://admin.myapp.com",
    ];

    // origin undefined = ayni origin (server-to-server veya Postman)
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error("CORS izni yok"));
    }
  },
  methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  exposedHeaders: ["X-Total-Count", "X-Page-Count"],
  credentials: true,          // Cookie gonderimine izin ver
  maxAge: 86400,               // Preflight cache suresi (1 gun)
};

app.use(cors(corsOptions));

Zod ile Gelismis Validation

javascript
const { z } = require("zod");

// --- Karmasik sema ornegi ---
const registerSchema = z.object({
  body: z.object({
    name: z.string()
      .min(2, "Isim en az 2 karakter")
      .max(50, "Isim en fazla 50 karakter")
      .regex(/^[a-zA-ZğüşöçıİĞÜŞÖÇ\s]+$/, "Isim sadece harf icermeli"),
    email: z.string()
      .email("Gecerli email girin")
      .toLowerCase(),
    password: z.string()
      .min(8, "Sifre en az 8 karakter")
      .regex(/[A-Z]/, "En az 1 buyuk harf")
      .regex(/[a-z]/, "En az 1 kucuk harf")
      .regex(/[0-9]/, "En az 1 rakam")
      .regex(/[^A-Za-z0-9]/, "En az 1 ozel karakter"),
    passwordConfirm: z.string(),
    age: z.number().int().min(18).max(120).optional(),
    address: z.object({
      street: z.string().optional(),
      city: z.string().min(2),
      country: z.string().length(2, "ISO 3166-1 alpha-2 kodu girin"),
    }).optional(),
    tags: z.array(z.string()).max(5, "En fazla 5 etiket").optional(),
    website: z.string().url("Gecerli URL girin").optional(),
  }).refine((data) => data.password === data.passwordConfirm, {
    message: "Sifreler eslesmiyor",
    path: ["passwordConfirm"],
  }),
  params: z.object({}).optional(),
  query: z.object({}).optional(),
});

// Genel validation middleware
function validate(schema) {
  return (req, res, next) => {
    const result = schema.safeParse({
      body: req.body,
      params: req.params,
      query: req.query,
    });

    if (!result.success) {
      const errors = result.error.errors.map((e) => ({
        field: e.path.join("."),
        message: e.message,
      }));
      return res.status(400).json({
        error: "Validation hatasi",
        details: errors,
      });
    }

    req.body = result.data.body;
    req.params = result.data.params || req.params;
    req.query = result.data.query || req.query;
    next();
  };
}

router.post("/auth/register", validate(registerSchema), authController.register);

SQL/NoSQL Injection Korumasi

bash
npm install express-mongo-sanitize hpp
javascript
const mongoSanitize = require("express-mongo-sanitize");
const hpp = require("hpp");

// NoSQL injection korumasi
// { "email": { "$gt": "" } } gibi saldirilaRi engeller
app.use(mongoSanitize({
  replaceWith: "_",        // $ ve . karakterlerini _ ile degistir
  allowDots: false,
}));

// HTTP Parameter Pollution korumasi
// ?sort=name&sort=email gibi tekrarlanan parametreleri engeller
app.use(hpp({
  whitelist: ["filter", "fields", "sort", "page", "limit"],
}));

// --- SQL Injection icin Prisma/ORM kullan ---
// YANLIS: Raw query
// const user = await db.query(`SELECT * FROM users WHERE email = '${email}'`);

// DOGRU: Parametreli sorgu
// const user = await prisma.user.findUnique({ where: { email } });

// Eger raw query gerekiyorsa:
// const user = await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`;

JWT Best Practices

javascript
// --- JWT guvenlik en iyi uygulamalari ---

const jwt = require("jsonwebtoken");

// 1. Guclu secret kullan (en az 256 bit)
// Ornek: openssl rand -hex 32
const JWT_SECRET = process.env.JWT_SECRET; // En az 64 karakter

// 2. Kisa omurlu access token
const accessToken = jwt.sign(
  { id: user.id, role: user.role },
  JWT_SECRET,
  {
    expiresIn: "15m",       // Kisa omur
    issuer: "my-api",       // Token'i kim olusturdu
    audience: "my-app",     // Token kimin icin
    algorithm: "HS256",     // Algoritma belirt
  }
);

// 3. Refresh token icin ayri secret
const refreshToken = jwt.sign(
  { id: user.id, tokenVersion: user.tokenVersion },
  process.env.JWT_REFRESH_SECRET,
  { expiresIn: "7d" }
);

// 4. Token blacklist (cikis yapinca gecersiz kil)
const tokenBlacklist = new Set(); // Production'da Redis kullan

function isTokenBlacklisted(token) {
  return tokenBlacklist.has(token);
}

// 5. Token'da hassas bilgi SAKLAMA
// YANLIS: jwt.sign({ password: "123", creditCard: "..." })
// DOGRU: jwt.sign({ id: user.id, role: user.role })

// 6. Token yenileme sirasinda eski token'i gecersiz kil
async function refreshAccessToken(req, res) {
  const oldRefreshToken = req.cookies.refreshToken;

  // Eski token'i blacklist'e ekle
  tokenBlacklist.add(oldRefreshToken);

  // Yeni token olustur
  const newTokens = generateTokens(user);

  // Yeni refresh token'i cookie'ye yaz
  res.cookie("refreshToken", newTokens.refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: "strict",
  });

  res.json({ accessToken: newTokens.accessToken });
}

bcrypt ile Sifre Hashleme

javascript
const bcrypt = require("bcryptjs");

// --- Sifre hashleme ---
const SALT_ROUNDS = 12; // 10-12 onerilen, yuksek = yavas ama guvenli

async function hashPassword(plainPassword) {
  return bcrypt.hash(plainPassword, SALT_ROUNDS);
}

// --- Sifre dogrulama ---
async function verifyPassword(plainPassword, hashedPassword) {
  return bcrypt.compare(plainPassword, hashedPassword);
}

// --- TIMING ATTACK korumasi ---
// bcrypt.compare zaten sabit zamanli karsilastirma yapar
// Ek koruma: kullanici bulunamazsa bile hash karsilastirmasi yap
async function login(email, password) {
  const user = await User.findByEmail(email);

  // Kullanici yoksa bile bcrypt calistir (timing attack engelleme)
  const dummyHash = "$2b$12$dummy.hash.for.timing.attack.prevention";
  const isValid = await bcrypt.compare(
    password,
    user?.password || dummyHash
  );

  if (!user || !isValid) {
    throw new ApiError(401, "Email veya sifre hatali");
  }

  return user;
}
javascript
const cookieParser = require("cookie-parser");
app.use(cookieParser());

// --- Guvenli cookie ayarlari ---
res.cookie("token", value, {
  httpOnly: true,      // JavaScript ile erisilemez (XSS koruması)
  secure: true,        // Sadece HTTPS uzerinden
  sameSite: "strict",  // CSRF koruması (strict/lax/none)
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 gun
  path: "/",           // Cookie yolu
  domain: ".myapp.com", // Alt domainler dahil
  signed: true,        // Cookie imzalama (cookie-parser secret gerekir)
});

// Signed cookie kullanimi
app.use(cookieParser(process.env.COOKIE_SECRET));

res.cookie("userId", "123", { signed: true });
const userId = req.signedCookies.userId; // "123" veya false (tahrifat)

// Cookie silme
res.clearCookie("token", {
  httpOnly: true,
  secure: true,
  sameSite: "strict",
});

Güvenlik Checklist

- [ ] helmet() ile HTTP guvenlik basliklarini ayarla
- [ ] CORS'u sadece izinli domainlere ac
- [ ] Rate limiting uygula (genel + login)
- [ ] Tum input'lari Zod/Joi ile dogrula
- [ ] bcrypt ile sifre hashle (salt rounds >= 10)
- [ ] JWT secret en az 64 karakter
- [ ] Access token kisa omurlu (15dk)
- [ ] Refresh token httpOnly cookie'de
- [ ] Cookie'lerde secure + sameSite + httpOnly
- [ ] express-mongo-sanitize ile NoSQL injection engelle
- [ ] hpp ile parameter pollution engelle
- [ ] Hata mesajlarinda stack trace production'da gizle
- [ ] npm audit ile bagimlilik guvenligi tara
- [ ] .env dosyasi .gitignore'da
- [ ] HTTPS kullan (reverse proxy arkasinda)
- [ ] Dosya upload'da tip ve boyut sinirla

Summary: Güvenlik katmanli olmali: helmet (basliklar) + CORS + rate limiting + validation + auth + sanitization. Her katman bir saldiri turunu engeller. Production'da HTTPS sart.


17) Testing

Jest Kurulumu

bash
npm install -D jest supertest @types/jest
json
// package.json
{
  "scripts": {
    "test": "jest --coverage --forceExit --detectOpenHandles",
    "test:watch": "jest --watch"
  },
  "jest": {
    "testEnvironment": "node",
    "coverageDirectory": "coverage",
    "collectCoverageFrom": ["src/**/*.js", "!src/index.js"]
  }
}

Vitest Alternatifi

bash
npm install -D vitest
javascript
// vitest.config.js
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,           // describe, it, expect global
    environment: "node",
    coverage: {
      provider: "v8",
      reporter: ["text", "json", "html"],
      exclude: ["node_modules/", "tests/"],
    },
    setupFiles: ["./tests/setup.js"],
    testTimeout: 10000,
  },
});
json
// package.json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage"
  }
}

Unit Test Ornekleri

javascript
// tests/utils/apiError.test.js
const ApiError = require("../../src/utils/ApiError");

describe("ApiError", () => {
  it("dogru statusCode ve mesaj olusturur", () => {
    const error = new ApiError(404, "Bulunamadi");
    expect(error.statusCode).toBe(404);
    expect(error.message).toBe("Bulunamadi");
    expect(error.isOperational).toBe(true);
  });

  it("static factory metodlari calisir", () => {
    const err = ApiError.notFound("User bulunamadi");
    expect(err.statusCode).toBe(404);
    expect(err.message).toBe("User bulunamadi");
  });

  it("varsayilan mesajlar calisir", () => {
    expect(ApiError.unauthorized().message).toBe("Giris yapmaniz gerekiyor");
    expect(ApiError.forbidden().message).toBe("Yetkiniz yok");
  });
});
javascript
// tests/utils/jwt.test.js
const { generateTokens, verifyToken } = require("../../src/utils/jwt");

// .env test degerleri
process.env.JWT_SECRET = "test-secret";
process.env.JWT_REFRESH_SECRET = "test-refresh-secret";

describe("JWT Utils", () => {
  const mockUser = { id: "123", email: "test@test.com", role: "user" };

  it("access ve refresh token olusturur", () => {
    const tokens = generateTokens(mockUser);
    expect(tokens).toHaveProperty("accessToken");
    expect(tokens).toHaveProperty("refreshToken");
  });

  it("token verify eder", () => {
    const { accessToken } = generateTokens(mockUser);
    const decoded = verifyToken(accessToken, process.env.JWT_SECRET);
    expect(decoded.id).toBe(mockUser.id);
    expect(decoded.email).toBe(mockUser.email);
  });

  it("gecersiz token'da hata firlatir", () => {
    expect(() => verifyToken("invalid-token", process.env.JWT_SECRET)).toThrow();
  });
});

Integration Test (Supertest)

javascript
// tests/integration/auth.test.js
const request = require("supertest");
const mongoose = require("mongoose");
const app = require("../../src/app");
const User = require("../../src/models/User");

describe("Auth Endpoints", () => {
  beforeAll(async () => {
    await mongoose.connect(process.env.TEST_MONGODB_URI);
  });

  afterAll(async () => {
    await mongoose.connection.dropDatabase();
    await mongoose.connection.close();
  });

  beforeEach(async () => {
    await User.deleteMany({});
  });

  describe("POST /api/auth/register", () => {
    const validUser = {
      name: "Test User",
      email: "test@example.com",
      password: "Test1234!",
    };

    it("basarili kayit yapar (201)", async () => {
      const res = await request(app)
        .post("/api/auth/register")
        .send(validUser)
        .expect(201);

      expect(res.body).toHaveProperty("accessToken");
      expect(res.body.user.email).toBe(validUser.email);
      expect(res.body.user).not.toHaveProperty("password");
    });

    it("eksik alan ile 400 doner", async () => {
      const res = await request(app)
        .post("/api/auth/register")
        .send({ name: "Test" })
        .expect(400);

      expect(res.body.success).toBe(false);
    });

    it("ayni email ile 409 doner", async () => {
      await request(app).post("/api/auth/register").send(validUser);

      const res = await request(app)
        .post("/api/auth/register")
        .send(validUser)
        .expect(409);

      expect(res.body.error).toContain("zaten");
    });
  });

  describe("POST /api/auth/login", () => {
    beforeEach(async () => {
      await request(app)
        .post("/api/auth/register")
        .send({ name: "Test", email: "test@example.com", password: "Test1234!" });
    });

    it("basarili giris yapar", async () => {
      const res = await request(app)
        .post("/api/auth/login")
        .send({ email: "test@example.com", password: "Test1234!" })
        .expect(200);

      expect(res.body).toHaveProperty("accessToken");
    });

    it("yanlis sifre ile 401 doner", async () => {
      await request(app)
        .post("/api/auth/login")
        .send({ email: "test@example.com", password: "wrong" })
        .expect(401);
    });
  });

  describe("GET /api/profile (protected)", () => {
    it("token olmadan 401 doner", async () => {
      await request(app)
        .get("/api/profile")
        .expect(401);
    });

    it("gecerli token ile profil doner", async () => {
      const registerRes = await request(app)
        .post("/api/auth/register")
        .send({ name: "Fahri", email: "fahri@test.com", password: "Test1234!" });

      const res = await request(app)
        .get("/api/profile")
        .set("Authorization", `Bearer ${registerRes.body.accessToken}`)
        .expect(200);

      expect(res.body.user.email).toBe("fahri@test.com");
    });
  });
});

Mocking

javascript
// --- Jest ile mocking ---

// Modul mock'lama
jest.mock("../../src/models/User");
const User = require("../../src/models/User");

describe("UserController", () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  it("getUsers: kullanicilari doner", async () => {
    const mockUsers = [
      { id: "1", name: "Fahri", email: "fahri@test.com" },
      { id: "2", name: "Ali", email: "ali@test.com" },
    ];

    // Mock davranisini tanimla
    User.find.mockReturnValue({
      sort: jest.fn().mockReturnValue({
        skip: jest.fn().mockReturnValue({
          limit: jest.fn().mockResolvedValue(mockUsers),
        }),
      }),
    });
    User.countDocuments.mockResolvedValue(2);

    const res = await request(app).get("/api/users").expect(200);

    expect(res.body.data).toHaveLength(2);
    expect(User.find).toHaveBeenCalled();
  });
});

// --- Fonksiyon mock ---
const emailService = require("../../src/services/emailService");

jest.spyOn(emailService, "sendWelcomeEmail").mockResolvedValue(true);

// Test icinde
expect(emailService.sendWelcomeEmail).toHaveBeenCalledWith(
  expect.objectContaining({ email: "test@test.com" })
);

// --- Manuel mock (dosya bazli) ---
// __mocks__/nodemailer.js
module.exports = {
  createTransport: jest.fn().mockReturnValue({
    sendMail: jest.fn().mockResolvedValue({ messageId: "test-id" }),
  }),
};

// --- Timer mock ---
jest.useFakeTimers();

it("zamanlayici dogru calisir", () => {
  const callback = jest.fn();
  setTimeout(callback, 5000);

  jest.advanceTimersByTime(5000);
  expect(callback).toHaveBeenCalledTimes(1);
});

jest.useRealTimers();

Test DB Setup/Teardown

javascript
// tests/setup.js
const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");

let mongoServer;

// Tum testlerden once: Bellekte MongoDB baslat
beforeAll(async () => {
  mongoServer = await MongoMemoryServer.create();
  const uri = mongoServer.getUri();
  await mongoose.connect(uri);
});

// Her test sonrasi: Collection'lari temizle
afterEach(async () => {
  const collections = mongoose.connection.collections;
  for (const key in collections) {
    await collections[key].deleteMany({});
  }
});

// Tum testlerden sonra: Baglanti kapat ve sunucu durdur
afterAll(async () => {
  await mongoose.connection.dropDatabase();
  await mongoose.connection.close();
  await mongoServer.stop();
});
bash
npm install -D mongodb-memory-server
javascript
// --- Prisma test setup (PostgreSQL) ---
// tests/prisma-setup.js
const { PrismaClient } = require("@prisma/client");
const { execSync } = require("child_process");

const prisma = new PrismaClient();

beforeAll(async () => {
  // Test DB'ye migration uygula
  execSync("npx prisma migrate deploy", {
    env: {
      ...process.env,
      DATABASE_URL: process.env.TEST_DATABASE_URL,
    },
  });
});

afterEach(async () => {
  // Tum tablolari temizle (sira onemli: foreign key)
  await prisma.$transaction([
    prisma.post.deleteMany(),
    prisma.user.deleteMany(),
  ]);
});

afterAll(async () => {
  await prisma.$disconnect();
});

module.exports = prisma;

CI Entegrasyonu (GitHub Actions)

yaml
# .github/workflows/test.yml
name: Test

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      mongo:
        image: mongo:7
        ports:
          - 27017:27017

      redis:
        image: redis:7
        ports:
          - 6379:6379

    strategy:
      matrix:
        node-version: [20.x, 22.x]

    steps:
      - uses: actions/checkout@v4

      - name: Node.js ${{ matrix.node-version }} kur
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: "npm"

      - name: Bagimliliklari kur
        run: npm ci

      - name: Lint calistir
        run: npm run lint

      - name: Testleri calistir
        run: npm test
        env:
          NODE_ENV: test
          TEST_MONGODB_URI: mongodb://localhost:27017/test
          JWT_SECRET: test-secret-key-for-ci
          JWT_REFRESH_SECRET: test-refresh-secret-for-ci
          REDIS_HOST: localhost
          REDIS_PORT: 6379

      - name: Coverage raporu yukle
        uses: codecov/codecov-action@v4
        if: matrix.node-version == '22.x'
        with:
          file: ./coverage/lcov.info
          fail_ci_if_error: false

Coverage Hedefleri

javascript
// jest.config.js veya package.json
{
  "jest": {
    "coverageThreshold": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      },
      "src/utils/": {
        "branches": 90,
        "functions": 90,
        "lines": 90
      }
    }
  }
}
bash
# Coverage raporu olustur
npm test -- --coverage

# Raporu goruntuLe
open coverage/lcov-report/index.html

# Belirli dosyayi test et
npx jest tests/auth.test.js

# Belirli describe/it calistir
npx jest -t "basarili kayit"

Summary: Unit test = izole fonksiyon testi. Integration test = HTTP endpoint testi (supertest). Mock ile bagimliliklari izole et. mongodb-memory-server ile test DB. CI'da otomatik test ve coverage. Hedef: %80+ coverage.


18) Deployment

PM2 (Process Manager)

bash
npm install -g pm2
javascript
// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: "my-api",
      script: "src/index.js",
      instances: "max",      // CPU cekirdegi kadar instance
      exec_mode: "cluster",  // Cluster mode
      env: {
        NODE_ENV: "development",
        PORT: 3000,
      },
      env_production: {
        NODE_ENV: "production",
        PORT: 8080,
      },
      max_memory_restart: "500M",
      log_date_format: "YYYY-MM-DD HH:mm:ss",
      error_file: "logs/error.log",
      out_file: "logs/output.log",
      merge_logs: true,
      autorestart: true,
      watch: false,
      max_restarts: 10,
      restart_delay: 4000,
    },
  ],
};
bash
# PM2 komutlari
pm2 start ecosystem.config.js --env production
pm2 list                    # Calistirilan uygulamalar
pm2 monit                   # Canli izleme
pm2 logs my-api             # Log'lari gor
pm2 restart my-api           # Yeniden baslat
pm2 reload my-api            # Zero-downtime restart
pm2 stop my-api              # Durdur
pm2 delete my-api            # Sil

# Startup (sistem baslangicinda otomatik calistir)
pm2 startup
pm2 save

Docker

dockerfile
# Dockerfile
FROM node:22-alpine AS base
WORKDIR /app
COPY package*.json ./

# --- Dependencies stage ---
FROM base AS deps
RUN npm ci --only=production

# --- Build stage (TypeScript icin) ---
FROM base AS build
RUN npm ci
COPY . .
RUN npm run build

# --- Production stage ---
FROM node:22-alpine AS production
WORKDIR /app

# Guvenlik: root olmayan kullanici
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package*.json ./

USER appuser
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

CMD ["node", "dist/index.js"]
yaml
# docker-compose.yml
services:
  api:
    build: .
    ports:
      - "8080:8080"
    env_file:
      - .env.production
    depends_on:
      mongo:
        condition: service_healthy
    restart: unless-stopped

  mongo:
    image: mongo:7
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: secret
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mongo_data:
bash
# Docker komutlari
docker compose up -d              # Baslat
docker compose logs -f api        # Log'lari izle
docker compose down               # Durdur
docker compose build --no-cache   # Yeniden build et

Production Checklist

markdown
- [ ] NODE_ENV=production ayarla
- [ ] Tum environment degiskenleri tanimli
- [ ] Guvenlik basliklarI (helmet)
- [ ] CORS dogru yapilandirilmis
- [ ] Rate limiting aktif
- [ ] Input validation tum endpoint'lerde
- [ ] Error stack trace production'da gizli
- [ ] HTTPS aktif (reverse proxy arkasinda)
- [ ] Loglama yapisi hazir (winston/pino)
- [ ] Healthcheck endpoint mevcut
- [ ] Graceful shutdown implementasyonu
- [ ] npm audit temiz
- [ ] .env dosyasi .gitignore'da
- [ ] Test coverage yeterli (>80%)
- [ ] Dockerfile optimized (multi-stage build)
- [ ] PM2 veya container orchestration hazir
- [ ] Monitoring aracı (PM2 monit, Grafana, etc.)
- [ ] Backup stratejisi belirlenmis

Summary: PM2 ile cluster mode + auto-restart. Docker ile izole ortam. Production'da güvenlik, loglama, monitoring ihmal etme.


19) Tips & Best Practices

async/await Kullan (Callback Yerine)

javascript
// YANLIS: Callback hell
fs.readFile("a.txt", (err, a) => {
  if (err) throw err;
  fs.readFile("b.txt", (err, b) => {
    if (err) throw err;
    fs.writeFile("c.txt", a + b, (err) => {
      if (err) throw err;
      console.log("Tamam");
    });
  });
});

// DOGRU: async/await
async function mergeFiles() {
  const [a, b] = await Promise.all([
    fs.promises.readFile("a.txt", "utf-8"),
    fs.promises.readFile("b.txt", "utf-8"),
  ]);
  await fs.promises.writeFile("c.txt", a + b);
  console.log("Tamam");
}

// Promise.all ile paralel islemler (birbirinden bagimsiz isler)
const [users, posts, comments] = await Promise.all([
  User.find(),
  Post.find(),
  Comment.find(),
]);

// Promise.allSettled ile hatali islemleri de yakala
const results = await Promise.allSettled([
  sendEmail(user1),
  sendEmail(user2),
  sendEmail(user3),
]);

results.forEach((result, i) => {
  if (result.status === "rejected") {
    console.error(`Email ${i + 1} gonderilemedi:`, result.reason);
  }
});

Event Loop Blocking'den Kacin

javascript
// YANLIS: Event loop'u bloklar
app.get("/api/hash", (req, res) => {
  // Bu islem tum diger istekleri bekletir!
  const hash = crypto.pbkdf2Sync("password", "salt", 1000000, 64, "sha512");
  res.json({ hash: hash.toString("hex") });
});

// DOGRU: Asenkron versiyon kullan
app.get("/api/hash", async (req, res) => {
  const hash = await new Promise((resolve, reject) => {
    crypto.pbkdf2("password", "salt", 1000000, 64, "sha512", (err, key) => {
      if (err) reject(err);
      else resolve(key);
    });
  });
  res.json({ hash: hash.toString("hex") });
});

// DAHA IYI: Agir islemleri Worker Thread'e tasi
// (bkz. Clustering & Concurrency bolumu)

// --- Buyuk JSON parse etme ---
// YANLIS: Tek seferde buyuk JSON parse (event loop bloklar)
// const bigData = JSON.parse(hugeString); // 100MB+ string

// DOGRU: Stream ile satir satir isle
// (bkz. Streams bolumu)

// --- Buyuk dongulerden kacin ---
// YANLIS:
// for (let i = 0; i < 10000000; i++) { /* CPU-intensive */ }

// DOGRU: setImmediate ile event loop'a nefes aldIr
function processChunk(items, index, callback) {
  const CHUNK_SIZE = 1000;
  const end = Math.min(index + CHUNK_SIZE, items.length);

  for (let i = index; i < end; i++) {
    // islemi yap
  }

  if (end < items.length) {
    setImmediate(() => processChunk(items, end, callback));
  } else {
    callback();
  }
}

Memory Leak Tespit ve Onleme

javascript
// --- Memory kullanimi izleme ---
function logMemoryUsage() {
  const usage = process.memoryUsage();
  console.log({
    rss: `${(usage.rss / 1024 / 1024).toFixed(2)} MB`,       // Toplam bellek
    heapTotal: `${(usage.heapTotal / 1024 / 1024).toFixed(2)} MB`,
    heapUsed: `${(usage.heapUsed / 1024 / 1024).toFixed(2)} MB`,
    external: `${(usage.external / 1024 / 1024).toFixed(2)} MB`,
  });
}

// Periyodik izleme
setInterval(logMemoryUsage, 30000);

// --- Yaygin memory leak kaynaklari ---

// 1. Event listener temizlenmemesi
// YANLIS:
// emitter.on("data", handler); // Her istekte yeni listener eklenir

// DOGRU:
// emitter.once("data", handler);
// veya disconnect'te: emitter.removeListener("data", handler);

// 2. Global degiskenlerde veri biriktirme
// YANLIS:
// const cache = [];
// app.get("/api", (req, res) => {
//   cache.push(req.body); // Surekli buyur!
// });

// DOGRU: Sinirli boyutlu cache veya Redis kullan

// 3. Kapatilmayan stream/connection
// YANLIS:
// const stream = fs.createReadStream("file.txt");
// // stream.destroy() cagirilmadi!

// DOGRU:
// pipeline kullan veya try/finally ile kapat

// --- Chrome DevTools ile debug ---
// node --inspect src/index.js
// Chrome'da chrome://inspect ac
// Memory tab'inda heap snapshot al

// --- Heap snapshot ile analiz ---
const v8 = require("v8");
const fs = require("fs");

function takeHeapSnapshot() {
  const snapshotStream = v8.writeHeapSnapshot();
  console.log(`Heap snapshot yazildi: ${snapshotStream}`);
}

// API endpoint ile snapshot al
app.get("/debug/heap", (req, res) => {
  if (process.env.NODE_ENV !== "development") {
    return res.status(403).json({ error: "Sadece development'ta" });
  }
  const filename = v8.writeHeapSnapshot();
  res.json({ snapshot: filename });
});

Stream Backpressure

javascript
// Backpressure: veri kaynagi veri tuketicisinden hizli oldugunda olusan basinc

// YANLIS: Backpressure'i yoksayar
// readable.on("data", (chunk) => writable.write(chunk));

// DOGRU: pipeline kullan (her zaman)
const { pipeline } = require("stream/promises");
await pipeline(readable, transform, writable);

// VEYA: Manuel kontrol (ender durumlarda)
readable.on("data", (chunk) => {
  const ok = writable.write(chunk);
  if (!ok) readable.pause();
});
writable.on("drain", () => readable.resume());

Environment Degiskenleri (.env + dotenv)

javascript
// --- .env dosyasini mumkun olan en erken yukle ---
// src/index.js (ilk satir)
require("dotenv").config();

// --- Farkli ortamlar icin farkli .env ---
// .env                -> varsayilan (development)
// .env.test           -> test ortami
// .env.production     -> production

// Ortama gore yukle
require("dotenv").config({
  path: `.env.${process.env.NODE_ENV || "development"}`,
});

// --- Zorunlu degiskenleri kontrol et (startup'ta) ---
const REQUIRED_ENV = ["JWT_SECRET", "DATABASE_URL", "REDIS_HOST"];

for (const key of REQUIRED_ENV) {
  if (!process.env[key]) {
    console.error(`HATA: ${key} environment degiskeni tanimlanmamis`);
    process.exit(1);
  }
}

// --- Environment degiskenlerini type-safe yap (Zod ile) ---
const { z } = require("zod");

const envSchema = z.object({
  NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
  PORT: z.coerce.number().default(3000),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
  REDIS_HOST: z.string().default("127.0.0.1"),
  REDIS_PORT: z.coerce.number().default(6379),
});

const env = envSchema.parse(process.env);
// Artik env.PORT number, env.NODE_ENV validasyonlu

Genel Best Practices Ozeti

javascript
// 1. Her zaman async/await kullan (callback yerine)
// 2. Promise.all ile paralel islemleri hizlandir
// 3. Event loop'u bloklama (buyuk dongu, sync I/O, agir hesaplama)
// 4. Stream kullan (buyuk dosyalar, HTTP response)
// 5. Error handling: asyncHandler + global error handler
// 6. Graceful shutdown uygula (SIGTERM, SIGINT)
// 7. Loglama: console.log yerine winston/pino kullan
// 8. Environment degiskenlerini startup'ta dogrula
// 9. .gitignore: .env, node_modules, coverage, dist
// 10. package-lock.json'i commit et (tekrarlanabilir build)
// 11. npm audit ile guvenlik tara
// 12. TypeScript kullan (type safety)
// 13. Lint + format: eslint + prettier
// 14. Test yaz: unit + integration + %80 coverage
// 15. Docker ile ortam standardize et

Summary: async/await kullan, event loop'u bloklama, memory leak'lere dikkat et, stream backpressure'i yonet, .env dosyalarini doğrula. Node.js'te performansin anahtari event loop'u temiz tutmak.


20) Sik Kullanilan Paketler Tablosu

PaketKategoriAçıklamaKurulum
expressFrameworkWeb frameworknpm i express
corsGüvenlikCross-Origin Resource Sharingnpm i cors
helmetGüvenlikHTTP güvenlik basliklarinpm i helmet
morganLoglamaHTTP request loggernpm i morgan
winstonLoglamaGelismis loglama kutuphanesinpm i winston
pinoLoglamaYüksek performansli loggernpm i pino
compressionPerformansGzip/Brotli sIkistirmanpm i compression
express-rate-limitGüvenlikRate limitingnpm i express-rate-limit
dotenvConfigEnvironment degiskenlerinpm i dotenv
jsonwebtokenAuthJWT token islemlerinpm i jsonwebtoken
bcryptjsAuthSifre hashlemenpm i bcryptjs
zodValidationSchema-based validationnpm i zod
joiValidationObject schema validationnpm i joi
mongooseDatabaseMongoDB ODMnpm i mongoose
prismaDatabaseType-safe ORM (PostgreSQL, MySQL)npm i prisma @prisma/client
ioredisCacheRedis clientnpm i ioredis
multerUploadDosya yükleme middlewarenpm i multer
sharpGorselGorsel işleme/boyutlandirmanpm i sharp
nodemailerEmailEmail gondermenpm i nodemailer
socket.ioReal-timeWebSocket iletisiminpm i socket.io
bullQueueRedis-based is kuyrugunpm i bull
cronZamanlamaCron job zamanlayicinpm i cron
axiosHTTPHTTP client (server-side requests)npm i axios
uuidUtilityUnique ID oluşturmanpm i uuid
dayjsTarihTarih/saat islemleri (hafif)npm i dayjs
lodashUtilityGenel araclar (pick, merge, etc.)npm i lodash
swagger-ui-expressDokumantasyonAPI dokumanasyonunpm i swagger-ui-express
express-validatorValidationExpress için validationnpm i express-validator
cookie-parserUtilityCookie parse etmenpm i cookie-parser
hppGüvenlikHTTP Parameter Pollution korumasinpm i hpp
express-mongo-sanitizeGüvenlikNoSQL injection korumasinpm i express-mongo-sanitize
chokidarUtilityDosya sistemi izlemenpm i chokidar
connect-redisSessionRedis session storenpm i connect-redis
wsReal-timeHafif WebSocket kutuphanesinpm i ws
vitestTestHızlı test frameworknpm i -D vitest

Tipik Proje Kurulusu

bash
# Yeni Express API projesi baslangic paketi
npm init -y
npm install express cors helmet morgan compression dotenv \
  jsonwebtoken bcryptjs zod mongoose cookie-parser
npm install -D nodemon jest supertest eslint prettier

21) Hızlı Referans

npm Komutları Tablosu

KomutAçıklama
npm init -yYeni proje baslat (varsayılan)
npm installTüm bagimliliklar kur
npm install <pkg>Paket ekle (dependencies)
npm install -D <pkg>Dev dependency ekle
npm install -g <pkg>Global paket kur
npm uninstall <pkg>Paket kaldir
npm updatePaketleri güncelle
npm outdatedGuncellenecek paketleri gor
npm auditGüvenlik taramasi
npm audit fixGüvenlik sorunlarini duzelt
npm run <script>Script çalıştır
npm testTest çalıştır
npm startUygulamayi baslat
npx <cmd>Paket kurmadan çalıştır
npm list --depth=0Kurulu paketleri listele
npm cache clean --forceCache temizle
npm packTarball oluştur
npm version patchPatch sürüm artir
npm version minorMinor sürüm artir
npm publishnpm'e yayinla

Node.js Yerlesik Global Nesneler

Nesne / FonksiyonAçıklama
__dirnameMevcut dosyanin klasor yolu (CJS)
__filenameMevcut dosyanin tam yolu (CJS)
process.envEnvironment degiskenleri
process.argvKomut satiri argumanlari
process.cwd()Calisma dizini
process.exit(code)Uygulamayi sonlandir
process.pidİşlem ID
process.memoryUsage()Bellek kullanimi
Buffer.from(str)Binary data islemleri
setTimeout / setIntervalZamanlayicilar
setImmediateEvent loop'un sonraki check fazinda çalıştır
console.time(label)Performans olcumu baslat
console.timeEnd(label)Performans olcumu bitir
console.table(arr)Tablo formatinda log
structuredClone(obj)Derin kopya (deep clone)

HTTP Durum Kodlari Referansi

KodAnlamKullanım
200OKBasarili GET/PUT
201CreatedBasarili POST (yeni kaynak)
204No ContentBasarili DELETE
301Moved PermanentlyKalici yonlendirme
304Not ModifiedCache kullan
400Bad RequestValidation hatasi
401UnauthorizedGiris yapilmamis
403ForbiddenYetki yetersiz
404Not FoundKaynak bulunamadi
409ConflictCakisma (duplicate)
422Unprocessable EntityIsleme alinabilir ama mantiksal hata
429Too Many RequestsRate limit asildi
500Internal Server ErrorSunucu hatasi
502Bad GatewayUpstream sunucu hatasi
503Service UnavailableServis kullanım disi

Express Middleware Sirasi (Önerilen)

javascript
// 1. Guvenlik
app.use(helmet());
app.use(cors(corsOptions));
app.use(hpp());
app.use(mongoSanitize());

// 2. Rate Limiting
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));

// 3. Loglama
app.use(morgan("combined"));

// 4. Body Parsing
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());

// 5. Sikistirma
app.use(compression());

// 6. Static Dosyalar
app.use(express.static("public"));

// 7. Route'lar
app.use("/api", routes);

// 8. 404 Handler
app.use((req, res) => {
  res.status(404).json({ error: "Not Found" });
});

// 9. Global Error Handler (EN SON)
app.use(errorHandler);

Faydali Tek Satirlar

javascript
// Port'u al veya varsayilan kullan
const PORT = process.env.PORT || 3000;

// Async hatalari yakala (Express 5 oncesi)
const wrap = (fn) => (req, res, next) => fn(req, res, next).catch(next);

// Benzersiz ID olustur (uuid olmadan)
const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2);

// Object'ten bos alanlari temizle
const clean = (obj) => Object.fromEntries(Object.entries(obj).filter(([, v]) => v != null));

// ms'yi okunabilir formata cevir
const ms = (ms) => `${(ms / 1000).toFixed(2)}s`;

// Basit retry mekanizmasi
async function retry(fn, retries = 3, delay = 1000) {
  for (let i = 0; i < retries; i++) {
    try { return await fn(); }
    catch (err) {
      if (i === retries - 1) throw err;
      await new Promise((r) => setTimeout(r, delay * (i + 1)));
    }
  }
}

// Sleep utility
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

Laravel -> Node.js Kavram Eslestirmesi

LaravelNode.js / Express
php artisan servenpm run dev (nodemon)
Route::get()app.get() / router.get()
Middlewareapp.use() / route middleware
ControllerController dosyaları (ayni mantik)
Eloquent ORMMongoose / Prisma / Sequelize
MigrationPrisma migrate / Knex migrate
FormRequestZod / Joi validation middleware
.env.env + dotenv paketi (ayni!)
php artisan tinkernode REPL
Blade templateEJS / Pug (veya React SPA)
Laravel Mix / ViteVite / Webpack
Queue (Redis)Bull / BullMQ
Schedulernode-cron / cron paketi
Sanctum / PassportJWT + bcrypt / Passport.js
Storage facadefs modulu + multer
Composernpm / yarn / pnpm
composer.jsonpackage.json
vendor/node_modules/
PHPUnitJest + Supertest

Son Soz: Node.js, Laravel bilen bir gelistirici için cok tanidik kavramlar sunar. En büyük fark: JavaScript'in asenkron dogasi ve callback/promise yapisi. async/await kullan, hata yonetimini ihmal etme, güvenlik middleware'lerini uygula. Basarilar!


Ilgili Rehberler

Backend

Diger Kategoriler

Developer Guides & Technical References