Middleware في Express — REST APIs
الوسائط (Middleware) في Express
Middleware دالة تقع بين وصول الطلب والمعالج النهائي. Express كله سلسلة منها:
الطلب ← تسجيل ← JSON ← المصادقة ← التحقق ← المعالج ← الاستجابة
(أي حلقة تستطيع إيقاف السلسلة بإرسال استجابة)التوقيع
function logger(req, res, next) {
const start = Date.now();
res.on("finish", () => console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${Date.now() - start}ms`));
next(); // مرّر للحلقة التالية — بدونها يعلق الطلب للأبد
}كل Middleware إما أن يرسل استجابة أو يستدعي next(). نسيان الاثنين = طلب معلّق حتى انتهاء المهلة.
أين تُركّب؟
const express = require("express");
const app = express();
app.use(express.json({ limit: "100kb" })); // لكل الطلبات — مع حد للحجم
app.use("/api", (req, res, next) => next()); // لكل ما يبدأ بـ /api
function requireJson(req, res, next) {
if (!req.is("application/json")) return res.status(415).json({ error: "JSON فقط" });
next();
}
app.post("/api/books", requireJson, (req, res) => res.status(201).json(req.body)); // لمسار واحدالترتيب مهم: express.json() يجب أن يأتي قبل المسارات التي تقرأ req.body.
Middleware شائعة
| الحزمة | الوظيفة |
|---|---|
| cors | السماح لنطاقات محددة (واجهتك) باستدعاء الـ API من المتصفح. حدد origin صراحةً؛ * مع ملفات تعريف الارتباط لا يعمل ولا يُنصح به |
| helmet | ترويسات أمان افتراضية (منع التضمين، تعطيل كشف التقنية...) |
| express-rate-limit | حد للطلبات لكل IP — ضروري لمسارات تسجيل الدخول |
| morgan أو pino-http | تسجيل الطلبات |
| cookie-parser | قراءة ملفات تعريف الارتباط |
معالج الأخطاء: أربعة معاملات
const express = require("express");
const app = express();
app.get("/boom", (req, res, next) => next(new Error("شيء ما فشل")));
// يُعرَّف بعد كل المسارات، وأول معامل هو الخطأ
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status || 500).json({ error: err.expose ? err.message : "خطأ في الخادم" });
});Express يتعرف عليه من عدد المعاملات (4). أي next(err) يقفز إليه مباشرة متجاوزاً بقية الوسائط.
تمرين
اكتب Middleware requestId يضيف req.id عشوائياً (crypto.randomUUID()) ويضعه في ترويسة الاستجابة X-Request-Id، واطبعه في سجل كل طلب وفي رسائل الأخطاء.
const express = require('express');
const app = express();
// ===== Built-in Middleware =====
app.use(express.json()); // Parse JSON
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded
app.use(express.static('public')); // Serve static files
// ===== Custom Middleware =====
// Logger middleware
const logger = (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
next(); // الانتقال للـ middleware التالي
};
app.use(logger);
// Request time middleware
app.use((req, res, next) => {
req.requestTime = Date.now();
next();
});
// ===== Route-specific Middleware =====
const checkAuth = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Token required' });
}
// verify token...
req.user = decodedUser;
next();
};
// تطبيق على route محدد
app.get('/api/profile', checkAuth, (req, res) => {
res.json({ user: req.user });
});
// تطبيق على مجموعة routes
app.use('/api/admin', checkAuth, adminRouter);
// ===== Error Handling Middleware =====
// يجب أن يكون آخر middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
success: false,
error: {
message: err.message || 'خطأ في الخادم',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
});
// ===== Async Middleware Helper =====
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/api/users', asyncHandler(async (req, res) => {
const users = await User.find();
res.json(users);
}));اختبر فهمك