[NODE JS] SQLite ile Hafif Veritabanı Kullanımı
SQLite, benim küçük ve orta ölçekli projelerimde en sık tercih ettiğim veritabanıdır. Neden mi? Çünkü kurulum gerektirmez, sunucu çalıştırmaya ihtiyaç duymaz ve tek bir dosyada tüm veritabanını barındırır. Prototip geliştirme, masaüstü uygulamalar, IoT cihazları ve küçük web uygulamaları için mükemmel bir çözüm. Hatta birçok büyük şirket de üretim ortamında SQLite kullanıyor — mesela WhatsApp, her kullanıcının mesaj geçmişini SQLite veritabanında saklıyor.
Node.js ile SQLite kullanmak için birkaç farklı kütüphane mevcut. En popülerleri better-sqlite3 ve sqlite3 modülleridir. better-sqlite3, senkron API sunması ve daha yüksek performansıyla öne çıkar. sqlite3 ise asenkron yapısıyla geleneksel Node.js yaklaşımına daha uygundur. Ben genellikle better-sqlite3'ü tercih ediyorum çünkü kullanımı daha basit ve hataları yönetmek daha kolay.
SQLite'ın en büyük avantajlarından biri taşınabilirlik. Veritabanı dosyasını kopyalamak, yedeklemek veya başka bir ortama taşımak son derece kolay. Bu özellik özellikle geliştirme ve test aşamalarında çok değerli. Ayrıca SQLite, ACID uyumlu bir veritabanıdır, yani veri bütünlüğü konusunda güvenilirdir.
SQLite'ta tablo oluşturma, SQL standartlarına uygun şekilde yapılır. CREATE TABLE ifadesiyle tablolarımızı tanımlarız. INTEGER PRIMARY KEY AUTOINCREMENT ile otomatik artan birincil anahtar, NOT NULL ile zorunlu alanlar, UNIQUE ile benzersiz alanlar, DEFAULT ile varsayılan değerler tanımlayabiliriz. Ayrıca FOREIGN KEY kısıtlamalarıyla tablolar arası ilişkiler kurabiliriz.
Performans optimizasyonu için WAL (Write-Ahead Logging) modu çok önemli. Bu mod, okuma ve yazma işlemlerinin eş zamanlı olarak gerçekleşmesine olanak tanır. PRAGMA journal_mode=WAL komutuyla bu modu etkinleştirebiliriz. Ayrıca PRAGMA foreign_keys=ON ile yabancı anahtar kısıtlamalarını aktif hale getirmemiz gerekir çünkü SQLite'ta bu özellik varsayılan olarak kapalıdır.
İndeksleme de SQLite'ta performans için kritik. Sık sorgulanan alanlara indeks eklemek sorgu hızını büyük ölçüde artırır. CREATE INDEX ifadesiyle indeksler oluşturulur. Bileşik indeksler, birden fazla alanı kapsayan sorgular için oldukça faydalıdır. Ancak gereksiz indekslerden kaçınmak gerekir çünkü her indeks yazma işlemlerini yavaşlatır.
Transaction (işlem) yönetimi de SQLite'ta önemli bir konudur. Birden fazla yazma işlemini tek bir transaction içinde gruplamak hem performansı artırır hem de veri bütünlüğünü sağlar. better-sqlite3'te transaction() metodu ile bu işlemleri kolayca yönetebiliriz.
JavaScript:
// SQLite ile Hafif Veritabanı Kullanımı - better-sqlite3
const Database = require('better-sqlite3');
const path = require('path');
// Veritabanı oluşturma ve yapılandırma
const db = new Database(path.join(__dirname, 'uygulama.db'), {
verbose: console.log // SQL sorgularını konsola yazdır
});
// Performans ayarları
db.pragma('journal_mode = WAL'); // Eş zamanlı okuma/yazma
db.pragma('foreign_keys = ON'); // Yabancı anahtar kontrolü
db.pragma('synchronous = NORMAL'); // Dengeli performans/güvenlik
// Tabloları oluşturma
function tablolariOlustur() {
db.exec(`
CREATE TABLE IF NOT EXISTS kategoriler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ad TEXT NOT NULL UNIQUE,
aciklama TEXT,
olusturma_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS urunler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ad TEXT NOT NULL,
aciklama TEXT,
fiyat REAL NOT NULL CHECK(fiyat >= 0),
stok INTEGER NOT NULL DEFAULT 0 CHECK(stok >= 0),
kategori_id INTEGER,
aktif INTEGER DEFAULT 1,
olusturma_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
guncelleme_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (kategori_id) REFERENCES kategoriler(id)
ON DELETE SET NULL ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS musteriler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ad TEXT NOT NULL,
soyad TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
telefon TEXT,
adres TEXT,
kayit_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS siparisler (
id INTEGER PRIMARY KEY AUTOINCREMENT,
musteri_id INTEGER NOT NULL,
toplam_tutar REAL NOT NULL DEFAULT 0,
durum TEXT NOT NULL DEFAULT 'beklemede'
CHECK(durum IN ('beklemede', 'hazirlaniyor', 'kargoda', 'teslim_edildi', 'iptal')),
siparis_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (musteri_id) REFERENCES musteriler(id)
);
CREATE TABLE IF NOT EXISTS siparis_kalemleri (
id INTEGER PRIMARY KEY AUTOINCREMENT,
siparis_id INTEGER NOT NULL,
urun_id INTEGER NOT NULL,
adet INTEGER NOT NULL CHECK(adet > 0),
birim_fiyat REAL NOT NULL,
FOREIGN KEY (siparis_id) REFERENCES siparisler(id)
ON DELETE CASCADE,
FOREIGN KEY (urun_id) REFERENCES urunler(id)
);
-- İndeksler
CREATE INDEX IF NOT EXISTS idx_urunler_kategori
ON urunler(kategori_id);
CREATE INDEX IF NOT EXISTS idx_urunler_fiyat
ON urunler(fiyat);
CREATE INDEX IF NOT EXISTS idx_siparisler_musteri
ON siparisler(musteri_id);
CREATE INDEX IF NOT EXISTS idx_siparisler_tarih
ON siparisler(siparis_tarihi);
`);
console.log('Tablolar ve indeksler oluşturuldu');
}
// Hazırlanmış ifadeler (prepared statements)
const ifadeler = {
kategoriEkle: db.prepare(
'INSERT INTO kategoriler (ad, aciklama) VALUES (?, ?)'
),
urunEkle: db.prepare(
'INSERT INTO urunler (ad, aciklama, fiyat, stok, kategori_id) VALUES (?, ?, ?, ?, ?)'
),
musteriEkle: db.prepare(
'INSERT INTO musteriler (ad, soyad, email, telefon) VALUES (?, ?, ?, ?)'
),
siparisEkle: db.prepare(
'INSERT INTO siparisler (musteri_id, toplam_tutar) VALUES (?, ?)'
),
kalemEkle: db.prepare(
'INSERT INTO siparis_kalemleri (siparis_id, urun_id, adet, birim_fiyat) VALUES (?, ?, ?, ?)'
),
stokGuncelle: db.prepare(
'UPDATE urunler SET stok = stok - ?, guncelleme_tarihi = CURRENT_TIMESTAMP WHERE id = ? AND stok >= ?'
)
};
// Transaction ile toplu veri ekleme
const topluKategoriEkle = db.transaction((kategoriler) => {
for (const kat of kategoriler) {
ifadeler.kategoriEkle.run(kat.ad, kat.aciklama);
}
return kategoriler.length;
});
// Örnek verileri ekleme
function ornekVerileriEkle() {
// Kategoriler
const eklenenKategori = topluKategoriEkle([
{ ad: 'Elektronik', aciklama: 'Elektronik ürünler ve aksesuarlar' },
{ ad: 'Giyim', aciklama: 'Erkek ve kadın giyim ürünleri' },
{ ad: 'Kitap', aciklama: 'Her türden kitaplar' },
{ ad: 'Spor', aciklama: 'Spor ekipmanları ve giysileri' }
]);
console.log(`${eklenenKategori} kategori eklendi`);
// Ürünler
const urunler = [
['Kablosuz Kulaklık', 'Bluetooth 5.0 destekli', 299.99, 50, 1],
['Akıllı Saat', 'Nabız ölçer özellikli', 899.99, 30, 1],
['Pamuklu Tişört', 'Organik pamuk', 149.99, 100, 2],
['Kot Pantolon', 'Slim fit kesim', 349.99, 75, 2],
['JavaScript Kitabı', 'Başlangıçtan ileri seviyeye', 89.99, 200, 3],
['Yoga Matı', 'Kaymaz yüzey', 199.99, 60, 4]
];
const topluUrunEkle = db.transaction((urunListesi) => {
for (const urun of urunListesi) {
ifadeler.urunEkle.run(...urun);
}
});
topluUrunEkle(urunler);
console.log(`${urunler.length} ürün eklendi`);
// Müşteriler
ifadeler.musteriEkle.run('Ali', 'Yıldız', 'ali@ornek.com', '5551234567');
ifadeler.musteriEkle.run('Ayşe', 'Korkmaz', 'ayse@ornek.com', '5559876543');
console.log('Müşteriler eklendi');
}
// Sipariş oluşturma (transaction ile)
const siparisOlustur = db.transaction((musteriId, kalemler) => {
let toplamTutar = 0;
// Her kalem için stok kontrolü yap
for (const kalem of kalemler) {
const urun = db.prepare('SELECT fiyat, stok FROM urunler WHERE id = ?').get(kalem.urunId);
if (!urun) throw new Error(`Ürün bulunamadı: ${kalem.urunId}`);
if (urun.stok < kalem.adet) throw new Error(`Yetersiz stok: Ürün ${kalem.urunId}`);
toplamTutar += urun.fiyat * kalem.adet;
}
// Siparişi oluştur
const siparisSonuc = ifadeler.siparisEkle.run(musteriId, toplamTutar);
const siparisId = siparisSonuc.lastInsertRowid;
// Kalemleri ekle ve stokları güncelle
for (const kalem of kalemler) {
const urun = db.prepare('SELECT fiyat FROM urunler WHERE id = ?').get(kalem.urunId);
ifadeler.kalemEkle.run(siparisId, kalem.urunId, kalem.adet, urun.fiyat);
ifadeler.stokGuncelle.run(kalem.adet, kalem.urunId, kalem.adet);
}
return { siparisId, toplamTutar };
});
// Sorgulama örnekleri
function sorgulamaOrnekleri() {
// Kategoriye göre ürün sayısı
const kategoriOzet = db.prepare(`
SELECT k.ad AS kategori, COUNT(u.id) AS urun_sayisi,
ROUND(AVG(u.fiyat), 2) AS ortalama_fiyat,
SUM(u.stok) AS toplam_stok
FROM kategoriler k
LEFT JOIN urunler u ON k.id = u.kategori_id
GROUP BY k.id
ORDER BY urun_sayisi DESC
`).all();
console.log('Kategori özeti:', kategoriOzet);
// Fiyat aralığında ürün arama
const uygunUrunler = db.prepare(`
SELECT u.ad, u.fiyat, k.ad AS kategori
FROM urunler u
JOIN kategoriler k ON u.kategori_id = k.id
WHERE u.fiyat BETWEEN ? AND ? AND u.aktif = 1
ORDER BY u.fiyat ASC
`).all(100, 500);
console.log('Uygun fiyatlı ürünler:', uygunUrunler);
}
// Ana akış
tablolariOlustur();
ornekVerileriEkle();
try {
const sonuc = siparisOlustur(1, [
{ urunId: 1, adet: 2 },
{ urunId: 3, adet: 1 }
]);
console.log('Sipariş oluşturuldu:', sonuc);
} catch (hata) {
console.error('Sipariş hatası:', hata.message);
}
sorgulamaOrnekleri();
db.close();
console.log('Veritabanı bağlantısı kapatıldı');
SQLite, doğru senaryolarda kullanıldığında inanılmaz verimli ve pratik bir veritabanıdır. Özellikle sunucu kurulumu gerektirmemesi ve tek dosyada çalışması, geliştirme sürecini hızlandırır. Ancak yüksek eş zamanlı yazma gerektiren uygulamalarda PostgreSQL veya MySQL gibi sunucu tabanlı çözümlere yönelmek daha doğru olacaktır. Projenizin ihtiyaçlarını iyi analiz edip doğru veritabanını seçmek her zaman en önemli adımdır.
Emeğiniz ve katkınız için teşekkür ederiz!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)
Benzer konular
- Cevaplar
- 0
- Görüntüleme
- 193
- Cevaplar
- 2
- Görüntüleme
- 74
- Cevaplar
- 33
- Görüntüleme
- 2K
- Cevaplar
- 4
- Görüntüleme
- 488
- Cevaplar
- 58
- Görüntüleme
- 4K