Gereksinimler ve Ön Hazırlık
Çalışmaya başlamadan önce sisteminizde Node.js (tercihen LTS sürümü) ve Postgresql veritabanının kurulu olması gerekir. Veri modelleme aşamasında veritabanı ile iletişim kurmak için en güvenilir yöntem olan pg sürücüsünü veya bir ORM (Object-Relational Mapping) aracı olan Prisma'yı kullanacağız.
- Node.js v22+ (Güncel ECMAScript özelliklerini destekler)
- Postgresql v16+
- Proje klasörü içerisinde
npm init -ykomutu ile başlatılmış bir çalışma alanı - Güvenli bağlantı yönetimi için
dotenvpaketi
Veritabanı Bağlantısı ve Yapılandırma
Veri modelleme işlemine başlamadan önce, Node.js uygulamanızın Postgresql veritabanına güvenli bir şekilde bağlanması gerekir. Bağlantı bilgilerini asla doğrudan kod içerisinde tutmayın; bunun yerine ortam değişkenlerini (environment variables) kullanın.
// db.js dosyası
require('dotenv').config();
const { Pool } = require('pg');
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT,
});
module.exports = {
query: (text, params) => pool.query(text, params),
};
Yukarıdaki kod, pg kütüphanesini kullanarak bir bağlantı havuzu (pool) oluşturur. Bu yöntem, her istekte yeni bir bağlantı açmak yerine mevcut bağlantıları yeniden kullanarak uygulamanızın performansını artırır.
Veri Modelleme: Tablo Tasarımı ve SQL Şemaları
Veri modelleme, verilerinizin birbirleriyle olan ilişkilerini tanımlama sürecidir. Postgresql tarafında bu işlemi SQL DDL (Data Definition Language) komutları ile yaparız. Örneğin, bir kullanıcı ve bu kullanıcının gönderileri için bir yapı kuralım.
-- schema.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
content TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
Bu SQL yapısı, users ve posts tabloları arasında bire-çok (one-to-many) bir ilişki kurar. ON DELETE CASCADE kuralı, bir kullanıcı silindiğinde ona ait tüm gönderilerin de otomatik olarak silinmesini sağlar; bu, veri bütünlüğünü korumak için kritik bir adımdır.
Node.js ile Veri Ekleme ve SQL Injection Koruması
Veri modellemede en büyük güvenlik riski SQL Injection saldırılarıdır. Kullanıcıdan gelen verileri asla doğrudan sorgu dizisine eklemeyin. Her zaman parametreli sorgular (parameterized queries) kullanın.
// userService.js
const db = require('./db');
async function createUser(username, email) {
const query = 'INSERT INTO users(username, email) VALUES($1, $2) RETURNING *';
const values = [username, email];
try {
const res = await db.query(query, values);
return res.rows[0];
} catch (err) {
console.error('Kullanıcı ekleme hatası:', err.stack);
throw err;
}
}
Burada $1 ve $2 ifadeleri, values dizisindeki verilerle güvenli bir şekilde değiştirilir. Bu yöntem, veritabanı sürücüsünün girdileri temizlemesini sağlar ve SQL Injection riskini ortadan kaldırır.
Veri İlişkilendirme ve Join İşlemleri
Modelleme yaptığınız verileri birleştirmek için JOIN operatörlerini kullanırız. Örneğin, belirli bir kullanıcının tüm gönderilerini çekmek için aşağıdaki yapıyı kullanabilirsiniz.
async function getUserPosts(userId) {
const query = `
SELECT u.username, p.title, p.content
FROM users u
JOIN posts p ON u.id = p.user_id
WHERE u.id = $1
`;
const res = await db.query(query, [userId]);
return res.rows;
}
Bu sorgu, users ve posts tablolarını user_id üzerinden birleştirerek ilişkisel veriyi tek bir sonuç kümesinde döndürür. Bu, uygulamanızın veritabanına yaptığı sorgu sayısını azaltarak performansı optimize eder.
Veri Modelleme Yöntemlerinin Karşılaştırılması
| Yöntem | Avantajı | Dezavantajı |
|---|---|---|
| Raw SQL | Maksimum performans, tam kontrol | Yüksek kod karmaşıklığı |
| Query Builder (Knex) | Dinamik sorgu oluşturma kolaylığı | SQL bilgisi yine de gereklidir |
| ORM (Prisma) | Tip güvenliği, hızlı geliştirme | Ekstra kütüphane yükü |
Kritik Güvenlik ve Performans Uyarıları
Veritabanı işlemlerinde her zaman "Least Privilege" (En Az Yetki) prensibini uygulayın. Node.js uygulamanızın veritabanına bağlanan kullanıcısı, sadece ihtiyaç duyduğu tablolara erişebilmeli; veritabanını silme veya tablo oluşturma gibi yetkileri üretim ortamında (production) kısıtlanmalıdır.
Sıkça Sorulan Sorular
Node.js ile Postgresql kullanırken neden ORM yerine Raw SQL tercih etmeliyim?
Raw SQL, veritabanı üzerinde tam hakimiyet sağlar ve karmaşık sorgularda ORM'lerin oluşturabileceği verimsiz sorgulardan kaçınmanıza olanak tanır. Ancak, küçük ve orta ölçekli projelerde ORM kullanımı geliştirme hızını ciddi oranda artırır.
SQL Injection saldırısından korunmanın en kesin yolu nedir?
Her zaman parametreli sorgular (prepared statements) kullanmak ve kullanıcı girdilerini asla doğrudan sorgu metnine dahil etmemektir. Ayrıca, veritabanı sürücünüzün güncel olduğundan emin olun.
Veritabanı indeksleme (indexing) nedir ve neden gereklidir?
İndeksleme, veritabanındaki arama işlemlerini hızlandıran bir yapıdır. Özellikle sık sorgulanan sütunlarda (örneğin email veya kullanıcı adı) indeks kullanmak, sorgu yanıt sürelerini milisaniyelere indirir.
Async/Await yapısı veritabanı işlemlerini nasıl etkiler?
Node.js'in olay döngüsünü (event loop) engellemeden, veritabanından veri gelene kadar kodun beklemesini sağlar. Bu, uygulamanın aynı anda binlerce isteği yönetebilmesini mümkün kılar.
Veri modellemede "normalization" ne anlama gelir?
Veri tekrarını azaltmak ve veri bütünlüğünü sağlamak için veritabanı tablolarını mantıksal parçalara ayırma sürecidir. İlişkisel veritabanı tasarımının temel taşıdır.
Veritabanı İşlemlerinde Hata Ayıklama ve Loglama Stratejileri
Node.js ve PostgreSQL entegrasyonunda karşılaşılan hataların çoğu, bağlantı zaman aşımları veya hatalı SQL sözdiziminden kaynaklanır. Üretim ortamında veritabanı sorgularını izlemek, sistemin kararlılığını artırmak için kritik öneme sahiptir.
Hataları yönetmek için merkezi bir "Error Handler" yapısı kurmak ve her sorguyu bir try-catch bloğu ile sarmalamak en iyi pratiktir. Aşağıdaki örnek, sorgu hatalarını detaylı bir şekilde loglamanızı sağlar:
async function executeQuery(query, params) {
const client = await pool.connect();
try {
const res = await client.query(query, params);
return res.rows;
} catch (err) {
console.error('Veritabanı Sorgu Hatası:', {
message: err.message,
query: query,
params: params,
timestamp: new Date().toISOString()
});
throw new Error('Veritabanı işlemi başarısız oldu.');
} finally {
client.release();
}
}
Bu yapı sayesinde, hatalı bir sorgu gönderildiğinde sadece hata mesajını değil, hataya neden olan parametreleri ve sorgu metnini de loglayarak hata ayıklama sürecini hızlandırabilirsiniz.
Veritabanı Performansını İzleme ve İleri İpuçları
Uygulamanız büyüdükçe, veritabanı sorgularının yanıt süreleri (latency) önem kazanır. PostgreSQL üzerinde sorgu performansını optimize etmek için EXPLAIN ANALYZE komutunu kullanmak, sorgunun hangi aşamada yavaşladığını anlamanızı sağlar.
Sorgu Optimizasyonu İçin İpuçları:
- Bağlantı Havuzu (Connection Pooling): Her istek için yeni bir bağlantı açmak yerine
pg.Poolkullanarak mevcut bağlantıları yeniden kullanın. - Seçici Sorgulama:
SELECT *yerine sadece ihtiyacınız olan sütunları çağırın. Bu, ağ trafiğini ve bellek kullanımını azaltır. - Transaction Yönetimi: Birbiriyle ilişkili birden fazla işlem yapıyorsanız, verilerin tutarlılığını korumak için
BEGIN,COMMITveROLLBACKkomutlarını kullanın.
Örneğin, bir banka transferi senaryosunda transaction kullanımı şu şekilde olmalıdır:
async function transferFunds(fromAccount, toAccount, amount) {
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [amount, fromAccount]);
await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [amount, toAccount]);
await client.query('COMMIT');
} catch (e) {
await client.query('ROLLBACK');
throw e;
} finally {
client.release();
}
}
Bu yöntem, sistemde beklenmedik bir hata oluştuğunda veritabanının "tutarsız" bir durumda kalmasını engeller ve veri bütünlüğünü garanti altına alır.
Sonuç
Node.js ile Postgresql üzerinden veri modelleme yapmak, uygulamanızın temelini sağlamlaştırmak demektir. Doğru tablo tasarımı, parametreli sorgularla güvenlik ve ilişkisel sorgularla performans optimizasyonu, başarılı bir backend mimarisinin anahtarıdır. Bir sonraki adım olarak, veritabanı işlemlerinizi daha da kolaylaştırmak için Prisma gibi modern bir ORM aracı ile tip güvenli (TypeScript) bir yapı kurmayı deneyebilirsiniz.
Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamına geçmeden önce tüm veritabanı işlemlerinizi güvenlik testlerinden geçirmeli ve hassas verileri şifreleyerek (hashing) saklamalısınız.


Yorumlar (0)
Yorum Yaz