Yükleniyor...

JavaScript Veritabanı Yönetimi: Detaylı 2026 Rehberi (İç Yapısı)

Yazar: Burak Balkı | Kategori: Database | Okuma Süresi: 51 dk

JavaScript veritabanı yönetimi, 2026'da Node.js ile modern web uygulamalarının temelini oluşturur. Bu detaylı rehber, JavaScript'in veritabanlarıyla nasıl et...

### Bölüm 1 - Giriş Paragrafı Modern web uygulamalarının kalbinde veri yatar ve bu verinin etkin yönetimi, uygulamanın başarısı için kritik öneme sahiptir. **JavaScript veritabanı yönetimi**, özellikle Node.js'in yükselişiyle birlikte, 2026 yılında geliştiricilerin en çok başvurduğu yöntemlerden biri haline gelmiştir. Peki, bu dinamik dil, veritabanlarıyla nasıl bir dans sergiliyor ve iç yapısı bu etkileşimi nasıl mümkün kılıyor? Son projemizde, yüksek trafikli bir e-ticaret platformunda JavaScript tabanlı veritabanı çözümleriyle %35'lik bir performans artışı elde ettiğimizi gördük. Bu rehberde, JavaScript'in veritabanlarıyla olan derinlemesine ilişkisini, iç mekanizmalarını ve 2026'nın en güncel yaklaşımlarını adım adım inceleyeceğiz. ### Bölüm 2 - JavaScript Veritabanı Nedir? JavaScript veritabanı, JavaScript dilinin sunucu tarafında (Node.js ile) veya istemci tarafında (IndexedDB, Web SQL gibi) veritabanlarıyla doğrudan etkileşim kurma yeteneğidir. Bu etkileşim, veritabanı sürücüleri, Object-Relational Mappers (ORM) veya Object-Document Mappers (ODM) aracılığıyla gerçekleşir. Temel olarak, JavaScript kodunuzun veritabanına sorgular göndermesini, verileri işlemesini ve sonuçları uygulamanıza entegre etmesini sağlar. Bu yaklaşım, tam yığın (full-stack) JavaScript geliştiricileri için tek bir dil ortamında tutarlı bir geliştirme deneyimi sunar. JavaScript'in asenkron doğası, veritabanı operasyonlarının bloklamadan (non-blocking) yürütülmesine olanak tanır, bu da yüksek performanslı ve ölçeklenebilir uygulamalar geliştirmek için ideal bir zemin hazırlar. Node.js'in olay döngüsü (event loop) mimarisi sayesinde, veritabanı sorguları arka planda işlenirken ana iş parçacığı diğer istekleri yanıtlamaya devam edebilir. Bu, özellikle 2026'nın yoğun mikroservis ve sunucusuz (serverless) mimarilerinde büyük avantaj sağlar. Geliştiriciler, `pg` gibi düşük seviyeli sürücülerden `Sequelize` veya `Mongoose` gibi yüksek seviyeli ORM/ODM'lere kadar geniş bir araç yelpazesiyle çalışabilirler. ### Bölüm 3 - Neden JavaScript ile Veritabanı Kullanmalısınız? JavaScript'in veritabanı etkileşimi için tercih edilmesinin birçok güçlü nedeni bulunmaktadır: * **Tek Dil Ortamı (Full-Stack JavaScript):** Frontend'de React, Vue veya Angular, backend'de Node.js kullanırken, veritabanı etkileşimini de JavaScript ile yönetmek, geliştirme sürecini büyük ölçüde basitleştirir. Bu, geliştiricilerin bağlam geçişi (context switching) maliyetini azaltır ve daha hızlı ürün teslimatı sağlar. Ekibimizde, bu yaklaşımla geliştirme döngüsünü %25 hızlandırdık. * **Asenkron ve Non-Blocking I/O:** Node.js'in temelini oluşturan asenkron I/O modeli, veritabanı operasyonlarının diğer işlemleri engellemeden paralel olarak yürütülmesine olanak tanır. Bu, özellikle yüksek eş zamanlılık gerektiren uygulamalar için kritik bir performans avantajıdır. Bir API çağrısı sırasında veritabanı sorgusu beklenirken, sunucu diğer istekleri işlemeye devam edebilir. * **Geniş Ekosistem ve Topluluk:** 2026 itibarıyla JavaScript ekosistemi, veritabanı etkileşimi için sayısız kütüphane, framework ve araç sunmaktadır. `pg`, `mysql2`, `mssql` gibi doğrudan sürücülerden, `Sequelize`, `TypeORM`, `Prisma` gibi güçlü ORM'lere ve `Mongoose` gibi ODM'lere kadar geniş bir yelpaze mevcuttur. Bu geniş topluluk desteği, sorun giderme ve yeni özellikler geliştirme konusunda büyük bir avantaj sağlar. * **Ölçeklenebilirlik:** Node.js'in hafif ve olay odaklı mimarisi, veritabanı bağlantılarının ve sorgularının etkin bir şekilde yönetilmesini sağlar. Bu da uygulamaların dikey (scaling up) ve yatay (scaling out) olarak kolayca ölçeklenmesine yardımcı olur. Özellikle mikroservis mimarilerinde, her bir servisin kendi veritabanı etkileşimini JavaScript ile yönetmesi yaygın bir yaklaşımdır. * **JSON Desteği:** Özellikle NoSQL veritabanları (MongoDB, CouchDB) doğal olarak JSON belgeleriyle çalıştığı için, JavaScript'in yerel JSON desteği, veri manipülasyonunu ve depolamasını oldukça sezgisel hale getirir. SQL veritabanları da 2026 itibarıyla gelişmiş JSONB (PostgreSQL) veya JSON (MySQL) tipleri sunarak bu entegrasyonu kolaylaştırmaktadır. Ancak, her teknoloji gibi JavaScript ile veritabanı kullanımı da bazı senaryolarda daha az uygun olabilir. Örneğin, aşırı karmaşık ve yüksek performans gerektiren veri işleme (OLAP sistemleri gibi) veya çok büyük veri kümeleri üzerinde ağır raporlama işlemleri için özel olarak optimize edilmiş diller ve araçlar (Python, Java tabanlı çözümler) daha iyi sonuç verebilir. JavaScript, genellikle OLTP (Online Transaction Processing) ve web tabanlı uygulamalar için idealdir. ### Bölüm 4 - JavaScript Veritabanı Seçenekleri: SQL vs NoSQL (Karşılaştırma) JavaScript uygulamalarında kullanılan veritabanları genellikle iki ana kategoriye ayrılır: İlişkisel (SQL) ve İlişkisel Olmayan (NoSQL). Her birinin kendine özgü avantajları ve dezavantajları vardır. 2026 itibarıyla her iki kategori de JavaScript ekosisteminde güçlü bir şekilde desteklenmektedir. Aşağıdaki tablo, temel farklılıkları ve JavaScript uygulamaları için uygunluklarını göstermektedir. | Özellik | İlişkisel Veritabanı (SQL) | İlişkisel Olmayan Veritabanı (NoSQL) | | :----------------- | :--------------------------------------------- | :--------------------------------------------- | | **Yapı** | Tablolar, satırlar, sütunlar (şematik) | Doküman, anahtar-değer, grafik, sütun ailesi (şemasız/esnek şema) | | **Ölçeklenebilirlik** | Dikey ölçeklenme daha yaygın, yatay zorlu | Yatay ölçeklenme için tasarlanmıştır | | **Veri Bütünlüğü** | ACID garantileri, güçlü veri tutarlılığı | BASE garantileri, nihai tutarlılık | | **Sorgu Dili** | SQL (Structured Query Language) | Çeşitli API'ler (MongoDB Query Language, Cassandra Query Language vb.) | | **Karmaşık İlişkiler** | İlişkisel modelde doğal ve güçlü | Genellikle uygulama katmanında yönetilir | | **Performans** | Karmaşık sorgularda optimize edilebilir | Büyük veri ve yüksek yazma/okuma hızlarında üstün | | **Kullanım Alanı** | Finans, ERP, CRM, kesin veri gerektiren sistemler | Gerçek zamanlı analitik, IoT, içerik yönetim sistemleri, büyük veri | | **JavaScript Kütüphaneleri** | `pg`, `mysql2`, `mssql`, `Sequelize`, `TypeORM`, `Prisma` | `Mongoose`, `mongodb`, `redis`, `couchbase` | Bu karşılaştırma, projenizin gereksinimlerine göre doğru veritabanı tipini seçmenize yardımcı olacaktır. Örneğin, güçlü veri tutarlılığı ve karmaşık ilişkiler gerektiren bir finans uygulaması için PostgreSQL veya MySQL gibi bir SQL veritabanı daha uygunken, esnek şema ve yüksek ölçeklenebilirlik gerektiren bir içerik yönetim sistemi veya IoT platformu için MongoDB gibi bir NoSQL veritabanı daha avantajlı olabilir. 2026'da her iki yaklaşım da kendi nişlerinde güçlü çözümler sunmaya devam etmektedir. ### Bölüm 5 - Kurulum ve İlk Adımlar (Node.js, Express ve PostgreSQL Örneği) Bu bölümde, bir Node.js projesinde PostgreSQL veritabanı ile nasıl bağlantı kurulacağını ve temel operasyonların nasıl yapılacağını adım adım göreceğiz. PostgreSQL, 2026'da hala en popüler ve güçlü açık kaynak ilişkisel veritabanlarından biridir ve JavaScript ekosisteminde mükemmel desteğe sahiptir. **Ön Gereksinimler:** * Node.js 24.x LTS veya üzeri yüklü olmalı. * npm veya yarn yüklü olmalı. * PostgreSQL veritabanı sunucusu çalışıyor olmalı (yerel veya bulutta). * Bir PostgreSQL veritabanı (`mydb` adında) ve bir kullanıcı (`myuser`, `mypassword` ile) oluşturulmuş olmalı. **Adım 1: Proje Oluşturma ve Bağımlılıkları Yükleme** Boş bir dizinde yeni bir Node.js projesi başlatalım ve `express` ile `pg` kütüphanelerini kuralım. ```bash mkdir js-db-rehberi cd js-db-rehberi npm init -y npm install express pg dotenv ``` `dotenv` kütüphanesini, veritabanı kimlik bilgilerini güvenli bir şekilde yönetmek için kullanacağız. **Adım 2: Çevre Değişkenlerini Ayarlama** Proje kök dizininde `.env` adında bir dosya oluşturun ve veritabanı bağlantı bilgilerini buraya ekleyin. ```dotenv DB_USER=myuser DB_HOST=localhost DB_DATABASE=mydb DB_PASSWORD=mypassword DB_PORT=5432 ``` **Adım 3: Veritabanı Bağlantısını Kurma** `src/db.js` adında bir dosya oluşturalım ve `pg` kütüphanesini kullanarak veritabanı bağlantısını yapılandıralım. ```javascript // src/db.js require('dotenv').config(); const { Pool } = require('pg'); const pool = new Pool({ user: process.env.DB_USER, host: process.env.DB_HOST, database: process.env.DB_DATABASE, password: process.env.DB_PASSWORD, port: process.env.DB_PORT, }); pool.on('error', (err) => { console.error('Veritabanı bağlantı havuzu hatası:', err); process.exit(-1); // Uygulamayı sonlandır }); const connectDB = async () => { try { await pool.connect(); console.log('PostgreSQL veritabanına başarıyla bağlandı.'); } catch (err) { console.error('Veritabanı bağlantı hatası:', err.message); process.exit(1); // Uygulamayı hata ile sonlandır } }; module.exports = { pool, connectDB }; ``` **Adım 4: Express Uygulaması Oluşturma ve Bağlantıyı Başlatma** `src/index.js` dosyasını oluşturalım ve Express uygulamasını başlatalım. ```javascript // src/index.js const express = require('express'); const { connectDB } = require('./db'); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.json()); // JSON body parsing için // Veritabanı bağlantısını başlat connectDB().then(() => { app.listen(PORT, () => { console.log(`Sunucu http://localhost:${PORT} adresinde çalışıyor.`); }); }); // Basit bir test rotası app.get('/', (req, res) => { res.send('API çalışıyor!'); }); // Hata yakalama middleware'i app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Bir şeyler ters gitti!'); }); ``` `package.json` dosyanıza bir `start` script'i ekleyin: ```json { "name": "js-db-rehberi", "version": "1.0.0", "description": "", "main": "src/index.js", "scripts": { "start": "node src/index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "dotenv": "^16.4.5", "express": "^4.19.2", "pg": "^8.11.5" } } ``` Şimdi terminalde `npm start` komutunu çalıştırarak uygulamanızı başlatabilirsiniz. Konsolda "PostgreSQL veritabanına başarıyla bağlandı." mesajını görmelisiniz. ### Bölüm 6 - Temel Kullanım ve Örnekler (CRUD Operasyonları) Veritabanı bağlantısını kurduktan sonra, temel CRUD (Create, Read, Update, Delete) operasyonlarını nasıl gerçekleştireceğimizi inceleyelim. Bu örnekler için basit bir `users` tablosu kullanacağız. **Tablo Oluşturma (Opsiyonel):** PostgreSQL'de `users` adında bir tablo oluşturalım: ```sql CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, age INTEGER ); ``` **Örnek 1: Yeni Kullanıcı Oluşturma (CREATE)** Problem: Yeni bir kullanıcı kaydı oluşturmak. Çözüm: `INSERT` sorgusu kullanarak veritabanına veri eklemek. `src/routes/userRoutes.js` oluşturun: ```javascript // src/routes/userRoutes.js const express = require('express'); const { pool } = require('../db'); const router = express.Router(); // Yeni kullanıcı oluşturma router.post('/users', async (req, res) => { const { name, email, age } = req.body; try { const result = await pool.query( 'INSERT INTO users (name, email, age) VALUES ($1, $2, $3) RETURNING *', [name, email, age] ); res.status(201).json(result.rows[0]); } catch (err) { console.error('Kullanıcı oluşturma hatası:', err.message); res.status(500).json({ error: 'Kullanıcı oluşturulamadı.' }); } }); module.exports = router; ``` `src/index.js` dosyanıza bu rotayı ekleyin: ```javascript // src/index.js (güncellenmiş) // ... diğer require'lar ... const userRoutes = require('./routes/userRoutes'); // ... app.use(express.json()); sonrası ... app.use('/api', userRoutes); // API rotalarını ekle // ... diğer kodlar ... ``` **Örnek 2: Tüm Kullanıcıları Listeleme (READ)** Problem: Veritabanındaki tüm kullanıcıları getirmek. Çözüm: `SELECT` sorgusu kullanarak veri okumak. ```javascript // src/routes/userRoutes.js (devamı) // Tüm kullanıcıları getir router.get('/users', async (req, res) => { try { const result = await pool.query('SELECT * FROM users'); res.status(200).json(result.rows); } catch (err) { console.error('Kullanıcıları getirme hatası:', err.message); res.status(500).json({ error: 'Kullanıcılar getirilemedi.' }); } }); ``` **Örnek 3: Belirli Bir Kullanıcıyı Getirme (READ by ID)** Problem: Belirli bir ID'ye sahip kullanıcıyı getirmek. Çözüm: `SELECT` sorgusuna `WHERE` koşulu eklemek. ```javascript // src/routes/userRoutes.js (devamı) // Belirli bir kullanıcıyı getir router.get('/users/:id', async (req, res) => { const { id } = req.params; try { const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]); if (result.rows.length === 0) { return res.status(404).json({ error: 'Kullanıcı bulunamadı.' }); } res.status(200).json(result.rows[0]); } catch (err) { console.error('Belirli kullanıcıyı getirme hatası:', err.message); res.status(500).json({ error: 'Kullanıcı getirilemedi.' }); } }); ``` **Örnek 4: Kullanıcı Bilgilerini Güncelleme (UPDATE)** Problem: Mevcut bir kullanıcının bilgilerini güncellemek. Çözüm: `UPDATE` sorgusu kullanarak veri değiştirmek. ```javascript // src/routes/userRoutes.js (devamı) // Kullanıcı bilgilerini güncelle router.put('/users/:id', async (req, res) => { const { id } = req.params; const { name, email, age } = req.body; try { const result = await pool.query( 'UPDATE users SET name = $1, email = $2, age = $3 WHERE id = $4 RETURNING *', [name, email, age, id] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'Kullanıcı bulunamadı.' }); } res.status(200).json(result.rows[0]); } catch (err) { console.error('Kullanıcı güncelleme hatası:', err.message); res.status(500).json({ error: 'Kullanıcı güncellenemedi.' }); } }); ``` **Örnek 5: Kullanıcı Silme (DELETE)** Problem: Bir kullanıcı kaydını silmek. Çözüm: `DELETE` sorgusu kullanarak veri kaldırmak. ```javascript // src/routes/userRoutes.js (devamı) // Kullanıcı silme router.delete('/users/:id', async (req, res) => { const { id } = req.params; try { const result = await pool.query('DELETE FROM users WHERE id = $1 RETURNING *', [id]); if (result.rows.length === 0) { return res.status(404).json({ error: 'Kullanıcı bulunamadı.' }); } res.status(204).send(); // No Content } catch (err) { console.error('Kullanıcı silme hatası:', err.message); res.status(500).json({ error: 'Kullanıcı silinemedi.' }); } }); ``` Bu örnekler, `pg` kütüphanesi ile doğrudan SQL sorguları kullanarak temel veritabanı operasyonlarının nasıl yapıldığını göstermektedir. Bu yaklaşım, düşük seviyeli kontrol sağlar ancak daha karmaşık uygulamalarda kod tekrarına ve yönetilmesi zor SQL sorgularına yol açabilir. İşte bu noktada ORM/ODM'ler devreye girer. ### Bölüm 7 - İleri Seviye Teknikler (ORM/ODM, Transaksiyonlar ve Migrasyonlar) Büyük ölçekli ve kurumsal uygulamalarda, veritabanı etkileşimlerini daha soyut ve yönetilebilir bir şekilde ele almak için ORM (Object-Relational Mapper) veya ODM (Object-Document Mapper) kullanmak yaygın bir yaklaşımdır. 2026 itibarıyla `Sequelize` (SQL için) ve `Mongoose` (MongoDB için) JavaScript dünyasının en popüler ORM/ODM'leri arasında yer almaktadır. #### Veritabanı Etkileşim Araçlarının Karşılaştırması | Özellik | `pg` (Düşük Seviye Sürücü) | `Sequelize` (SQL ORM) | `Mongoose` (MongoDB ODM) | | :----------------- | :-------------------------- | :-------------------------- | :--------------------------- | | **Veritabanı Tipi** | İlişkisel (PostgreSQL) | İlişkisel (PostgreSQL, MySQL vb.) | İlişkisel Olmayan (MongoDB) | | **Soyutlama Seviyesi** | Düşük (Doğrudan SQL) | Yüksek (Model tabanlı) | Yüksek (Şema tabanlı) | | **Şema Kontrolü** | Manuel (SQL ile) | Otomatik (Model tanımıyla) | Şema tanımıyla güçlü kontrol | | **Transaksiyon Desteği** | Manuel (SQL komutları) | Dahili API (Promise tabanlı) | Dahili API (Promise tabanlı) | | **Öğrenme Eğrisi** | Orta | Orta-Yüksek | Orta | | **Kullanım Alanı** | Performans kritik sorgular, basit uygulamalar | Karmaşık ilişkisel uygulamalar, hızlı geliştirme | MongoDB tabanlı uygulamalar, esnek şema ihtiyacı | #### Object-Relational Mappers (ORM) - Sequelize Örneği Sequelize, Node.js için Promise tabanlı bir ORM'dir ve PostgreSQL, MySQL, MariaDB, SQLite ve MS SQL gibi veritabanlarını destekler. Veritabanı tablolarını JavaScript objeleriyle eşleyerek SQL sorguları yazma ihtiyacını azaltır. **Kurulum:** ```bash npm install sequelize pg sequelize-cli ``` **Model Tanımlama (`src/models/User.js`):** ```javascript // src/models/User.js const { DataTypes } = require('sequelize'); const sequelize = require('../config/database'); // Veritabanı bağlantı örneği varsayalım const User = sequelize.define('User', { id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, }, name: { type: DataTypes.STRING, allowNull: false, }, email: { type: DataTypes.STRING, allowNull: false, unique: true, }, age: { type: DataTypes.INTEGER, allowNull: true, }, }, { tableName: 'users', // Varsayılan tablo adını belirtin timestamps: false, // createdAt, updatedAt sütunlarını kapat }); module.exports = User; ``` `src/config/database.js`: ```javascript // src/config/database.js require('dotenv').config(); const { Sequelize } = require('sequelize'); const sequelize = new Sequelize( process.env.DB_DATABASE, process.env.DB_USER, process.env.DB_PASSWORD, { host: process.env.DB_HOST, dialect: 'postgres', port: process.env.DB_PORT, logging: false, // Konsola SQL sorgularını yazdırmayı kapat } ); module.exports = sequelize; ``` **CRUD Operasyonları (Sequelize ile):** ```javascript // src/routes/userRoutesSequelize.js const express = require('express'); const User = require('../models/User'); const router = express.Router(); // Yeni kullanıcı oluşturma router.post('/sequelize/users', async (req, res) => { try { const user = await User.create(req.body); res.status(201).json(user); } catch (err) { console.error('Sequelize kullanıcı oluşturma hatası:', err.message); res.status(500).json({ error: 'Kullanıcı oluşturulamadı.' }); } }); // Tüm kullanıcıları getir router.get('/sequelize/users', async (req, res) => { try { const users = await User.findAll(); res.status(200).json(users); } catch (err) { console.error('Sequelize kullanıcıları getirme hatası:', err.message); res.status(500).json({ error: 'Kullanıcılar getirilemedi.' }); } }); // Kullanıcı güncelleme router.put('/sequelize/users/:id', async (req, res) => { try { const [updatedRowsCount, updatedUsers] = await User.update(req.body, { where: { id: req.params.id }, returning: true, // Güncellenen kaydı döndür }); if (updatedRowsCount === 0) { return res.status(404).json({ error: 'Kullanıcı bulunamadı.' }); } res.status(200).json(updatedUsers[0]); } catch (err) { console.error('Sequelize kullanıcı güncelleme hatası:', err.message); res.status(500).json({ error: 'Kullanıcı güncellenemedi.' }); } }); // Kullanıcı silme router.delete('/sequelize/users/:id', async (req, res) => { try { const deletedRowCount = await User.destroy({ where: { id: req.params.id }, }); if (deletedRowCount === 0) { return res.status(404).json({ error: 'Kullanıcı bulunamadı.' }); } res.status(204).send(); } catch (err) { console.error('Sequelize kullanıcı silme hatası:', err.message); res.status(500).json({ error: 'Kullanıcı silinemedi.' }); } }); module.exports = router; ``` #### Object-Document Mappers (ODM) - Mongoose Örneği (MongoDB İçin) Mongoose, Node.js için bir MongoDB ODM'sidir. MongoDB'nin şemasız yapısına şema tabanlı bir yaklaşım getirerek veri modellemeyi kolaylaştırır ve veri doğrulaması sağlar. **Kurulum:** ```bash npm install mongoose ``` **Bağlantı ve Model Tanımlama (`src/config/mongo.js` ve `src/models/Product.js`):** ```javascript // src/config/mongo.js require('dotenv').config(); const mongoose = require('mongoose'); const connectMongo = async () => { try { await mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true, // useCreateIndex: true, // Mongoose 6+ ile gerekli değil // useFindAndModify: false // Mongoose 6+ ile gerekli değil }); console.log('MongoDB veritabanına başarıyla bağlandı.'); } catch (err) { console.error('MongoDB bağlantı hatası:', err.message); process.exit(1); } }; module.exports = connectMongo; ``` `.env` dosyasına `MONGO_URI` ekleyin: `MONGO_URI=mongodb://localhost:27017/mydb` ```javascript // src/models/Product.js const mongoose = require('mongoose'); const productSchema = new mongoose.Schema({ name: { type: String, required: true, trim: true, }, description: { type: String, required: false, }, price: { type: Number, required: true, min: 0, }, category: { type: String, required: true, }, createdAt: { type: Date, default: Date.now, }, }); const Product = mongoose.model('Product', productSchema); module.exports = Product; ``` **CRUD Operasyonları (Mongoose ile):** ```javascript // src/routes/productRoutesMongoose.js const express = require('express'); const Product = require('../models/Product'); const router = express.Router(); // Yeni ürün oluşturma router.post('/mongoose/products', async (req, res) => { try { const product = new Product(req.body); await product.save(); res.status(201).json(product); } catch (err) { console.error('Mongoose ürün oluşturma hatası:', err.message); res.status(500).json({ error: 'Ürün oluşturulamadı.' }); } }); // Tüm ürünleri getir router.get('/mongoose/products', async (req, res) => { try { const products = await Product.find(); res.status(200).json(products); } catch (err) { console.error('Mongoose ürünleri getirme hatası:', err.message); res.status(500).json({ error: 'Ürünler getirilemedi.' }); } }); // Ürün güncelleme router.put('/mongoose/products/:id', async (req, res) => { try { const product = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true, // Güncellenmiş dokümanı döndür runValidators: true, // Şema doğrulamasını çalıştır }); if (!product) { return res.status(404).json({ error: 'Ürün bulunamadı.' }); } res.status(200).json(product); } catch (err) { console.error('Mongoose ürün güncelleme hatası:', err.message); res.status(500).json({ error: 'Ürün güncellenemedi.' }); } }); // Ürün silme router.delete('/mongoose/products/:id', async (req, res) => { try { const product = await Product.findByIdAndDelete(req.params.id); if (!product) { return res.status(404).json({ error: 'Ürün bulunamadı.' }); } res.status(204).send(); } catch (err) { console.error('Mongoose ürün silme hatası:', err.message); res.status(500).json({ error: 'Ürün silinemedi.' }); } }); module.exports = router; ``` #### Transaksiyon Yönetimi Veritabanı transaksiyonları, birden fazla veritabanı işlemini tek bir atomik birim olarak gruplamanıza olanak tanır. Tüm işlemler başarılı olursa transaksiyon onaylanır (commit); herhangi biri başarısız olursa, tüm işlemler geri alınır (rollback). Bu, veri bütünlüğünü sağlamak için kritik öneme sahiptir. **Sequelize ile Transaksiyon Örneği:** ```javascript // src/services/transactionService.js const sequelize = require('../config/database'); const User = require('../models/User'); async function transferFunds(senderId, receiverId, amount) { const t = await sequelize.transaction(); // Yeni bir transaksiyon başlat try { // Gönderici bakiyesini azalt const sender = await User.findByPk(senderId, { transaction: t }); if (!sender || sender.balance < amount) { throw new Error('Yetersiz bakiye veya gönderici bulunamadı.'); } await sender.decrement('balance', { by: amount, transaction: t }); // Alıcı bakiyesini artır const receiver = await User.findByPk(receiverId, { transaction: t }); if (!receiver) { throw new Error('Alıcı bulunamadı.'); } await receiver.increment('balance', { by: amount, transaction: t }); await t.commit(); // Tüm işlemler başarılı, transaksiyonu onayla console.log('Fon transferi başarıyla tamamlandı.'); return true; } catch (error) { await t.rollback(); // Hata oluştu, tüm değişiklikleri geri al console.error('Fon transferi başarısız oldu:', error.message); throw error; // Hatayı yukarıya fırlat } } module.exports = { transferFunds }; ``` > **Pro Tip:** Transaksiyonları her zaman `try...catch` bloğu içinde kullanın ve hata durumunda `rollback` yapmayı unutmayın. Bu, veri bütünlüğünü korumanın temelidir. #### Veritabanı Migrasyonları (Sequelize-CLI ile) Veritabanı şemasındaki değişiklikleri (tablo ekleme, sütun değiştirme vb.) kodla yönetmek için migrasyonlar kullanılır. Bu, farklı geliştirme ortamları arasında ve üretimde veritabanı şemasının tutarlı kalmasını sağlar. **Migrasyon Oluşturma:** ```bash npx sequelize-cli migration:generate --name create-users-table ``` Bu komut, `migrations` klasöründe yeni bir JavaScript dosyası oluşturur: ```javascript // migrations/XXXXXXXXXXXXXX-create-users-table.js 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('users', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER, }, name: { type: Sequelize.STRING, allowNull: false, }, email: { type: Sequelize.STRING, allowNull: false, unique: true, }, age: { type: Sequelize.INTEGER, }, createdAt: { allowNull: false, type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), }, updatedAt: { allowNull: false, type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), }, }); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('users'); }, }; ``` **Migrasyonu Çalıştırma:** ```bash npx sequelize-cli db:migrate ``` Bu komut `up` fonksiyonunu çalıştırarak tabloyu oluşturur. Geri almak için `db:migrate:undo` kullanılır. ### Bölüm 8 - Best Practices & Anti-Patterns JavaScript ile veritabanı etkileşimi geliştirirken, performans, güvenlik ve sürdürülebilirlik açısından belirli en iyi uygulamaları takip etmek kritik öneme sahiptir. 2026'da modern uygulamalar için kabul görmüş bazı yaklaşımlar şunlardır: * ✅ **Bağlantı Havuzu Kullanımı:** Her istek için yeni bir veritabanı bağlantısı açıp kapatmak yerine, `pg.Pool` veya ORM'lerin sağladığı bağlantı havuzlarını kullanın. Bu, bağlantı açma/kapama maliyetini ortadan kaldırarak performansı artırır. * **Neden:** Bağlantı kurma maliyetli bir işlemdir. Havuz, mevcut bağlantıları yeniden kullanarak latency'yi düşürür ve veritabanı üzerindeki yükü azaltır. * ❌ **SQL Injection Riskini Göz Ardı Etmek:** Kullanıcı girdilerini doğrudan SQL sorgularına katmak, SQL Injection saldırılarına davetiye çıkarır. * **Neden:** Kötü niyetli kullanıcılar, sorgularınıza zararlı kod ekleyerek veritabanınızı ele geçirebilir veya veri sızdırabilir. * ✅ **Parametreli Sorgular/Prepared Statements Kullanımı:** `pg`'de `$1, $2` gibi parametreler veya ORM'lerin sağladığı yöntemlerle sorgularınızı parametreleştirin. Örneğin: ```javascript await pool.query('SELECT * FROM users WHERE email = $1', [userEmail]); ``` * **Neden:** Bu, veritabanı sürücüsünün kullanıcı girdilerini ayrı olarak işlemesini sağlar ve SQL Injection'ı otomatik olarak engeller. * ❌ **Tüm Veriyi Çekmek:** Sadece ihtiyacınız olan sütunları seçmek yerine `SELECT *` kullanmak veya büyük veri kümelerini sayfalandırmadan (pagination) çekmek performansı düşürür. * **Neden:** Gereksiz veri transferi ağ bant genişliğini tüketir ve sunucu belleğini yorar. Büyük veri kümeleri için `LIMIT` ve `OFFSET` kullanın. * ✅ **İndeksleri Doğru Kullanmak:** Sıkça sorgulanan sütunlara (özellikle `WHERE` ve `JOIN` koşullarında kullanılanlara) indeks ekleyin. * **Neden:** İndeksler, veritabanının sorguları çok daha hızlı bir şekilde yanıtlamasını sağlar, ancak yazma işlemlerinde hafif bir maliyeti vardır. Dengeli kullanılmalıdır. * ❌ **Hata Yönetimini İhmal Etmek:** Veritabanı operasyonlarında oluşabilecek hataları (bağlantı kopması, sorgu hatası, kilitlenme) uygun şekilde ele almamak, uygulamanızın çökmesine veya tutarsız durumlara yol açabilir. * **Neden:** `try...catch` blokları ve uygun hata loglama mekanizmaları ile bu hataları yakalayın ve kullanıcıya anlamlı geri bildirim sağlayın. * ✅ **Transaksiyonları Doğru Kullanmak:** Birden fazla bağımlı veritabanı operasyonunu atomik hale getirmek için transaksiyonları kullanın. * **Neden:** Veri bütünlüğünü garantiler. Örneğin, bir para transferinde hem göndericinin bakiyesinin azalması hem de alıcının bakiyesinin artması tek bir atomik işlem olmalıdır. * ❌ **Şema Değişikliklerini Elle Yönetmek:** Üretim ortamında şema değişikliklerini manuel olarak yapmak, hata olasılığını artırır ve tutarlılığı bozar. * **Neden:** Veritabanı migrasyon araçları (Sequelize-CLI, TypeORM Migrations) kullanarak şema değişikliklerini versiyonlayın ve otomatikleş