البرمجة

توجيه الروابط في Express

إنشاء مدونة باستخدام Express (الجزء 2): توجيه الرّوابط في Express

يُعد توجيه الروابط (Routing) من المفاهيم الجوهرية في أي تطبيق ويب مبني باستخدام إطار العمل Express، حيث يُمثل الرابط طريقة التفاعل الرئيسية بين المستخدم والتطبيق. يُتيح نظام التوجيه في Express تعريف نقاط الوصول المختلفة (Routes) والتي تمثل عناوين URL تقوم بتنفيذ وظائف معينة على حسب الطلب الوارد من المستخدم. في الجزء الأول من هذه السلسلة، تم التركيز على إعداد بيئة العمل الأساسية لإنشاء مدونة باستخدام Express، بينما سيتم تخصيص هذا الجزء للحديث المفصّل عن توجيه الروابط في Express، وتطبيقه العملي في مشروع مدونة متكامل.

توجيه الروابط في Express: المفهوم والوظيفة

نظام التوجيه في Express هو آلية يتم من خلالها مطابقة الطلبات الواردة (HTTP Requests) مع وظائف معينة في التطبيق. يُمكن تعريفه ببساطة على أنه عملية ربط عنوان URL بطريقة الطلب (GET، POST، PUT، DELETE، إلخ) مع إجراء أو عملية تتم على الخادم. على سبيل المثال، عندما يزور المستخدم الصفحة الرئيسية للمدونة عبر الرابط /، فإن التطبيق يرسل إليه الصفحة الرئيسية. بينما عند زيارته للرابط /posts، فإنه يتوقع أن يرى قائمة بجميع المنشورات.

يمثل Express هذا النظام من خلال دوال مثل:

javascript
app.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، سيتم اعتماد بنية منظمة للملفات تسهل عملية إدارة الروابط وفصل المسؤوليات:

markdown
blog-app/ ├── app.js ├── routes/ │ └── posts.js ├── controllers/ │ └── postsController.js └── views/ └── ...

ملف app.js

هذا الملف هو نقطة الدخول الرئيسية للتطبيق، ويحتوي على إعدادات Express العامة وربط المسارات المختلفة بالتطبيق.

javascript
const 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

في هذا الملف يتم تعريف جميع المسارات المتعلقة بالمنشورات، مع الإشارة إلى الوظائف المناسبة من وحدة التحكم:

javascript
const 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”:

javascript
let 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، كما في المثال:

javascript
router.get('/posts/:id', (req, res) => { const postId = req.params.id; res.send(`المنشور رقم: ${postId}`); });

هذه الآلية تُستخدم بكثرة في تحديد المنشورات، الصفحات، التصنيفات، أو أي عنصر ديناميكي آخر داخل المدونة.

التوجيه الديناميكي والتفريع (Route Grouping)

يسمح Express بتنظيم التوجيهات بشكل ديناميكي من خلال تجميعها في وحدات Routes مستقلة، كما تم بيانه أعلاه باستخدام مجلد routes. هذا يساعد في الحفاظ على قابلية التوسع وسهولة إدارة التطبيق.

كما يمكن ربط المسارات بشكل هرمي:

javascript
app.use('/admin/posts', adminPostsRoutes); app.use('/user/posts', userPostsRoutes);

توجيه الروابط باستخدام express.Router()

تمثل وحدة Router ميزة قوية في Express لإنشاء مجموعات توجيه مستقلة. من خلالها يمكن بناء نظام مدونة يحتوي على:

  • router.get('/create'): صفحة إنشاء منشور.

  • router.get('/:slug'): عرض منشور مفصل حسب العنوان (Slug).

  • router.post('/store'): تخزين المنشور الجديد.

معالجة الأخطاء في التوجيه

يُعتبر التعامل مع الأخطاء جزءاً لا يتجزأ من تطوير نظام توجيه احترافي. يُمكن إضافة موجه أخطاء عالمي لتسجيل أو الرد على الطلبات غير المعالجة:

javascript
app.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) بعمليات تعتمد على استعلامات قاعدة البيانات. مثلاً:

javascript
Post.find({}, (err, posts) => { if (err) return res.status(500).send(err); res.json(posts); });

تكامل التوجيه مع المحركات القالبية (Template Engines)

عند إنشاء صفحات الويب الديناميكية، تُستخدم محركات القوالب مثل EJS أو Pug. في هذه الحالة، تُرسل الردود من خلال ملفات عرض:

javascript
res.render('post', { post });

ويُستخدم ذلك في المسارات التالية:

javascript
router.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 واحدة:

javascript
app.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. بفضل بساطته وقدرته على التوسع، يمكن للمطور بناء نظام مدونة مرن وقابل للتطوير من خلال بنية توجيه منظمة، وبتكامل مع وحدات التحكم وقواعد البيانات، ومحركات العرض. هذا التوجيه لا يساهم فقط في تنظيم الكود بل يرفع من مستوى أمان وسرعة التطبيق ويجعل الصيانة أسهل بكثير على المدى الطويل.

المراجع