Node.js: التوجيه والوسيط
آخر تحديث: 2026-08-26
كان مشروع واجهة برمجة التطبيقات (API) الخاص بـ«تشارلي» يضم في البداية خمس مسارات فقط، وجميعها مكتوبة بلغة app.js، وكان الكود لا يزال واضحًا إلى حد ما. وبعد شهرين، ارتفع عدد المسارات إلى 30، وتجاوز حجم الملف 500 سطر — وأصبح إجراء تغيير واحد على نقطة نهاية API يستغرق وقتًا طويلاً للغاية. قرر الفريق استخدام express.Router() لتقسيم المسارات إلى وحدات — حيث تم نقل مسارات المستخدمين إلى users.js، ومسارات المصادقة إلى auth.js، وتم نقل البرمجيات الوسيطة المخصصة للتسجيل إلى ملف منفصل. بعد التقسيم، أصبح لكل ملف مسؤولية واحدة، وتحسنت كفاءة الصيانة بشكل ملحوظ.
ستتعلم:
- استخدم
express.Router()لتقسيم المسارات المعيارية - كيفية قراءة معلمات المسار وسلاسل الاستعلام ونصوص الطلبات
- كتابة برامج الوسيطة المخصصة وآلية
next() - دمج برامج الوسيطة (CORS / Morgan / Helmet) من جهات خارجية
- تصميم البرمجيات الوسيطة لمعالجة الأخطاء ومعالجة الأخطاء على المستوى الشامل
1. express.Router() Modular Routing
مع تزايد عدد المسارات، قد يؤدي وضع جميع المسارات في app.js إلى تضخم حجم الملفات ويجعل التعاون صعبًا. تتيح لك express.Router() إنشاء مثيلات مستقلة للمسارات ثم ربطها ببادئة مسار محددة في تطبيقك.
| ميزة | تحديد المسارات مباشرةً داخل التطبيق | التوجيه المعياري عبر وحدة التوجيه (Router) |
|---|---|---|
| تنظيم الملفات | كل شيء في ملف app.js | تقسيمها إلى ملفات منفصلة حسب الوحدات النمطية |
| بادئة المسار | حدد المسار الكامل لكل جذر | تعيين بادئة موحدة عند التثبيت |
| التعاون بين أعضاء الفريق | قيام عدة مستخدمين بتحرير الملف نفسه | الصيانة المستقلة لكل وحدة |
| قابلية إعادة الاستخدام | منخفضة | عالية؛ يمكن إعادة استخدامها عبر المشاريع المختلفة |
▶ مثال: إنشاء وحدة توجيه مستقلة للمستخدمين
// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({ users: ['Alice', 'Bob'] });
});
router.get('/:id', (req, res) => {
res.json({ user: req.params.id });
});
module.exports = router;
▶ مثال: تضمين وحدة مسار في ملف app.js
// app.js
const express = require('express');
const app = express();
const userRouter = require('./routes/users');
app.use('/users', userRouter);
app.listen(3000, () => {
console.log('Server running on port 3000');
});
الزيارة إلى /users تتطابق مع router.get('/')، والزيارة إلى /users/42 تتطابق مع router.get('/:id').
2. معلمات المسار وسلاسل الاستعلام ونصوص الطلبات
يقدم Express ثلاث طرق لتلقي بيانات العملاء، كل منها مناسبة لسيناريوهات مختلفة.
| مصدر البيانات | كيفية الوصول | مثال على عنوان URL | الاستخدامات الشائعة |
|---|---|---|---|
| معلمات التوجيه | req.params |
/users/42 |
تحدد موردًا معينًا |
| سلسلة الاستعلام | req.query |
/users?role=admin |
التصفية، الفرز، البحث |
| نص الطلب | req.body |
نص طلب POST | إرسال بيانات النموذج/JSON |
▶ مثال: معلمة المسار :id
app.get('/products/:id', (req, res) => {
const productId = req.params.id;
res.json({ productId });
});
▶ مثال: استعلام سلسلة req.query
app.get('/products', (req, res) => {
const { category, sort, page } = req.query;
res.json({ category, sort, page: page || 1 });
});
الوصول إلى /products?category=electronics&sort=price&page=2 وreq.query و{ category: 'electronics', sort: 'price', page: '2' }.
▶ مثال: نص الطلب req.body وexpress.json()
app.use(express.json());
app.post('/products', (req, res) => {
const { name, price } = req.body;
res.status(201).json({ name, price });
});
ملاحظة: قيمتا
req.queryوreq.paramsهما سلسلتان نصيتان ويجب تحويلهما يدويًّا إلى أرقام.
3. أساسيات البرمجيات الوسيطة وآلية next()
تعد البرمجيات الوسيطة مفهومًا أساسيًّا في Express — فكل طلب يمر عبر سلسلة من البرمجيات الوسيطة، ويمكن لكل دالة من دوال البرمجيات الوسيطة قراءة الطلب، أو تعديل الاستجابة، أو تمرير التحكم إلى دالة البرمجيات الوسيطة التالية.
graph LR A[Request] --> B[morgan Log] B --> C[express.json Analysis] C --> D[Custom auth Middleware] D --> E[Route Handling Functions] E --> F[Response] D -->|next error| G[Error-handling middleware]
▶ مثال: أبسط برنامج وسيط مخصص
function logger(req, res, next) {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
}
app.use(logger);
| القاعدة | الوصف |
|---|---|
يجب استدعاء next() |
وإلا، فسيتم تعليق الطلب، ولن يتلقى العميل أي رد على الإطلاق |
| ترتيب الاستدعاء هو ترتيب التنفيذ | يتم تنفيذ البرامج الوسيطة المسجلة أولاً أولاً |
app.use() ينطبق بشكل عام |
app.use('/api', ...) ينطبق فقط على المسار /api |
next('route') |
تخطي برامج الوسيطة المتبقية للمسار الحالي |
4. التكامل مع برامج الوسيطة التابعة لأطراف ثالثة
يوفر المجتمع مجموعة واسعة من برامج الوسيطة الجاهزة للاستخدام، والتي يمكن تفعيلها بسطر واحد من التعليمات البرمجية بعد التثبيت.
| البرامج الوسيطة | الغرض | أمر التثبيت |
|---|---|---|
| cors | حل مشكلات الطلبات عبر الأصول | npm install cors |
| مورغان | تسجيل طلبات HTTP | npm install morgan |
| خوذة | تجهيز خوذة للاستجابة في حالات الطوارئ | npm install helmet |
| express-rate-limit | حد معدل الطلبات | npm install express-rate-limit |
| cookie-parser | تحليل ملفات تعريف الارتباط | npm install cookie-parser |
▶ مثال: دمج CORS وMorgan وHelmet
const cors = require('cors');
const morgan = require('morgan');
const helmet = require('helmet');
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json());
ملاحظة: يُنصح بوضع البرامج الوسيطة المتعلقة بالأمان (Helmet) أولاً، تليها مباشرةً البرامج الوسيطة الخاصة بالتسجيل.
5. البرمجيات الوسيطة لمعالجة الأخطاء
اتفاقية Express: الدالة ذات المعلمات الأربعة (err, req, res, next) هي برمجية وسيطة لمعالجة الأخطاء. وعندما تستدعي أي برمجية وسيطة الدالة next(err)، فإنها تتخطى البرمجيات الوسيطة العادية التالية وتنتقل مباشرةً إلى البرمجية الوسيطة لمعالجة الأخطاء.
| عنصر المقارنة | البرمجيات الوسيطة القياسية | البرمجيات الوسيطة لمعالجة الأخطاء |
|---|---|---|
| عدد المعلمات | 3 (req، res، next) | 4 (err، req، res، next) |
| طريقة التشغيل | التنفيذ بالتتابع عند وصول الطلبات | next(err) التشغيل |
| مكان التسجيل | أي مكان | يجب أن يوضع في النهاية |
| هل يمكن أن يكون هناك أكثر من واحد؟ | نعم | نعم، يتم تنفيذها بالترتيب |
▶ مثال: برمجيات وسيطة عالمية لمعالجة الأخطاء
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || 'Internal Server Error'
});
});
▶ مثال: إحداث خطأ في المسار
app.get('/admin', (req, res, next) => {
const err = new Error('Access denied');
err.status = 403;
next(err);
});
6. Mermaid: الهيكل المعياري لتوجيه البيانات في جهاز التوجيه
graph TD APP[app.js] --> UR["/users → userRouter"] APP --> AR["/auth → authRouter"] APP --> MW["middleware/logger.js"] UR --> U1["GET / → User List"] UR --> U2["GET /:id → User Details"] UR --> U3["POST / → Create a User"] AR --> A1["POST /login → Log In"] AR --> A2["POST /register → Register"] MW --> ML["Logging Middleware"]
7. مثال شامل: واجهة برمجة تطبيقات التوجيه المعيارية
في هذا المثال، يتم تقسيم برامج الوساطة الخاصة بتوجيه المستخدم وتوجيه المصادقة والتسجيل إلى ملفات منفصلة، ثم يتم دمجها في النهاية في app.js.
▶ مثال: middleware/logger.js — برنامج وسيط مخصص للتسجيل
الدالة logger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`);
});
next();
}
module.exports = logger;
▶ مثال: routes/users.js — وحدة مسارات المستخدمين
const express = require('express');
const router = express.Router();
let users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
router.get('/', (req, res) => {
res.json(users);
});
router.get('/:id', (req, res, next) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
const err = new Error('User not found');
err.status = 404;
return next(err);
}
res.json(user);
});
router.post('/', (req, res) => {
const { name } = req.body;
if (!name) {
return res.status(400).json({ error: 'Name is required' });
}
const newUser = { id: users.length + 1, name };
users.push(newUser);
res.status(201).json(newUser);
});
module.exports = router;
▶ مثال: routes/auth.js — وحدة مسارات المصادقة
const express = require('express');
const router = express.Router();
router.post('/login', (req, res) => {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ error: 'Username and password required' });
}
res.json({ message: 'Login successful', token: 'mock-jwt-token' });
});
router.post('/register', (req, res) => {
const { username, email, password } = req.body;
if (!username || !email || !password) {
return res.status(400).json({ error: 'All fields are required' });
}
res.status(201).json({ message: 'Registration successful' });
});
module.exports = router;
▶ مثال: app.js — نقطة دخول التكامل
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const logger = require('./middleware/logger');
const userRouter = require('./routes/users');
const authRouter = require('./routes/auth');
const app = express();
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json());
app.use(logger);
app.use('/users', userRouter);
app.use('/auth', authRouter);
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || 'Internal Server Error'
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
تشغيل الاختبار:
node app.js
Server running on port 3000
GET /users 200 3ms
GET /users/1 200 2ms
POST /auth/login 200 5ms
❓ أسئلة شائعة
next()؟next() في البرمجيات الوسيطة لتمرير التحكم، أو استخدام طرق مثل res.end() أو res.json() لإنهاء الاستجابة مباشرةً.express.json() قبل أي مسار يحتاج إلى قراءة req.body؛ وإلا فإن req.body سيصبح undefined.app.use() بعد جميع المسارات والبرمجيات الوسيطة. وسيتم توجيه أي برمجية وسيطة تستدعي next(err) إلى هذا المعالج.Access-Control-Allow-Origin إلى رؤوس الاستجابة، مما يسمح لتطبيقات الواجهة الأمامية من منشآت محددة بالوصول إلى واجهة برمجة التطبيقات (API).express.Router() واستخدام app.get() مباشرةً؟Router بإنشاء مثيل موجه مستقل يمكن ربطه بأي بادئة مسار، مما يجعله مناسبًا للتقسيم إلى وحدات؛ أما app.get() فيتم تسجيله مباشرةً على مثيل التطبيق، مما يجعله مناسبًا للمشاريع البسيطة. كلاهما متكافئان من الناحية الوظيفية؛ ويكمن الاختلاف في طريقة تنظيمهما.req في برنامج وسيط لتمرير البيانات إلى المراحل التالية من المعالجة؟req، مثل req.user = { id: 1 }. وبذلك، يمكن لبرامج الوسيطة والمسارات التالية الوصول إلى هذه البيانات عبر req.user. وهذا نمط شائع للتواصل بين برامج الوسيطة في Express.📖 ملخص
- 1 express.Router(): المفاهيم الأساسية واستخدامات التوجيه المعياري
- 2 المفاهيم الأساسية واستخدامات معلمات المسار وسلاسل الاستعلام ونصوص الطلبات
- 3 المفاهيم الأساسية واستخدامات أساسيات البرمجيات الوسيطة وآلية next()
- 4 مفاهيم أساسية وطرق استخدام لتكامل البرمجيات الوسيطة التابعة لجهات خارجية
- 5 مفاهيم أساسية واستخدامات برامج الوسيطة لمعالجة الأخطاء
- 6 Mermaid: المفاهيم الأساسية واستخدام هيكل التوجيه المعياري في وحدة التوجيه
- 7 مثال شامل: المفاهيم الأساسية واستخدام واجهة برمجة تطبيقات التوجيه المعياري (API)
📝 تمارين
- أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
- قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
- راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
- التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
- حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.