طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 302 · من الفكرة إلى المنتج (٢): نظام حجز مواعيد
الدرس 3 · الوحدة 2: بناء الخادم والواجهة البرمجية

المحطّة ٣: خادم Express ومسارات المواعيد

طرف الخادم يبدأ: اقرأ المواعيد وصفِّ المتاح بغلافٍ موحّد. ⏱ نحو 30 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تبني خادم Express يقدّم موارد مشروعك.
  • تطبّق الغلاف الموحّد {data, meta} من عقد الـ API.
  • تصفّي النتائج بمعاملات الاستعلام.
  • تختار رمز الحالة الصحيح لكل نتيجة.

١من العقد إلى خادمٍ يعمل

معنا من المحطّة السابقة عقد الـ API. نبدأ ببناء طرف الخادم: تطبيق Express يحفظ المواعيد (في الذاكرة الآن، وفي قاعدة بياناتٍ لاحقًا) ويقدّمها عبر المسارات المتّفق عليها. هذا عملٌ أتقنته في مقرّر 242؛ هنا نطبّقه على مشروعنا.

نبدأ بالقراءة: قائمة المواعيد، والتصفية بالمتاح، وموعدٌ واحدٌ بمعرّفه. جرّب الخادم — أرسِل الطلبات وراقب الردود:

const express = require('express');const app = express();app.use(express.json()); let appts = [  { id: 1, doctor: 'د. منى',  time: '09:00', bookedBy: null },  { id: 2, doctor: 'د. منى',  time: '09:30', bookedBy: 'سارة' },  { id: 3, doctor: 'د. أحمد', time: '10:00', bookedBy: null },]; app.get('/api/appointments', (req, res) => {  let data = appts;  if (req.query.status === 'free') data = appts.filter(a => a.bookedBy === null);  res.json({ data, meta: { total: data.length } });}); app.get('/api/appointments/:id', (req, res) => {  const a = appts.find(x => x.id === Number(req.params.id));  if (!a) return res.status(404).json({ error: { code: 'NOT_FOUND', message: 'لا يوجد موعد' } });  res.json({ data: a });}); app.listen(3000);

لاحظ الغلاف الموحّد { data, meta }، ورمز 404 مع { error } للموعد غير الموجود — كما صمّمنا في العقد.

٢التصفية بمعامل الاستعلام

المسار نفسه يخدم حالتين بحسب ?status=free: كل المواعيد، أو المتاحة فقط. هذا أنظف من مسارين منفصلين:

app.get('/api/appointments', (req, res) => {  let data = appts;  if (req.query.status === 'free') data = appts.filter(a => a.bookedBy === null);  res.json({ data, meta: { total: data.length } });});

المتاح هو ما bookedBy فيه null — الحقل الذي صمّمناه في النموذج يؤدّي دوره الآن.

توقّع وتحقّق

ما الذي يعيد المواعيد المتاحة فقط؟

لماذا Number(req.params.id)؟

ما رمز الحالة للموعد غير الموجود؟

أين نعالج جسم JSON في الطلب؟

الخلاصة

تحدٍّ برمجي: واجهة قراءة المواعيد

برمجي

ابنِ طرف القراءة من خادم الحجز: قائمة المواعيد مع تصفية المتاح، وموعدٌ واحدٌ بمعرّفه — بالغلاف الموحّد الذي صمّمته.

المطلوب:
  • GET /api/appointments: يرد {"data": [...], "meta": {"total": n}}، ويقبل ?status=free للمتاح فقط.
  • GET /api/appointments/:id: يرد {"data": …} للموعد، أو 404 مع {"error": {"code": "NOT_FOUND"}}.
👀 اعرض الاستجابات المطلوبة
✓ التحقق الآلي: يشغّل المختبر خادمك من جديد ويرسل إليه هذه الطلبات بالترتيب ثم يفحص الردود
  1. GET /api/appointments
  2. GET /api/appointments?status=free
  3. GET /api/appointments/2
  4. GET /api/appointments/99
  • الخادم يعمل ويرد على كل الطلبات بلا أخطاء غير معالَجة
  • القائمة بغلاف data و meta.total = 3
  • التصفية ?status=free تعيد المتاحين فقط (2)
  • الموعد المتاح الأوّل ضمن نتيجة free
  • موعدٌ واحدٌ بمعرّفه
  • غير الموجود ← 404
  • … مع رمز NOT_FOUND

💡 تلميح 1
التصفية: req.query.status === "free" ثم filter(a => a.bookedBy === null).
💡 تلميح 2
للموعد الواحد: find(x => x.id === Number(req.params.id))، وإن لم يوجد فـ 404.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

const express = require('express');const app = express();app.use(express.json()); let appts = [  { id: 1, doctor: 'د. منى',  time: '09:00', bookedBy: null },  { id: 2, doctor: 'د. منى',  time: '09:30', bookedBy: 'سارة' },  { id: 3, doctor: 'د. أحمد', time: '10:00', bookedBy: null },]; app.get('/api/appointments', (req, res) => {  let data = appts;  if (req.query.status === 'free') data = appts.filter(a => a.bookedBy === null);  res.json({ data, meta: { total: data.length } });}); app.get('/api/appointments/:id', (req, res) => {  const a = appts.find(x => x.id === Number(req.params.id));  if (!a) return res.status(404).json({ error: { code: 'NOT_FOUND', message: 'لا يوجد موعد' } });  res.json({ data: a });}); app.listen(3000);

🐞 لاحظت خطأً في هذا الدرس؟ أبلغني بالبريد

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس