العودة للرئيسية
الدرس الثاني عشر

PostgreSQL - قاعدة بيانات SQL

استخدام PostgreSQL مع Node.js

55 دقيقة متقدم الفصل الرابع

ما هي PostgreSQL؟

PostgreSQL هي قاعدة بيانات علائقية (SQL) مفتوحة المصدر وقوية جداً. تتميز بالموثوقية والأداء العالي ودعم المعايير.

لماذا PostgreSQL؟

  • موثوقة - ACID Compliant
  • قوية - ميزات متقدمة كثيرة
  • مفتوحة المصدر - مجانية تماماً
  • قابلة للتوسع - تدعم Big Data
  • آمنة - نظام أذونات قوي

التثبيت والإعداد

1. تثبيت PostgreSQL

# macOS brew install postgresql@15 brew services start postgresql@15 # Ubuntu/Debian sudo apt install postgresql postgresql-contrib # Windows # حمل من: https://www.postgresql.org/download/

2. تثبيت pg (Node.js Client)

pnpm add pg

الاتصال بقاعدة البيانات

const { Pool } = require('pg'); // إنشاء Connection Pool const pool = new Pool({ user: 'postgres', host: 'localhost', database: 'mydatabase', password: 'your_password', port: 5432, max: 20, // Max clients idleTimeoutMillis: 30000, // Close idle clients connectionTimeoutMillis: 2000 }); // اختبار الاتصال pool.query('SELECT NOW()', (err, res) => { if (err) { console.error('❌ خطأ في الاتصال:', err); } else { console.log('✅ متصل بنجاح:', res.rows[0]); } }); module.exports = pool;

إنشاء الجداول

const pool = require('./db'); async function createTables() { const client = await pool.connect(); try { // إنشاء جدول المستخدمين await client.query(` CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, age INTEGER CHECK (age >= 18), is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // إنشاء جدول المقالات await client.query(` CREATE TABLE IF NOT EXISTS posts ( id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, content TEXT, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, views INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // إنشاء Index await client.query(` CREATE INDEX IF NOT EXISTS idx_users_email ON users(email) `); console.log('✅ تم إنشاء الجداول بنجاح'); } catch (err) { console.error('❌ خطأ:', err.message); } finally { client.release(); } } createTables();

عمليات CRUD

Create - إدراج بيانات

const pool = require('./db'); // إدراج مستخدم async function createUser(name, email, password, age) { try { const result = await pool.query( 'INSERT INTO users (name, email, password, age) VALUES ($1, $2, $3, $4) RETURNING *', [name, email, password, age] ); console.log('تم إنشاء المستخدم:', result.rows[0]); return result.rows[0]; } catch (err) { console.error('خطأ:', err.message); throw err; } } // استخدام createUser('أحمد محمد', 'ahmed@test.com', 'hashed_password', 25); // إدراج عدة صفوف async function createMultipleUsers(users) { const values = users.map((_, i) => `($${i*4+1}, $${i*4+2}, $${i*4+3}, $${i*4+4})` ).join(','); const flatValues = users.flatMap(u => [u.name, u.email, u.password, u.age] ); const query = ` INSERT INTO users (name, email, password, age) VALUES ${values} RETURNING * `; const result = await pool.query(query, flatValues); return result.rows; }

Read - قراءة البيانات

// جلب جميع المستخدمين async function getAllUsers() { const result = await pool.query('SELECT * FROM users'); return result.rows; } // جلب مستخدم حسب ID async function getUserById(id) { const result = await pool.query( 'SELECT * FROM users WHERE id = $1', [id] ); return result.rows[0]; } // جلب مع شروط async function getActiveUsers() { const result = await pool.query( 'SELECT * FROM users WHERE is_active = $1 AND age >= $2', [true, 18] ); return result.rows; } // جلب مع Pagination async function getPaginatedUsers(page = 1, limit = 10) { const offset = (page - 1) * limit; const result = await pool.query( 'SELECT * FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2', [limit, offset] ); return result.rows; } // البحث async function searchUsers(searchTerm) { const result = await pool.query( 'SELECT * FROM users WHERE name ILIKE $1 OR email ILIKE $1', [`%${searchTerm}%`] ); return result.rows; } // JOIN بين جدولين async function getUsersWithPosts() { const result = await pool.query(` SELECT users.id, users.name, users.email, COUNT(posts.id) as post_count FROM users LEFT JOIN posts ON users.id = posts.user_id GROUP BY users.id ORDER BY post_count DESC `); return result.rows; }

Update - تحديث البيانات

// تحديث مستخدم async function updateUser(id, updates) { const { name, email, age } = updates; const result = await pool.query( `UPDATE users SET name = $1, email = $2, age = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $4 RETURNING *`, [name, email, age, id] ); return result.rows[0]; } // تحديث حقل واحد async function deactivateUser(id) { const result = await pool.query( 'UPDATE users SET is_active = $1 WHERE id = $2 RETURNING *', [false, id] ); return result.rows[0]; } // تحديث عدة صفوف async function deactivateInactiveUsers(days = 30) { const result = await pool.query(` UPDATE users SET is_active = false WHERE created_at < NOW() - INTERVAL '${days} days' RETURNING id, name `); return result.rows; }

Delete - حذف البيانات

// حذف مستخدم async function deleteUser(id) { const result = await pool.query( 'DELETE FROM users WHERE id = $1 RETURNING *', [id] ); return result.rows[0]; } // حذف حسب شرط async function deleteInactiveUsers() { const result = await pool.query( 'DELETE FROM users WHERE is_active = false RETURNING id, name' ); return result.rows; }

Transactions

Transactions تضمن تنفيذ مجموعة عمليات كوحدة واحدة:

async function transferMoney(fromUserId, toUserId, amount) { const client = await pool.connect(); try { // بدء Transaction await client.query('BEGIN'); // خصم من المستخدم الأول await client.query( 'UPDATE accounts SET balance = balance - $1 WHERE user_id = $2', [amount, fromUserId] ); // إضافة للمستخدم الثاني await client.query( 'UPDATE accounts SET balance = balance + $1 WHERE user_id = $2', [amount, toUserId] ); // تسجيل العملية await client.query( 'INSERT INTO transactions (from_user, to_user, amount) VALUES ($1, $2, $3)', [fromUserId, toUserId, amount] ); // تأكيد Transaction await client.query('COMMIT'); console.log('✅ تمت العملية بنجاح'); } catch (err) { // إلغاء Transaction في حالة الخطأ await client.query('ROLLBACK'); console.error('❌ فشلت العملية:', err.message); throw err; } finally { client.release(); } }

مثال عملي: API كامل

const express = require('express'); const pool = require('./db'); const app = express(); app.use(express.json()); // GET - جلب جميع المستخدمين app.get('/api/users', async (req, res) => { try { const result = await pool.query('SELECT * FROM users ORDER BY created_at DESC'); res.json({ success: true, count: result.rows.length, data: result.rows }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); // GET - جلب مستخدم واحد app.get('/api/users/:id', async (req, res) => { try { const { id } = req.params; const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]); if (result.rows.length === 0) { return res.status(404).json({ success: false, error: 'المستخدم غير موجود' }); } res.json({ success: true, data: result.rows[0] }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); // POST - إنشاء مستخدم app.post('/api/users', async (req, res) => { try { const { name, email, password, age } = req.body; const result = await pool.query( 'INSERT INTO users (name, email, password, age) VALUES ($1, $2, $3, $4) RETURNING *', [name, email, password, age] ); res.status(201).json({ success: true, data: result.rows[0] }); } catch (err) { res.status(400).json({ success: false, error: err.message }); } }); // PUT - تحديث مستخدم app.put('/api/users/:id', async (req, res) => { try { const { id } = req.params; const { name, email, age } = req.body; const result = await pool.query( 'UPDATE users SET name = $1, email = $2, age = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $4 RETURNING *', [name, email, age, id] ); if (result.rows.length === 0) { return res.status(404).json({ success: false, error: 'المستخدم غير موجود' }); } res.json({ success: true, data: result.rows[0] }); } catch (err) { res.status(400).json({ success: false, error: err.message }); } }); // DELETE - حذف مستخدم app.delete('/api/users/:id', async (req, res) => { try { const { id } = req.params; const result = await pool.query('DELETE FROM users WHERE id = $1 RETURNING *', [id]); if (result.rows.length === 0) { return res.status(404).json({ success: false, error: 'المستخدم غير موجود' }); } res.json({ success: true, message: 'تم حذف المستخدم' }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); app.listen(3000, () => { console.log('API يعمل على المنفذ 3000'); });