Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!
[NODE JS] Knex.js Query Builder Kullanımı
Knex.js, ORM'lerin getirdiği soyutlama katmanını istemeyenler için mükemmel bir çözüm. Tam teşekküllü bir ORM değil, bir SQL sorgu oluşturucu (query builder). Bu ne demek? SQL'in gücünü kaybetmeden, JavaScript ile sorgular yazabilmenizi sağlar. SQL bilginizi doğrudan kullanırsınız ama sorguları programatik olarak oluşturabilirsiniz. Ben özellikle SQL'e hâkim olduğum projelerde Knex.js'i tercih ediyorum çünkü tam kontrol bende kalıyor.
Knex.js, PostgreSQL, MySQL, MariaDB, SQLite3, Oracle ve Amazon Redshift gibi birçok veritabanını destekler. Migration ve seed yönetimi de yerleşik olarak gelir. Ayrıca transaction desteği, bağlantı havuzu yönetimi ve streaming gibi gelişmiş özellikler sunar. Şema oluşturucu (schema builder) ile tablo yapılarını da JavaScript koduyla tanımlayabilirsiniz.
Knex.js'in zincirleme (chaining) API'si çok sezgisel. knex('tablo').select('*').where('id', 1) gibi ifadeler, SQL sorgularının JavaScript karşılığıdır. Bu zincirleme yapı, karmaşık sorguları adım adım oluşturmanıza olanak tanır. Koşullu sorgu oluşturma özellikle güçlü — bir filtre varsa where ekle, yoksa ekleme gibi dinamik sorgular oluşturabilirsiniz.
Alt sorgular (subqueries) Knex.js'te doğal olarak desteklenir. Bir knex sorgusunu başka bir sorgunun parçası olarak kullanabilirsiniz. Bu özellik, karmaşık raporlama sorgularında çok işe yarar. Ayrıca ham SQL (raw) ifadeleri de desteklenir, böylece Knex.js'in oluşturamadığı özel SQL ifadelerini doğrudan yazabilirsiniz.
Migration'lar, Knex.js'te veritabanı şema değişikliklerini yönetmenin standart yoludur. npx knex migrate:make ve npx knex migrate:latest komutlarıyla migration oluşturabilir ve çalıştırabilirsiniz. Seed dosyaları ise başlangıç verileri eklemek için kullanılır. Bu araçlar, ekip çalışmasında veritabanı şemasının tutarlılığını sağlar.
Bağlantı havuzu yönetimi de Knex.js'in güçlü yönlerinden biri. Havuz boyutu, zaman aşımı ve bağlantı oluşturma/silme geri çağırmaları yapılandırılabilir. Bu özellik, üretim ortamında veritabanı bağlantılarının verimli kullanılmasını sağlar.
Performans açısından Knex.js, sorgu önbellekleme ve toplu işlem desteği sunar. Ayrıca sorguların performansını analiz etmek için .toSQL() metoduyla oluşturulan SQL'i inceleyebilirsiniz. Bu özellik, performans sorunlarını tespit etmek için çok faydalıdır.
JavaScript:
// Knex.js Query Builder - Kapsamlı Kullanım Rehberi
const knex = require('knex');
// Veritabanı yapılandırması
const db = knex({
client: 'pg', // veya 'mysql2', 'sqlite3', 'mssql'
connection: {
host: 'localhost',
port: 5432,
user: 'kullanici',
password: 'sifre',
database: 'egitim_db'
},
pool: {
min: 2,
max: 10,
acquireTimeoutMillis: 30000,
idleTimeoutMillis: 10000
},
// Sorgu logları
debug: false,
log: {
warn(mesaj) { console.warn('[KNEX UYARI]', mesaj); },
error(mesaj) { console.error('[KNEX HATA]', mesaj); }
}
});
// =============================================
// Şema Oluşturma (Migration benzeri)
// =============================================
async function semalariOlustur() {
// Kategoriler tablosu
if (!(await db.schema.hasTable('kategoriler'))) {
await db.schema.createTable('kategoriler', (tablo) => {
tablo.increments('id').primary();
tablo.string('ad', 100).notNullable().unique();
tablo.string('slug', 100).notNullable().unique();
tablo.text('aciklama');
tablo.integer('ust_kategori_id').unsigned()
.references('id').inTable('kategoriler')
.onDelete('SET NULL');
tablo.integer('sira').defaultTo(0);
tablo.boolean('aktif').defaultTo(true);
tablo.timestamps(true, true);
// İndeksler
tablo.index(['aktif']);
});
console.log('Kategoriler tablosu oluşturuldu');
}
// Ürünler tablosu
if (!(await db.schema.hasTable('urunler'))) {
await db.schema.createTable('urunler', (tablo) => {
tablo.increments('id').primary();
tablo.string('ad', 200).notNullable();
tablo.string('slug', 200).notNullable().unique();
tablo.text('aciklama');
tablo.decimal('fiyat', 10, 2).notNullable();
tablo.decimal('indirimli_fiyat', 10, 2);
tablo.integer('stok').defaultTo(0).unsigned();
tablo.string('sku', 50).unique();
tablo.integer('kategori_id').unsigned()
.references('id').inTable('kategoriler')
.onDelete('SET NULL');
tablo.enu('durum', ['taslak', 'aktif', 'pasif', 'tukendi'])
.defaultTo('taslak');
tablo.timestamps(true, true);
tablo.index(['kategori_id']);
tablo.index(['fiyat']);
tablo.index(['durum']);
});
console.log('Ürünler tablosu oluşturuldu');
}
// Müşteriler tablosu
if (!(await db.schema.hasTable('musteriler'))) {
await db.schema.createTable('musteriler', (tablo) => {
tablo.increments('id').primary();
tablo.string('ad', 50).notNullable();
tablo.string('soyad', 50).notNullable();
tablo.string('email').notNullable().unique();
tablo.string('telefon', 20);
tablo.text('adres');
tablo.boolean('aktif').defaultTo(true);
tablo.timestamps(true, true);
});
console.log('Müşteriler tablosu oluşturuldu');
}
// Siparişler tablosu
if (!(await db.schema.hasTable('siparisler'))) {
await db.schema.createTable('siparisler', (tablo) => {
tablo.increments('id').primary();
tablo.string('siparis_no', 20).notNullable().unique();
tablo.integer('musteri_id').unsigned()
.references('id').inTable('musteriler');
tablo.decimal('toplam_tutar', 10, 2).notNullable();
tablo.decimal('kargo_ucreti', 10, 2).defaultTo(0);
tablo.enu('durum', ['beklemede', 'onaylandi', 'hazirlaniyor', 'kargoda', 'teslim', 'iptal'])
.defaultTo('beklemede');
tablo.text('not');
tablo.timestamp('siparis_tarihi').defaultTo(db.fn.now());
tablo.timestamps(true, true);
tablo.index(['musteri_id']);
tablo.index(['durum']);
});
console.log('Siparişler tablosu oluşturuldu');
}
// Sipariş kalemleri tablosu
if (!(await db.schema.hasTable('siparis_kalemleri'))) {
await db.schema.createTable('siparis_kalemleri', (tablo) => {
tablo.increments('id').primary();
tablo.integer('siparis_id').unsigned()
.references('id').inTable('siparisler')
.onDelete('CASCADE');
tablo.integer('urun_id').unsigned()
.references('id').inTable('urunler');
tablo.integer('adet').notNullable().unsigned();
tablo.decimal('birim_fiyat', 10, 2).notNullable();
});
console.log('Sipariş kalemleri tablosu oluşturuldu');
}
}
// =============================================
// CRUD İşlemleri
// =============================================
// CREATE - Veri ekleme
async function veriEkle() {
console.log('
=== Veri Ekleme ===');
// Tek kayıt ekleme
const [kategoriId] = await db('kategoriler')
.insert({
ad: 'Elektronik',
slug: 'elektronik',
aciklama: 'Elektronik ürünler ve aksesuarlar'
})
.returning('id');
console.log('Kategori eklendi, ID:', kategoriId);
// Toplu ekleme
await db('urunler').insert([
{
ad: 'Kablosuz Kulaklık',
slug: 'kablosuz-kulaklik',
aciklama: 'Bluetooth 5.0 gürültü engellemeli',
fiyat: 449.99,
stok: 100,
sku: 'KLK-001',
kategori_id: kategoriId,
durum: 'aktif'
},
{
ad: 'Akıllı Saat',
slug: 'akilli-saat',
aciklama: 'Sağlık takip özellikli',
fiyat: 1299.99,
stok: 50,
sku: 'SAT-001',
kategori_id: kategoriId,
durum: 'aktif'
},
{
ad: 'Mekanik Klavye',
slug: 'mekanik-klavye',
aciklama: 'RGB aydınlatmalı mekanik klavye',
fiyat: 699.99,
stok: 75,
sku: 'KLV-001',
kategori_id: kategoriId,
durum: 'aktif'
}
]);
console.log('Ürünler eklendi');
// Müşteri ekleme
const [musteriId] = await db('musteriler')
.insert({
ad: 'Ayşe',
soyad: 'Korkmaz',
email: 'ayse@ornek.com',
telefon: '5551234567'
})
.returning('id');
console.log('Müşteri eklendi, ID:', musteriId);
return { kategoriId, musteriId };
}
// READ - Veri okuma
async function veriOku() {
console.log('
=== Veri Okuma ===');
// Basit sorgulama
const tumUrunler = await db('urunler')
.select('id', 'ad', 'fiyat', 'stok')
.where('durum', 'aktif')
.orderBy('fiyat', 'asc');
console.log('Aktif ürünler:', tumUrunler.length);
tumUrunler.forEach(u => console.log(` - ${u.ad}: ${u.fiyat} TL`));
// JOIN ile sorgulama
const urunlerDetayli = await db('urunler as u')
.select(
'u.id',
'u.ad',
'u.fiyat',
'u.stok',
'k.ad as kategori_adi'
)
.leftJoin('kategoriler as k', 'u.kategori_id', 'k.id')
.where('u.durum', 'aktif')
.andWhere('u.stok', '>', 0);
console.log('
Ürün detayları:');
urunlerDetayli.forEach(u => {
console.log(` - ${u.ad} [${u.kategori_adi}]: ${u.fiyat} TL`);
});
// Koşullu sorgu oluşturma
async function urunAra(filtreler) {
let sorgu = db('urunler as u')
.select('u.*', 'k.ad as kategori_adi')
.leftJoin('kategoriler as k', 'u.kategori_id', 'k.id');
if (filtreler.arama) {
sorgu = sorgu.where(function() {
this.where('u.ad', 'ilike', `%${filtreler.arama}%`)
.orWhere('u.aciklama', 'ilike', `%${filtreler.arama}%`);
});
}
if (filtreler.minFiyat) {
sorgu = sorgu.andWhere('u.fiyat', '>=', filtreler.minFiyat);
}
if (filtreler.maxFiyat) {
sorgu = sorgu.andWhere('u.fiyat', '<=', filtreler.maxFiyat);
}
if (filtreler.kategoriId) {
sorgu = sorgu.andWhere('u.kategori_id', filtreler.kategoriId);
}
if (filtreler.durum) {
sorgu = sorgu.andWhere('u.durum', filtreler.durum);
}
// Sayfalama
const sayfa = filtreler.sayfa || 1;
const sayfaBoyutu = filtreler.sayfaBoyutu || 10;
sorgu = sorgu.limit(sayfaBoyutu).offset((sayfa - 1) * sayfaBoyutu);
// Sıralama
const siraAlan = filtreler.siraAlan || 'created_at';
const siraYon = filtreler.siraYon || 'desc';
sorgu = sorgu.orderBy(`u.${siraAlan}`, siraYon);
return await sorgu;
}
const aramaSonucu = await urunAra({
arama: 'kulaklık',
minFiyat: 100,
maxFiyat: 1000,
durum: 'aktif'
});
console.log('
Arama sonuçları:', aramaSonucu.length);
// Gruplama ve toplama
const istatistikler = await db('urunler')
.select('durum')
.count('id as urun_sayisi')
.sum('stok as toplam_stok')
.avg('fiyat as ortalama_fiyat')
.min('fiyat as en_dusuk_fiyat')
.max('fiyat as en_yuksek_fiyat')
.groupBy('durum');
console.log('
Ürün istatistikleri:', istatistikler);
// Alt sorgu örneği
const ortalamaUstu = await db('urunler')
.where('fiyat', '>', function() {
this.select(db.raw('AVG(fiyat)')).from('urunler').where('durum', 'aktif');
})
.select('ad', 'fiyat');
console.log('
Ortalama üstü ürünler:', ortalamaUstu.length);
}
// UPDATE - Güncelleme
async function veriGuncelle() {
console.log('
=== Güncelleme ===');
// Tek kayıt güncelleme
const guncellenen = await db('urunler')
.where('sku', 'KLK-001')
.update({
fiyat: 399.99,
indirimli_fiyat: 349.99,
updated_at: db.fn.now()
});
console.log(`${guncellenen} ürün güncellendi`);
// Koşullu güncelleme
const stokGuncelle = await db('urunler')
.where('stok', '<=', 10)
.andWhere('durum', 'aktif')
.update({ durum: 'tukendi' });
console.log(`${stokGuncelle} ürün tükendi olarak işaretlendi`);
// Sayısal artırma
await db('urunler')
.where('sku', 'SAT-001')
.increment('stok', 20);
console.log('Stok artırıldı');
}
// DELETE - Silme
async function veriSil() {
console.log('
=== Silme ===');
const silinen = await db('urunler')
.where('durum', 'pasif')
.andWhere('stok', 0)
.del();
console.log(`${silinen} pasif ürün silindi`);
}
// Transaction
async function siparisIslemleri(musteriId) {
console.log('
=== Sipariş İşlemi (Transaction) ===');
const trx = await db.transaction();
try {
const urun = await trx('urunler').where('sku', 'KLK-001').first();
if (!urun || urun.stok < 2) throw new Error('Yetersiz stok');
await trx('urunler').where('id', urun.id).decrement('stok', 2);
const siparisNo = `SIP-${Date.now()}`;
const [siparisId] = await trx('siparisler').insert({
siparis_no: siparisNo,
musteri_id: musteriId,
toplam_tutar: (urun.indirimli_fiyat || urun.fiyat) * 2,
durum: 'beklemede'
}).returning('id');
await trx('siparis_kalemleri').insert({
siparis_id: siparisId,
urun_id: urun.id,
adet: 2,
birim_fiyat: urun.indirimli_fiyat || urun.fiyat
});
await trx.commit();
console.log('Sipariş oluşturuldu:', siparisNo);
} catch (hata) {
await trx.rollback();
console.error('Sipariş hatası, geri alındı:', hata.message);
}
}
// Ham SQL kullanımı
async function hamSorgu() {
console.log('
=== Ham SQL Sorgusu ===');
const sonuc = await db.raw(`
SELECT
k.ad AS kategori,
COUNT(u.id) AS urun_sayisi,
ROUND(AVG(u.fiyat)::numeric, 2) AS ortalama_fiyat,
SUM(u.stok) AS toplam_stok,
ROUND(SUM(u.fiyat * u.stok)::numeric, 2) AS toplam_deger
FROM kategoriler k
LEFT JOIN urunler u ON k.id = u.kategori_id
WHERE k.aktif = true
GROUP BY k.id, k.ad
ORDER BY toplam_deger DESC
`);
console.log('Kategori raporu:', sonuc.rows || sonuc);
}
// Ana fonksiyon
async function main() {
try {
await semalariOlustur();
const { musteriId } = await veriEkle();
await veriOku();
await veriGuncelle();
await veriSil();
await siparisIslemleri(musteriId);
await hamSorgu();
} catch (hata) {
console.error('Hata:', hata);
} finally {
await db.destroy();
console.log('
Bağlantı havuzu kapatıldı');
}
}
main();
Knex.js, SQL'in gücünü JavaScript'in esnekliğiyle birleştiren mükemmel bir sorgu oluşturucudur. ORM'lerin getirdiği soyutlama katmanını istemeyenler için ideal bir çözüm. Dinamik sorgu oluşturma, transaction yönetimi, migration desteği ve çoklu veritabanı uyumluluğu ile profesyonel Node.js uygulamalarında güvenle kullanılabilir. SQL bilginiz güçlüyse, Knex.js size tam kontrol sunarken kod kalitesini de yüksek tutar.
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
- 1
- Görüntüleme
- 8
- Cevaplar
- 4
- Görüntüleme
- 2K
- Cevaplar
- 12
- Görüntüleme
- 1K
- Cevaplar
- 104
- Görüntüleme
- 8K
- Cevaplar
- 3
- Görüntüleme
- 1K
