المحطّة ٣: خادم 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 في الطلب؟
الخلاصة
- الخادم يحفظ المواعيد ويقدّمها عبر المسارات المتّفق عليها.
- التصفية بمعامل الاستعلام ?
status=freeتخدم حالتين بمسارٍ واحد. - المتاح =
bookedBy === null(الحقل من نموذج البيانات). - حوّل معاملات المسار بـ
Numberقبل المقارنة بالمعرّفات. - الغلاف
{data, meta}ورمز 404 مع{error}ينفّذان العقد.
تحدٍّ برمجي: واجهة قراءة المواعيد
برمجيابنِ طرف القراءة من خادم الحجز: قائمة المواعيد مع تصفية المتاح، وموعدٌ واحدٌ بمعرّفه — بالغلاف الموحّد الذي صمّمته.
GET /api/appointments: يرد{"data": [...], "meta": {"total": n}}، ويقبل?status=freeللمتاح فقط.GET /api/appointments/:id: يرد{"data": …}للموعد، أو 404 مع{"error": {"code": "NOT_FOUND"}}.
👀 اعرض الاستجابات المطلوبة
GET /api/appointmentsGET /api/appointments?status=freeGET /api/appointments/2GET /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);