هيكلة مشروع Express.js بطريقة احترافية قابلة للتوسع


هيكلة مشروع Express.js بطريقة احترافية قابلة للتوسع

ماذا سنتعلم؟ في هذا الدرس، سنتعلم أفضل الممارسات لهيكلة مشروع Express.js، مما يجعله سهل الصيانة والتوسع وقابلًا لإدارة الفرق الكبيرة.

الخطوة 1: تهيئة المشروع وتثبيت Express

سنبدأ بإنشاء مجلد جديد للمشروع وتهيئته باستخدام npm، ثم نقوم بتثبيت Express. هذه هي الأساسيات لأي مشروع Express.

ملاحظة تقنية: استخدام npm init -y ينشئ ملف package.json بالقيم الافتراضية بسرعة.

أولاً، قم بإنشاء مجلد المشروع وتشغيله:

mkdir express-pro-structure
cd express-pro-structure
npm init -y
npm install express

الآن، لنقم بإنشاء ملف app.js وهو نقطة الدخول الرئيسية لتطبيقنا:

// app.js
const express = require('express'); // استيراد مكتبة Express
const app = express(); // إنشاء تطبيق Express
const PORT = process.env.PORT || 3000; // تحديد المنفذ، إما من المتغيرات البيئية أو الافتراضي 3000

// تعريف مسار بسيط لاختبار الخادم
app.get('/', (req, res) => {
  res.send('مرحباً بكم في مشروع Express الاحترافي!'); // إرسال رد نصي
});

// بدء تشغيل الخادم
app.listen(PORT, () => {
  console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ ${PORT}</code>); // طباعة رسالة عند بدء التشغيل
});

الخطوة 2: فصل المسارات (Routes) والتحكم (Controllers)

للحفاظ على نظافة ملف app.js، سنقوم بفصل منطق المسارات (Routes) ومنطق التحكم (Controllers) إلى ملفات ومجلدات منفصلة. هذا يعزز مبدأ فصل الاهتمامات (Separation of Concerns).

أنشئ المجلدات التالية:

mkdir routes controllers

الآن، لنقم بإنشاء ملف controllers/userController.js الذي سيحتوي على منطق التعامل مع طلبات المستخدمين:

// controllers/userController.js
exports.getAllUsers = (req, res) => {
  // منطق الحصول على جميع المستخدمين (مثال)
  res.status(200).json({ message: 'الحصول على جميع المستخدمين', users: [] }); // إرسال رد JSON
};

exports.getUserById = (req, res) => {
  const userId = req.params.id; // استخراج معرف المستخدم من معلمات المسار
  // منطق الحصول على مستخدم معين (مثال)
  res.status(200).json({ message: <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الحصول على المستخدم ${userId}</code>, user: { id: userId, name: 'مثال مستخدم' } });
};

exports.createUser = (req, res) => {
  const newUser = req.body; // البيانات المرسلة في جسم الطلب
  // منطق إنشاء مستخدم جديد (مثال)
  res.status(201).json({ message: 'تم إنشاء مستخدم جديد', user: newUser });
};

ثم، لنقم بإنشاء ملف routes/userRoutes.js الذي سيحدد المسارات الخاصة بالمستخدمين ويستخدم الـ Controllers:

// routes/userRoutes.js
const express = require('express'); // استيراد Express
const userController = require('../controllers/userController'); // استيراد وحدة التحكم للمستخدمين

const router = express.Router(); // إنشاء موجه جديد

// تعريف المسارات
router.get('/', userController.getAllUsers); // مسار للحصول على جميع المستخدمين
router.get('/:id', userController.getUserById); // مسار للحصول على مستخدم بمعرفه
router.post('/', userController.createUser); // مسار لإنشاء مستخدم جديد

module.exports = router; // تصدير الموجه لاستخدامه في app.js

وأخيراً، نقوم بتحديث ملف app.js لاستخدام هذه المسارات:

// app.js (تحديث)
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
const userRoutes = require('./routes/userRoutes'); // استيراد مسارات المستخدمين

// Middleware لمعالجة طلبات JSON
app.use(express.json());

// استخدام مسارات المستخدمين تحت بادئة '/api/users'
app.use('/api/users', userRoutes);

// مسار بسيط للترحيب
app.get('/', (req, res) => {
  res.send('مرحباً بكم في مشروع Express الاحترافي!');
});

app.listen(PORT, () => {
  console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ ${PORT}</code>);
});

الخطوة 3: إدارة الإعدادات (Configurations)

من الجيد فصل إعدادات التطبيق (مثل رقم المنفذ، وسلاسل اتصال قواعد البيانات) عن الكود الأساسي. سنستخدم مجلد config لهذا الغرض.

ملاحظة تقنية: في المشاريع الحقيقية، يُنصح بشدة باستخدام مكتبات مثل dotenv لإدارة المتغيرات البيئية بشكل آمن.

أنشئ المجلد config وملف config/index.js:

mkdir config
// config/index.js
const config = {
  port: process.env.PORT || 3000, // المنفذ الافتراضي أو من المتغيرات البيئية
  // databaseUrl: process.env.DATABASE_URL || 'mongodb://localhost:27017/my_db', // مثال على إعداد قاعدة بيانات
  // jwtSecret: process.env.JWT_SECRET || 'supersecretkey', // مثال على مفتاح سري
};

module.exports = config; // تصدير كائن الإعدادات

ثم، قم بتحديث app.js لاستخدام الإعدادات من ملف config:

// app.js (تحديث)
const express = require('express');
const app = express();
const config = require('./config'); // استيراد ملف الإعدادات
const userRoutes = require('./routes/userRoutes');

app.use(express.json());

app.use('/api/users', userRoutes);

app.get('/', (req, res) => {
  res.send('مرحباً بكم في مشروع Express الاحترافي!');
});

// استخدام المنفذ من ملف الإعدادات
app.listen(config.port, () => {
  console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ ${config.port}</code>);
});

الخطوة 4: التعامل مع الأخطاء (Error Handling)

إدارة الأخطاء بشكل مركزي أمر بالغ الأهمية لتطبيق مستقر. سنقوم بإنشاء middleware مخصص لمعالجة الأخطاء.

أنشئ المجلد middleware وملف middleware/errorHandler.js:

mkdir middleware
// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
  console.error(err.stack); // طباعة الخطأ في وحدة التحكم للخادم

  const statusCode = err.statusCode || 500; // تحديد حالة HTTP، افتراضي 500 (خطأ خادم داخلي)
  const message = err.message || 'حدث خطأ غير متوقع!'; // رسالة الخطأ

  res.status(statusCode).json({
    status: 'error',
    statusCode,
    message,
  });
};

module.exports = errorHandler; // تصدير الـ middleware

ثم، قم بتحديث app.js لاستخدام الـ middleware الخاص بمعالجة الأخطاء. يجب أن يكون هذا الـ middleware هو الأخير في سلسلة الـ middlewares.

// app.js (تحديث)
const express = require('express');
const app = express();
const config = require('./config');
const userRoutes = require('./routes/userRoutes');
const errorHandler = require('./middleware/errorHandler'); // استيراد middleware معالجة الأخطاء

app.use(express.json());

app.use('/api/users', userRoutes);

app.get('/', (req, res) => {
  res.send('مرحباً بكم في مشروع Express الاحترافي!');
});

// Middleware لمعالجة المسارات غير الموجودة (404 Not Found)
app.use((req, res, next) => {
  const error = new Error(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">المسار غير موجود - ${req.originalUrl}</code>);
  res.status(404);
  next(error); // تمرير الخطأ إلى middleware معالجة الأخطاء
});

// استخدام middleware معالجة الأخطاء (يجب أن يكون في النهاية)
app.use(errorHandler);

app.listen(config.port, () => {
  console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ ${config.port}</code>);
});

الكود النهائي الكامل

هنا تجد جميع الملفات التي أنشأناها في هذا الدرس، جاهزة للنسخ واللصق.

app.js

const express = require('express');
const app = express();
const config = require('./config');
const userRoutes = require('./routes/userRoutes');
const errorHandler = require('./middleware/errorHandler');

// Middleware لمعالجة طلبات JSON
app.use(express.json());

// استخدام مسارات المستخدمين تحت بادئة '/api/users'
app.use('/api/users', userRoutes);

// مسار بسيط للترحيب
app.get('/', (req, res) => {
  res.send('مرحباً بكم في مشروع Express الاحترافي!');
});

// Middleware لمعالجة المسارات غير الموجودة (404 Not Found)
app.use((req, res, next) => {
  const error = new Error(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">المسار غير موجود - ${req.originalUrl}</code>);
  res.status(404);
  next(error);
});

// استخدام middleware معالجة الأخطاء (يجب أن يكون في النهاية)
app.use(errorHandler);

// بدء تشغيل الخادم
app.listen(config.port, () => {
  console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الخادم يعمل على المنفذ ${config.port}</code>);
});

config/index.js

const config = {
  port: process.env.PORT || 3000,
  // databaseUrl: process.env.DATABASE_URL || 'mongodb://localhost:27017/my_db',
  // jwtSecret: process.env.JWT_SECRET || 'supersecretkey',
};

module.exports = config;

controllers/userController.js

exports.getAllUsers = (req, res) => {
  res.status(200).json({ message: 'الحصول على جميع المستخدمين', users: [] });
};

exports.getUserById = (req, res) => {
  const userId = req.params.id;
  res.status(200).json({ message: <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">الحصول على المستخدم ${userId}</code>, user: { id: userId, name: 'مثال مستخدم' } });
};

exports.createUser = (req, res) => {
  const newUser = req.body;
  res.status(201).json({ message: 'تم إنشاء مستخدم جديد', user: newUser });
};

routes/userRoutes.js

const express = require('express');
const userController = require('../controllers/userController');

const router = express.Router();

router.get('/', userController.getAllUsers);
router.get('/:id', userController.getUserById);
router.post('/', userController.createUser);

module.exports = router;

middleware/errorHandler.js

const errorHandler = (err, req, res, next) => {
  console.error(err.stack);

  const statusCode = err.statusCode || 500;
  const message = err.message || 'حدث خطأ غير متوقع!';

  res.status(statusCode).json({
    status: 'error',
    statusCode,
    message,
  });
};

module.exports = errorHandler;

النتيجة المتوقعة

بعد تشغيل الخادم باستخدام الأمر node app.js، ستشاهد الرسالة التالية في الطرفية:

الخادم يعمل على المنفذ 3000

عند زيارة المسارات التالية في المتصفح أو باستخدام أداة مثل Postman:

  • http://localhost:3000/: ستعرض "مرحباً بكم في مشروع Express الاحترافي!"
  • http://localhost:3000/api/users (GET): ستعرض قائمة بجميع المستخدمين (JSON).
  • http://localhost:3000/api/users/123 (GET): ستعرض تفاصيل المستخدم ذو المعرف 123 (JSON).
  • http://localhost:3000/api/users (POST مع جسم JSON): ستنشئ مستخدماً جديداً (JSON).
  • http://localhost:3000/non-existent-route: ستعيد خطأ 404 مع رسالة JSON من الـ middleware الخاص بمعالجة الأخطاء.

هذا الهيكل يوفر أساساً متيناً وقابلاً للتوسع لمشاريع Express.js المستقبلية.