ما هي 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');
});