تخطَّ إلى المحتوى

🔌 شرح REST APIs

Webhooks — REST يتواصل بلا طلب من العميل

الدرس 32 من 32· ⏱ 4 دقائق قراءة

المشكلة: "هل تغيّر شيء؟"

تخيّل تطبيقك محتاج يعرف فورًا لما عملية دفع تنجح عند مزوّد خارجي (Stripe مثلًا). الحل الساذج: تسأله كل شوي.

كل 10 ثواني:  GET /payments/123/status

لو عندك 1000 عملية نشطة، هذا 6000 طلب بالدقيقة — معظمها بيرجّع "لسا ما تغيّر شي". هذا اسمه Polling (استطلاع): مضيعة موارد، بياكل حصتك من الـ rate limit عند المزوّد، وبرضه فيه تأخير لحد الاستطلاع التالي.

الحل: Webhooks

Webhook هو عكس REST المعتاد بالاتجاه: بدل ما عميلك يسأل الخادم، الخادم (المزوّد) هو يلي بيرسل طلب POST لرابط حددته أنت، لحظة ما يصير حدث معيّن. بمصطلح تاني: REST عادي "سحب" (pull) — أنت بتطلب البيانات. Webhook "دفع" (push) — البيانات بتوصلك أول ما تصير.

بدل: أنت تسأل "هل الدفع نجح؟" كل 10 ثواني
     ↓
Webhook: المزوّد يرسل POST /webhooks/payment لحظة نجاح الدفع فعليًا

هذا النمط أساس أنظمة كبيرة كتير — Stripe وGitHub وShopify وSlack كلها بتعتمد Webhooks كآلية الإشعار الأساسية بدل إجبار عملائها على الاستطلاع.

استقبال Webhook — نقطة نهاية عادية

من ناحية الخادم عندك، الـ webhook مجرد مسار POST عادي بانتظار طلبات من طرف خارجي بدل مستخدمك:

app.post("/webhooks/payment", express.json(), (req, res) => {
  const event = req.body;

  if (event.type === "payment.succeeded") {
    // حدّث حالة الطلب بقاعدة البيانات
  }

  res.status(200).json({ received: true });   // أخبر المُرسِل إنك استلمت
});

الفرق الجوهري عن أي مسار عادي: المُرسِل مو مستخدمك المصادَق عليه بجلسة أو JWT — هو خادم خارجي. هذا يفتح سؤال أمان مهم: كيف تتأكد إن الطلب فعلًا من المزوّد يلي تتوقعه، مو من أي شخص عرف رابطك وأرسل POST مزيّف؟

الأمان — توقيع HMAC

المعيار المتّبع (تستخدمه GitHub وStripe وأغلب المزوّدين الكبار): المُرسِل يوقّع الجسم بمفتاح سري مشترك (Signing Secret) عبر HMAC-SHA256، ويرفق التوقيع بترويسة مخصَّصة. خادمك يعيد حساب نفس التوقيع محليًا ويقارنه.

المزوّدالترويسةالخوارزمية
GitHubX-Hub-Signature-256HMAC-SHA256
StripeStripe-SignatureHMAC-SHA256 (مع طابع زمني ضمن التوقيع)
import crypto from "node:crypto";

function verifySignature(payload, signatureHeader, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  // مقارنة بزمن ثابت (constant-time) — تمنع هجمات القياس الزمني (timing attacks)
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}

app.post(
  "/webhooks/github",
  express.raw({ type: "application/json" }),   // الجسم الخام مطلوب للتوقيع، لا JSON محلَّل
  (req, res) => {
    const signature = req.headers["x-hub-signature-256"];
    if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).json({ error: "توقيع غير صالح" });
    }
    // آمن الآن — تابع معالجة الحدث
    res.status(200).json({ received: true });
  }
);

⚠️ لازم تقارن التوقيعين بدالة "مقارنة بزمن ثابت" متل crypto.timingSafeEqual لا عبر === العادي — المقارنة العادية بتكشف معلومات عن التوقيع الصحيح حرفًا حرفًا عبر فرق التوقيت الدقيق، وهذا بالضبط ما يستغله هجوم "timing attack".

أفضل الممارسات

  • رد بـ 200 بسرعة، عالج لاحقًا. المزوّد بينتظر ردًا خلال مهلة قصيرة؛ لو منطق المعالجة بطيء (إرسال بريد، تحديث معقّد)، ضع الحدث بطابور (queue) ورد فورًا، عالجه بشكل غير متزامن.
  • تعامل مع التكرار. أغلب المزوّدين بيعيدون إرسال نفس الحدث لو ما استلموا 200 بالوقت المحدد (Stripe مثلًا بيعيد المحاولة لعدة أيام). خزّن معرّف كل حدث معالَج (event.id) وتجاهل التكرار.
  • تحقق من التوقيع دائمًا. بدون توقيع HMAC، أي شخص عرف رابط الـ webhook فيك يقدر يرسل أحداث مزيّفة.
  • لا تثق بمحتوى الحدث فقط لأنه "من مزوّد موثوق". تحقق من التوقيع أولًا، بعدين عالج — مو العكس.

خلاصة

REST عادي (Polling)Webhook
الاتجاهأنت تسأل الخادمالخادم يرسل لك
التأخيريعتمد على تكرار الاستطلاعفوري تقريبًا
استهلاك المواردمرتفع (طلبات متكررة بلا فائدة غالبًا)منخفض (طلب واحد عند وقوع الحدث فعليًا)
الأمانمصادقة عادية (JWT/API Key)توقيع HMAC للتحقق من هوية المُرسِل

Webhooks مش بديل عن REST APIs — هي إضافة له. غالبًا بتستخدم الاثنين معًا: الـ webhook يخبرك إن حدث وقع، وطلب REST عادي بعده يجيب لك التفاصيل الكاملة لو احتجتها.

🎯 العودة إلى قائمة دروس REST API

شرح Webhooks — REST يتواصل بلا طلب من العميل — REST APIs بالعربي
Webhooks — REST يتواصل بلا طلب من العميلREST APIs بالعربي · The Code Fix

📚 لمزيد من التعمّق في REST APIs، راجِع توثيق HTTP على MDN.

هل كان هذا الدرس مفيدًا؟