mannaxxx 1
mannaxxx
kralhakan2009 1
kralhakan2009
Vahsi Uzman 1
Vahsi Uzman
Best Studio 1
Best Studio
TuZaKK 1
TuZaKK
Mt2Hizmet 1
Mt2Hizmet
Agora Metin2 1
Agora Metin2
Bvural41 1
Bvural41
kaptanmikro1 1
kaptanmikro1
Hikaye Ekle

[NODE JS] Knex.js Query Builder Kullanımı

  • Konuyu başlatan Konuyu başlatan Silverhand
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 1
  • Görüntüleme Görüntüleme 9

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!

nodejs.png


[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.
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst