Kodax

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);
}));
اختبر فهمك