إنشاء مدونة باستخدام Express (الجزء 2): توجيه الرّوابط في Express
يُعد توجيه الروابط (Routing) من المفاهيم الجوهرية في أي تطبيق ويب مبني باستخدام إطار العمل Express، حيث يُمثل الرابط طريقة التفاعل الرئيسية بين المستخدم والتطبيق. يُتيح نظام التوجيه في Express تعريف نقاط الوصول المختلفة (Routes) والتي تمثل عناوين URL تقوم بتنفيذ وظائف معينة على حسب الطلب الوارد من المستخدم. في الجزء الأول من هذه السلسلة، تم التركيز على إعداد بيئة العمل الأساسية لإنشاء مدونة باستخدام Express، بينما سيتم تخصيص هذا الجزء للحديث المفصّل عن توجيه الروابط في Express، وتطبيقه العملي في مشروع مدونة متكامل.
توجيه الروابط في Express: المفهوم والوظيفة
نظام التوجيه في Express هو آلية يتم من خلالها مطابقة الطلبات الواردة (HTTP Requests) مع وظائف معينة في التطبيق. يُمكن تعريفه ببساطة على أنه عملية ربط عنوان URL بطريقة الطلب (GET، POST، PUT، DELETE، إلخ) مع إجراء أو عملية تتم على الخادم. على سبيل المثال، عندما يزور المستخدم الصفحة الرئيسية للمدونة عبر الرابط /، فإن التطبيق يرسل إليه الصفحة الرئيسية. بينما عند زيارته للرابط /posts، فإنه يتوقع أن يرى قائمة بجميع المنشورات.
يمثل Express هذا النظام من خلال دوال مثل:
javascriptapp.get('/', function (req, res) {
res.send('الصفحة الرئيسية');
});
في المثال أعلاه، يتم التعامل مع طلب من نوع GET إلى المسار /، حيث يتم إرسال رد للمستخدم يحتوي على النص “الصفحة الرئيسية”.
أنواع الطلبات HTTP المدعومة في Express
يُوفر Express دعماً لجميع أنواع طلبات HTTP التي تُستخدم عادة في التطبيقات الحديثة، مما يسمح بإنشاء تطبيقات RESTful بكل سهولة:
| نوع الطلب | الوصف |
|---|---|
| GET | يُستخدم لطلب بيانات من الخادم. |
| POST | يُستخدم لإرسال بيانات إلى الخادم (مثلاً إنشاء منشور جديد). |
| PUT | يُستخدم لتحديث البيانات الموجودة. |
| DELETE | يُستخدم لحذف البيانات. |
في سياق المدونة، يمكن استخدام هذه الأنواع كما يلي:
-
GET /posts: لعرض جميع المنشورات. -
GET /posts/:id: لعرض منشور محدد. -
POST /posts: لإنشاء منشور جديد. -
PUT /posts/:id: لتحديث منشور موجود. -
DELETE /posts/:id: لحذف منشور.
بنية مشروع المدونة
في هذا الجزء من بناء مدونة باستخدام Express، سيتم اعتماد بنية منظمة للملفات تسهل عملية إدارة الروابط وفصل المسؤوليات:
markdownblog-app/
├── app.js
├── routes/
│ └── posts.js
├── controllers/
│ └── postsController.js
└── views/
└── ...
ملف app.js
هذا الملف هو نقطة الدخول الرئيسية للتطبيق، ويحتوي على إعدادات Express العامة وربط المسارات المختلفة بالتطبيق.
javascriptconst express = require('express');
const app = express();
const postsRoutes = require('./routes/posts');
app.use(express.json());
app.use('/posts', postsRoutes);
const port = 3000;
app.listen(port, () => {
console.log(`App running on http://localhost:${port}`);
});
ملف routes/posts.js
في هذا الملف يتم تعريف جميع المسارات المتعلقة بالمنشورات، مع الإشارة إلى الوظائف المناسبة من وحدة التحكم:
javascriptconst express = require('express');
const router = express.Router();
const postsController = require('../controllers/postsController');
router.get('/', postsController.getAllPosts);
router.get('/:id', postsController.getPostById);
router.post('/', postsController.createPost);
router.put('/:id', postsController.updatePost);
router.delete('/:id', postsController.deletePost);
module.exports = router;
ملف controllers/postsController.js
هذا الملف يحتوي على جميع الوظائف المنفذة للطلبات، وهو ما يُعرف بـ”وحدة التحكم” أو “Controller”:
javascriptlet posts = [];
exports.getAllPosts = (req, res) => {
res.json(posts);
};
exports.getPostById = (req, res) => {
const id = parseInt(req.params.id);
const post = posts.find(p => p.id === id);
if (post) {
res.json(post);
} else {
res.status(404).send('Post not found');
}
};
exports.createPost = (req, res) => {
const newPost = {
id: posts.length + 1,
title: req.body.title,
content: req.body.content
};
posts.push(newPost);
res.status(201).json(newPost);
};
exports.updatePost = (req, res) => {
const id = parseInt(req.params.id);
const post = posts.find(p => p.id === id);
if (post) {
post.title = req.body.title;
post.content = req.body.content;
res.json(post);
} else {
res.status(404).send('Post not found');
}
};
exports.deletePost = (req, res) => {
const id = parseInt(req.params.id);
posts = posts.filter(p => p.id !== id);
res.status(204).send();
};
التعامل مع معلمات الروابط (Route Parameters)
من أهم مميزات Express دعمه الكامل لما يُعرف بـ”معلمات الروابط”، والتي تُتيح تمرير القيم عبر عنوان URL، كما في المثال:
javascriptrouter.get('/posts/:id', (req, res) => {
const postId = req.params.id;
res.send(`المنشور رقم: ${postId}`);
});
هذه الآلية تُستخدم بكثرة في تحديد المنشورات، الصفحات، التصنيفات، أو أي عنصر ديناميكي آخر داخل المدونة.
التوجيه الديناميكي والتفريع (Route Grouping)
يسمح Express بتنظيم التوجيهات بشكل ديناميكي من خلال تجميعها في وحدات Routes مستقلة، كما تم بيانه أعلاه باستخدام مجلد routes. هذا يساعد في الحفاظ على قابلية التوسع وسهولة إدارة التطبيق.
كما يمكن ربط المسارات بشكل هرمي:
javascriptapp.use('/admin/posts', adminPostsRoutes);
app.use('/user/posts', userPostsRoutes);
توجيه الروابط باستخدام express.Router()
تمثل وحدة Router ميزة قوية في Express لإنشاء مجموعات توجيه مستقلة. من خلالها يمكن بناء نظام مدونة يحتوي على:
-
router.get('/create'): صفحة إنشاء منشور. -
router.get('/:slug'): عرض منشور مفصل حسب العنوان (Slug). -
router.post('/store'): تخزين المنشور الجديد.
معالجة الأخطاء في التوجيه
يُعتبر التعامل مع الأخطاء جزءاً لا يتجزأ من تطوير نظام توجيه احترافي. يُمكن إضافة موجه أخطاء عالمي لتسجيل أو الرد على الطلبات غير المعالجة:
javascriptapp.use((req, res, next) => {
res.status(404).send('الرابط غير موجود');
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('حدث خطأ في الخادم');
});
تكامل التوجيه مع قواعد البيانات
في الواقع العملي، لا يتم تخزين المنشورات في مصفوفة داخلية كما في الأمثلة السابقة، بل يتم ربطها بقاعدة بيانات مثل MongoDB أو PostgreSQL. في هذه الحالة، تستبدل جميع العمليات (get, post, put, delete) بعمليات تعتمد على استعلامات قاعدة البيانات. مثلاً:
javascriptPost.find({}, (err, posts) => {
if (err) return res.status(500).send(err);
res.json(posts);
});
تكامل التوجيه مع المحركات القالبية (Template Engines)
عند إنشاء صفحات الويب الديناميكية، تُستخدم محركات القوالب مثل EJS أو Pug. في هذه الحالة، تُرسل الردود من خلال ملفات عرض:
javascriptres.render('post', { post });
ويُستخدم ذلك في المسارات التالية:
javascriptrouter.get('/create', (req, res) => {
res.render('create-post');
});
router.get('/:id', (req, res) => {
const post = ... // استرجاع المنشور
res.render('post-detail', { post });
});
مقارنة بين التوجيه التقليدي وتوجيه RESTful في Express
| التوجيه التقليدي | التوجيه وفق REST |
|---|---|
GET /posts/view/1 |
GET /posts/1 |
POST /posts/create |
POST /posts |
POST /posts/update/1 |
PUT /posts/1 |
GET /posts/delete/1 |
DELETE /posts/1 |
يوصى باستخدام توجيه RESTful لسهولة القراءة والامتثال لمعايير تطوير التطبيقات الحديثة.
توجيه الروابط في تطبيقات SPA (Single Page Application)
في حال استخدام إطار عمل واجهات أمامية مثل React أو Vue، يجب ضبط Express ليرد على جميع الطلبات من نوع GET بصفحة HTML واحدة:
javascriptapp.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
جدول لأمثلة توجيه الروابط في مشروع المدونة
| الطلب | الرابط | الوظيفة |
|---|---|---|
| GET | /posts |
عرض جميع المنشورات |
| GET | /posts/:id |
عرض منشور مفصل |
| POST | /posts |
إنشاء منشور جديد |
| PUT | /posts/:id |
تعديل منشور موجود |
| DELETE | /posts/:id |
حذف منشور |
| GET | /posts/create |
عرض نموذج إنشاء منشور (مع واجهة) |
| GET | /posts/edit/:id |
عرض نموذج تعديل منشور (مع واجهة) |
الخلاصة التقنية
يشكل توجيه الروابط في Express العمود الفقري لأي تطبيق ويب مبني باستخدام Node.js. بفضل بساطته وقدرته على التوسع، يمكن للمطور بناء نظام مدونة مرن وقابل للتطوير من خلال بنية توجيه منظمة، وبتكامل مع وحدات التحكم وقواعد البيانات، ومحركات العرض. هذا التوجيه لا يساهم فقط في تنظيم الكود بل يرفع من مستوى أمان وسرعة التطبيق ويجعل الصيانة أسهل بكثير على المدى الطويل.
المراجع
-
Express.js Official Documentation – https://expressjs.com
-
Node.js Documentation – https://nodejs.org/en/docs

