noisiv 1
noisiv
Manwe Work 1
Manwe Work
-Blaster- 2
-Blaster-
kralhakan2009 1
kralhakan2009
Bodyguardd 1
Bodyguardd
Vahsi Uzman 1
Vahsi Uzman
Hikaye Ekle

[NODE JS] Sequelize Migration Oluşturma

  • Konuyu başlatan Konuyu başlatan Silverhand
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 1
  • Görüntüleme Görüntüleme 17
nodejs.png


[NODE JS] Sequelize Migration Oluşturma​


Migration (göç) kavramı, veritabanı dünyasında versiyon kontrolü demektir. Tıpkı Git ile kod değişikliklerini takip ettiğimiz gibi, migration'lar ile veritabanı şemasındaki değişiklikleri takip ederiz. Bu yaklaşım özellikle ekip çalışmasında hayat kurtarıcı. Bir geliştirici yeni bir tablo veya sütun eklediğinde, diğer geliştiriciler migration'ı çalıştırarak kendi veritabanlarını güncelleyebilir. Üretim ortamına geçişlerde de migration'lar güvenli bir şema güncelleme yolu sunar.

Sequelize CLI, migration yönetimi için kapsamlı komutlar sağlar. npx sequelize-cli init komutuyla proje yapısını oluşturabilir, npx sequelize-cli migration:generate ile yeni migration dosyaları oluşturabilirsiniz. Her migration dosyasında iki fonksiyon bulunur: up fonksiyonu değişikliği uygular, down fonksiyonu ise değişikliği geri alır. Bu yapı, şema değişikliklerinin hem ileri hem geri alınabilmesini sağlar.

Migration dosyalarının isimlendirilmesi önemli. Sequelize CLI, dosya adının başına otomatik olarak bir zaman damgası ekler. Bu sayede migration'lar doğru sırada çalıştırılır. Migration dosyaları genellikle migrations klasöründe tutulur ve her dosya tek bir şema değişikliğini temsil eder. Bu tek sorumluluk prensibi, hata durumlarında geri almayı kolaylaştırır.

Seeder'lar ise veritabanına örnek veya başlangıç verisi eklemek için kullanılır. Test verileri, varsayılan kategoriler, admin kullanıcıları gibi başlangıç verileri seeder'lar aracılığıyla eklenir. Migration'lar şema değişikliklerini yönetirken, seeder'lar veri değişikliklerini yönetir.

Dikkat edilmesi gereken önemli bir nokta: üretim ortamında çalışan migration'lar asla geri alınmamalıdır. Bunun yerine yeni bir migration oluşturarak değişiklik yapılmalıdır. Ayrıca migration'larda veri kaybına yol açabilecek işlemler (sütun silme, tablo silme gibi) çok dikkatli yapılmalıdır. Mümkünse bu işlemler öncesinde yedek almak gerekir.

Migration stratejileri de önemlidir. Büyük tablolarda sütun ekleme veya tip değiştirme işlemleri uzun sürebilir ve tabloyu kilitleyebilir. Bu durumlarda online schema change araçları veya aşamalı migration stratejileri kullanılabilir. Ayrıca migration'ların idempotent olması, yani birden fazla kez çalıştırılsalar bile aynı sonucu vermesi önemlidir.

JavaScript:
// Sequelize Migration Oluşturma ve Yönetimi

// ============================================
// sequelize-cli yapılandırma dosyası: .sequelizerc
// ============================================
// const path = require('path');
// module.exports = {
//   config: path.resolve('config', 'database.js'),
//   'models-path': path.resolve('models'),
//   'seeders-path': path.resolve('seeders'),
//   'migrations-path': path.resolve('migrations')
// };

// ============================================
// config/database.js - Veritabanı yapılandırması
// ============================================
// module.exports = {
//   development: {
//     username: 'kullanici',
//     password: 'sifre',
//     database: 'egitim_db_dev',
//     host: '127.0.0.1',
//     dialect: 'postgres'
//   },
//   test: {
//     username: 'kullanici',
//     password: 'sifre',
//     database: 'egitim_db_test',
//     host: '127.0.0.1',
//     dialect: 'postgres'
//   },
//   production: {
//     use_env_variable: 'DATABASE_URL',
//     dialect: 'postgres',
//     dialectOptions: {
//       ssl: { require: true, rejectUnauthorized: false }
//     }
//   }
// };

// ============================================
// Migration 1: Kullanıcılar tablosu oluşturma
// migrations/20260101000001-kullanicilar-tablosu-olustur.js
// ============================================
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('kullanicilar', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true,
        allowNull: false
      },
      ad: {
        type: Sequelize.STRING(50),
        allowNull: false
      },
      soyad: {
        type: Sequelize.STRING(50),
        allowNull: false
      },
      email: {
        type: Sequelize.STRING,
        allowNull: false,
        unique: true
      },
      sifre_hash: {
        type: Sequelize.STRING(255),
        allowNull: false
      },
      rol: {
        type: Sequelize.ENUM('kullanici', 'moderator', 'admin'),
        defaultValue: 'kullanici'
      },
      aktif: {
        type: Sequelize.BOOLEAN,
        defaultValue: true
      },
      email_dogrulandi: {
        type: Sequelize.BOOLEAN,
        defaultValue: false
      },
      created_at: {
        type: Sequelize.DATE,
        allowNull: false,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      },
      updated_at: {
        type: Sequelize.DATE,
        allowNull: false,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      }
    });

    // İndeks ekleme
    await queryInterface.addIndex('kullanicilar', ['email'], {
      unique: true,
      name: 'idx_kullanicilar_email'
    });

    await queryInterface.addIndex('kullanicilar', ['rol'], {
      name: 'idx_kullanicilar_rol'
    });
  },

  async down(queryInterface) {
    await queryInterface.dropTable('kullanicilar');
  }
};

// ============================================
// Migration 2: Kategoriler tablosu
// migrations/20260101000002-kategoriler-tablosu-olustur.js
// ============================================
const kategoriMigration = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('kategoriler', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      ad: {
        type: Sequelize.STRING(100),
        allowNull: false,
        unique: true
      },
      slug: {
        type: Sequelize.STRING(100),
        allowNull: false,
        unique: true
      },
      aciklama: {
        type: Sequelize.TEXT
      },
      ust_kategori_id: {
        type: Sequelize.INTEGER,
        references: {
          model: 'kategoriler',
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
      },
      sira: {
        type: Sequelize.INTEGER,
        defaultValue: 0
      },
      aktif: {
        type: Sequelize.BOOLEAN,
        defaultValue: true
      },
      created_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      },
      updated_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      }
    });

    await queryInterface.addIndex('kategoriler', ['slug'], {
      unique: true,
      name: 'idx_kategoriler_slug'
    });
  },

  async down(queryInterface) {
    await queryInterface.dropTable('kategoriler');
  }
};

// ============================================
// Migration 3: Ürünler tablosu
// migrations/20260101000003-urunler-tablosu-olustur.js
// ============================================
const urunMigration = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('urunler', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      ad: {
        type: Sequelize.STRING(200),
        allowNull: false
      },
      slug: {
        type: Sequelize.STRING(200),
        allowNull: false,
        unique: true
      },
      aciklama: {
        type: Sequelize.TEXT
      },
      fiyat: {
        type: Sequelize.DECIMAL(10, 2),
        allowNull: false
      },
      indirimli_fiyat: {
        type: Sequelize.DECIMAL(10, 2)
      },
      stok: {
        type: Sequelize.INTEGER,
        defaultValue: 0
      },
      kategori_id: {
        type: Sequelize.INTEGER,
        references: {
          model: 'kategoriler',
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
      },
      olusturan_id: {
        type: Sequelize.INTEGER,
        references: {
          model: 'kullanicilar',
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
      },
      durum: {
        type: Sequelize.ENUM('taslak', 'aktif', 'pasif', 'tukendi'),
        defaultValue: 'taslak'
      },
      meta_baslik: {
        type: Sequelize.STRING(200)
      },
      meta_aciklama: {
        type: Sequelize.STRING(500)
      },
      created_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      },
      updated_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      }
    });

    // Çoklu indeksler
    await queryInterface.addIndex('urunler', ['kategori_id']);
    await queryInterface.addIndex('urunler', ['durum']);
    await queryInterface.addIndex('urunler', ['fiyat']);
    await queryInterface.addIndex('urunler', ['slug'], { unique: true });
  },

  async down(queryInterface) {
    await queryInterface.dropTable('urunler');
  }
};

// ============================================
// Migration 4: Mevcut tabloya sütun ekleme
// migrations/20260201000001-kullanicilara-profil-alanlari-ekle.js
// ============================================
const profilMigration = {
  async up(queryInterface, Sequelize) {
    // Transaction ile güvenli ekleme
    const transaction = await queryInterface.sequelize.transaction();
    try {
      await queryInterface.addColumn('kullanicilar', 'avatar_url', {
        type: Sequelize.STRING(500),
        allowNull: true
      }, { transaction });

      await queryInterface.addColumn('kullanicilar', 'biyografi', {
        type: Sequelize.TEXT,
        allowNull: true
      }, { transaction });

      await queryInterface.addColumn('kullanicilar', 'telefon', {
        type: Sequelize.STRING(20),
        allowNull: true
      }, { transaction });

      await queryInterface.addColumn('kullanicilar', 'son_giris_tarihi', {
        type: Sequelize.DATE,
        allowNull: true
      }, { transaction });

      await queryInterface.addColumn('kullanicilar', 'giris_sayisi', {
        type: Sequelize.INTEGER,
        defaultValue: 0
      }, { transaction });

      await transaction.commit();
      console.log('Profil alanları başarıyla eklendi');
    } catch (hata) {
      await transaction.rollback();
      throw hata;
    }
  },

  async down(queryInterface) {
    const transaction = await queryInterface.sequelize.transaction();
    try {
      await queryInterface.removeColumn('kullanicilar', 'avatar_url', { transaction });
      await queryInterface.removeColumn('kullanicilar', 'biyografi', { transaction });
      await queryInterface.removeColumn('kullanicilar', 'telefon', { transaction });
      await queryInterface.removeColumn('kullanicilar', 'son_giris_tarihi', { transaction });
      await queryInterface.removeColumn('kullanicilar', 'giris_sayisi', { transaction });
      await transaction.commit();
    } catch (hata) {
      await transaction.rollback();
      throw hata;
    }
  }
};

// ============================================
// Seeder: Başlangıç verileri
// seeders/20260101000001-varsayilan-kategoriler.js
// ============================================
const kategoriSeeder = {
  async up(queryInterface) {
    await queryInterface.bulkInsert('kategoriler', [
      { ad: 'Elektronik', slug: 'elektronik', aciklama: 'Elektronik ürünler', sira: 1, aktif: true, created_at: new Date(), updated_at: new Date() },
      { ad: 'Giyim', slug: 'giyim', aciklama: 'Giyim ürünleri', sira: 2, aktif: true, created_at: new Date(), updated_at: new Date() },
      { ad: 'Ev & Yaşam', slug: 'ev-yasam', aciklama: 'Ev ve yaşam ürünleri', sira: 3, aktif: true, created_at: new Date(), updated_at: new Date() },
      { ad: 'Kitap', slug: 'kitap', aciklama: 'Kitap ve kırtasiye', sira: 4, aktif: true, created_at: new Date(), updated_at: new Date() },
      { ad: 'Spor', slug: 'spor', aciklama: 'Spor ekipmanları', sira: 5, aktif: true, created_at: new Date(), updated_at: new Date() }
    ]);
  },

  async down(queryInterface) {
    await queryInterface.bulkDelete('kategoriler', null, {});
  }
};

// CLI Komutları referansı
console.log(`
Sequelize CLI Komutları:
========================
npx sequelize-cli init                           - Proje yapısını oluştur
npx sequelize-cli model:generate --name Urun     - Model ve migration oluştur
npx sequelize-cli migration:generate --name xyz  - Sadece migration oluştur
npx sequelize-cli db:migrate                     - Tüm migration'ları çalıştır
npx sequelize-cli db:migrate:undo                - Son migration'ı geri al
npx sequelize-cli db:migrate:undo:all            - Tüm migration'ları geri al
npx sequelize-cli seed:generate --name xyz       - Seeder oluştur
npx sequelize-cli db:seed:all                    - Tüm seeder'ları çalıştır
npx sequelize-cli db:seed:undo:all               - Seeder verilerini geri al
`);

Migration'lar, profesyonel veritabanı yönetiminin temel taşıdır. Sequelize CLI ile migration oluşturmak ve yönetmek oldukça kolaydır. Ekip çalışmasında herkesin aynı veritabanı şemasını kullanmasını garanti eder. Üretim ortamında güvenli şema güncellemeleri yapmanızı sağlar. Migration yazarken her zaman hem up hem down fonksiyonlarını eksiksiz yazmayı ve transaction kullanmayı unutmayın.
 
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! 🎉
 

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

Geri
Üst