Node.js İle Veritabanı Geçişlerinde Migrations İşlemi Nasıl Yapılır?

Node.js İle Veritabanı Geçişlerinde Migrations İşlemi Nasıl Yapılır?
Node.js İle Veritabanı Geçişlerinde Migrations İşlemi Nasıl Yapılır?

Ön Hazırlık ve Gereksinimler

Veritabanı geçişlerine başlamadan önce geliştirme ortamınızın hazır olduğundan emin olmalısınız. Bu eğitimde Node.js 20+ sürümü ve ilişkisel bir veritabanı olan PostgreSQL kullanılacaktır.

  • Node.js ve npm (veya pnpm/yarn) yüklü olmalıdır.
  • Projenizin kök dizininde bir package.json dosyası bulunmalıdır.
  • Veritabanı bağlantı bilgilerini saklamak için dotenv paketi kullanılmalıdır.

Gerekli paketleri projenize dahil etmek için terminalinizde şu komutu çalıştırın:

npm install sequelize sequelize-cli pg pg-hstore dotenv

Bu paketler, veritabanı ile Node.js arasında bir köprü kurarak, şema değişikliklerini kod üzerinden yönetmemize olanak tanır.

Adım Adım Migration Yapılandırması

Sequelize CLI, veritabanı geçişlerini yönetmek için standart bir klasör yapısı sunar. İlk olarak konfigürasyon dosyasını oluşturarak işe başlayalım. Bu işlem, veritabanı bağlantı ayarlarını tanımlayacağımız config/config.json dosyasını oluşturur.

npx sequelize-cli init

Bu komut, projenizde config, models, migrations ve seeders klasörlerini oluşturacaktır. config/config.json dosyasını açarak veritabanı kimlik bilgilerinizi (kullanıcı adı, şifre, veritabanı adı) güncelleyin.

Kritik Uyarı: Veritabanı şifrelerinizi asla doğrudan config.json dosyasına yazmayın. Bunun yerine ortam değişkenleri (environment variables) kullanarak process.env.DB_PASSWORD gibi bir yapı tercih edin.

İlk Migration Dosyasını Oluşturma

Bir tablo oluşturmak veya mevcut bir tabloyu değiştirmek için migration dosyası üretmeniz gerekir. Örneğin, "Kullanıcılar" tablosu oluşturmak için aşağıdaki komutu kullanın.

npx sequelize-cli migration:generate --name create-users-table

Bu komut, migrations/ klasörü içerisinde zaman damgası içeren bir dosya oluşturur. Bu dosya, veritabanına uygulanacak değişiklikleri tanımlayacağımız yerdir.

Migration Dosyasını Kodlama

Oluşturulan dosyanın içinde up ve down fonksiyonlarını göreceksiniz. up fonksiyonu geçişi ileri taşırken, down fonksiyonu bir hata durumunda yapılan değişikliği geri alır.

'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('Users', {
      id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
      username: { type: Sequelize.STRING, allowNull: false },
      email: { type: Sequelize.STRING, unique: true },
      createdAt: { type: Sequelize.DATE, allowNull: false },
      updatedAt: { type: Sequelize.DATE, allowNull: false }
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.dropTable('Users');
  }
};

Bu kod bloğu, veritabanında Users tablosunu oluşturur. allowNull: false kısıtlaması, veri bütünlüğünü korumak için zorunludur.

Migration İşlemini Çalıştırma ve Test Etme

Hazırladığınız migration dosyasını veritabanına uygulamak için şu komutu kullanın. Bu komut, henüz çalıştırılmamış tüm migration dosyalarını veritabanına yansıtır.

npx sequelize-cli db:migrate

İşlemin başarılı olup olmadığını kontrol etmek için veritabanı yönetim aracınızdan (örneğin pgAdmin veya terminal üzerinden psql) SequelizeMeta tablosuna bakabilirsiniz. Bu tablo, hangi migration'ların başarıyla tamamlandığını tutar.

Veritabanı Geçiş Yöntemlerinin Karşılaştırılması

Node.js dünyasında migration yönetimi için kullanılan farklı yaklaşımlar mevcuttur. İhtiyacınıza göre en uygun olanı seçmelisiniz.

Yöntem Avantajı Dezavantajı
Sequelize CLI Geniş topluluk, güçlü ORM desteği Öğrenme eğrisi biraz yüksek
Knex.js Daha hafif, SQL'e yakın kontrol ORM özellikleri daha kısıtlı
Prisma Tip güvenliği (Type-safe), hızlı geliştirme Büyük projelerde karmaşık olabilir

Sıkça Sorulan Sorular

Migration dosyasında hata yaparsam ne yapmalıyım?

Eğer migration henüz production ortamına çıkmadıysa, npx sequelize-cli db:migrate:undo komutu ile son işlemi geri alabilir, dosyayı düzenleyip tekrar db:migrate yapabilirsiniz.

Production ortamında migration nasıl yapılır?

Production ortamında migration'ları mutlaka bir CI/CD pipeline'ı üzerinden, veritabanı yedeği aldıktan sonra çalıştırmalısınız.

Migration dosyalarını neden versiyon kontrol sistemine (Git) eklemeliyim?

Ekip arkadaşlarınızın aynı veritabanı şemasına sahip olması için migration dosyaları şarttır. Git üzerinden bu dosyaları paylaşmak, "benim bilgisayarımda çalışıyor" sorununu ortadan kaldırır.

Mevcut bir tabloya yeni sütun nasıl eklenir?

Yeni bir migration dosyası oluşturup queryInterface.addColumn metodunu kullanarak sütun ekleme işlemini gerçekleştirebilirsiniz.

Veritabanı şifrelerini nasıl korumalıyım?

Her zaman .env dosyası kullanın ve bu dosyayı .gitignore içerisine ekleyerek asla uzak sunucuya göndermeyin.

Güvenlik Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Production ortamında veritabanı işlemleri yapmadan önce mutlaka veritabanı yedeği alınız ve SQL injection riskine karşı her zaman parametreli sorgular (ORM'lerin sunduğu yöntemler) kullanınız.

İleri Seviye Migration Stratejileri: Veri Taşıma ve Dönüştürme

Bazen sadece tablo yapısını değiştirmek yeterli olmaz; mevcut verilerin yeni şemaya uyumlu hale getirilmesi gerekir. Örneğin, users tablosundaki name sütununu first_name ve last_name olarak ayırmanız gerekebilir. Bu tür durumlarda migration dosyası içerisinde veri manipülasyonu yapmanız şarttır.

Aşağıdaki örnek, mevcut veriyi parçalayarak yeni sütunlara aktaran bir migration stratejisini göstermektedir:


module.exports = {
  up: async (queryInterface, Sequelize) => {
    // 1. Yeni sütunları ekle
    await queryInterface.addColumn('Users', 'first_name', Sequelize.STRING);
    await queryInterface.addColumn('Users', 'last_name', Sequelize.STRING);

    // 2. Mevcut veriyi işle
    const users = await queryInterface.sequelize.query('SELECT id, name FROM Users');
    for (const user of users[0]) {
      const parts = user.name.split(' ');
      await queryInterface.sequelize.query(
        'UPDATE Users SET first_name = :first, last_name = :last WHERE id = :id',
        { replacements: { first: parts[0], last: parts[1] || '', id: user.id } }
      );
    }

    // 3. Eski sütunu kaldır
    await queryInterface.removeColumn('Users', 'name');
  },

  down: async (queryInterface, Sequelize) => {
    // Geri alma işlemi (Rollback)
    await queryInterface.addColumn('Users', 'name', Sequelize.STRING);
    // ... veri birleştirme mantığı buraya yazılmalı
  }
};

Migration Süreçlerinde Performans ve Kilitleme Sorunları

Büyük ölçekli veritabanlarında (milyonlarca satır içeren tablolar), ALTER TABLE işlemleri veritabanını kilitleyebilir ve uygulamanızın yanıt vermemesine neden olabilir. Bu durumu yönetmek için şu stratejileri izlemelisiniz:

  • Batch İşlemler: Veri taşıma işlemlerini tek seferde yapmak yerine küçük parçalar (chunk) halinde gerçekleştirin.
  • Online DDL: Mümkünse veritabanınızın desteklediği "online schema change" araçlarını (örneğin MySQL için gh-ost veya pt-online-schema-change) kullanın.
  • Index Yönetimi: Çok büyük tablolarda yeni bir index eklemek, tablonun kilitlenmesine yol açabilir. Bunun yerine CONCURRENTLY (PostgreSQL için) anahtar kelimesini kullanmayı değerlendirin.

Özellikle down fonksiyonlarını yazarken, veritabanı yedeğinin önemini asla unutmayın. Karmaşık bir migration işlemi sırasında hata alırsanız, veritabanı tutarsız bir durumda kalabilir. Bu yüzden her zaman transaction bloklarını kullanın:


up: async (queryInterface, Sequelize) => {
  const transaction = await queryInterface.sequelize.transaction();
  try {
    await queryInterface.addColumn('Orders', 'status', Sequelize.INTEGER, { transaction });
    await transaction.commit();
  } catch (err) {
    await transaction.rollback();
    throw err;
  }
}

Migration Hata Ayıklama (Debugging) İpuçları

Migration hataları genellikle veritabanı kısıtlamaları (constraints) veya veri tipi uyuşmazlıklarından kaynaklanır. Hata ayıklama sürecini hızlandırmak için şu adımları izleyin:

  1. SQL Loglarını Aktif Edin: Sequelize konfigürasyonunuzda logging: console.log parametresini açarak hangi SQL sorgusunun hata verdiğini anlık görün.
  2. Dry Run: Değişiklikleri uygulamadan önce SQL çıktısını bir dosyaya yazdırarak inceleyin.
  3. State Kontrolü: SequelizeMeta tablosunu kontrol ederek, hangi migration dosyasının en son başarıyla çalıştırıldığını doğrulayın.

Sonuç

Node.js ile veritabanı geçişleri, projenizin sürdürülebilirliği için vazgeçilmez bir yetenektir. Sequelize CLI veya alternatif araçlarla şema değişikliklerini yönetmek, hataları minimize eder ve geliştirme sürecini hızlandırır. Bir sonraki adım olarak, veritabanı işlemlerinizi daha güvenli hale getirmek için Veritabanı Seed (Tohumlama) işlemlerini öğrenerek test verilerinizi nasıl otomatik oluşturacağınızı araştırmanızı öneririm.

Bu yazıya tepkinizi paylaşın:
Deniz Arslan

On yıldır dijital içerik üretimi ve editörlük alanında çalışıyorum. Karmaşık süreçleri herkesin anlayabileceği basit ve adım adım rehberlere dönüştürme konusunda uzmanım.

Yorumlar (0)

Yorum Yaz