4.1 · بناء واجهة برمجة تطبيقات REST كاملة (CRUD)

المستوى 4 · واجهة برمجة التطبيقات والتوثيق والزمن الحقيقي (API, authentication & real-time)

4.1بناء واجهة برمجة تطبيقات REST كاملة (CRUD)

الهدف: بناء واجهة برمجة تطبيقات (API) كاملة ومنظمة تنظيما جيدا من نوع REST ‏(CRUD والموارد وطرق HTTP الدلالية ورموز الحالة وMVC) — مُسلَّمة مركزية في خلفية Node.
الوقت المقدّر: 12 دقيقة · قراءة ذاتية · إذا كان لديك 5 دقائق فقط: اقرأ «الأساسي في 30 ثانية» + المخطط.

الأساسي في 30 ثانية

  • REST = الموارد + طرق HTTP : يحدد الرابط المورد بصيغة الجمع (/users)، وتحمل الطريقة الفعل (GET للقراءة، وPOST للإنشاء، وPUT للتعديل، وDELETE للحذف).
  • التنظيم في طبقات (MVC) : المسارات (الروابط) ← المتحكمات (المنطق) ← النماذج (البيانات) ؛ ويجمع express.Router() المسارات حسب المورد.
  • منعكس ساليم : إرجاع JSON مع الرمز الصحيح (201 بعد الإنشاء، و404 عند الغياب، و400 عند عدم الصلاحية) مع ترقيم الإصدار (/api/v1/).

حالة مصغرة متواصلة — ساليم يبني واجهة المنتجات في الجزائر

ساليم، مطور مبتدئ في شركة ناشئة في الجزائر، عليه أن يعرض كتالوج المنتجات للواجهة الأمامية : عرض القائمة، وعرض منتج، وإنشاء منتج، وتعديله، وحذفه. يصمم أولا نقاط النهاية REST ‏(GET /api/v1/produits، وGET /api/v1/produits/:id، وPOST، وPUT، وDELETE)، ويفصل المسارات والمتحكمات والبيانات، ويجمع المسارات في express.Router() مركَّب على التطبيق. وعندما تنادي الواجهة الأمامية واجهته، يصل كل رد بـ JSON مع رمز الحالة الصحيح. أول محطة في الخيط الرابط : واجهة متوقعة تخمنها الواجهة الأمامية دون توثيق.

الحالة نفسها تعود في الدرس والمخطط والنص الجاهز وتحدي 48 ساعة — وتستمر في 4.2 و4.3 و4.4.

1. مبادئ REST

REST أسلوب معماري لواجهات برمجة التطبيقات (API) : نعرض موارد (مستخدمين، منتجات) تحددها روابط واضحة بصيغة الجمع (/users، /produits)، ونتصرف عليها بـ طرق HTTP حسب دلالتها : GET /users (عرض القائمة)، وGET /users/:id (قراءة واحد)، وPOST /users (إنشاء)، وPUT/PATCH /users/:id (تعديل)، وDELETE /users/:id (حذف). وكما يفعل ساليم، لا نضع فعلا أبدا في الرابط : ليس /getUsers، بل GET /users.

ترجع الواجهة بيانات بـ JSON مع رموز الحالة الصحيحة : 200 نجاح، و201 أُنشئ (بعد POST)، و204 دون محتوى (بعد DELETE)، و400 طلب غير صالح، و404 غير موجود، و500 خطأ في الخادم. هذه الاتفاقيات المتسقة تجعل الواجهة متوقعة : تخمن الواجهة الأمامية نقاط النهاية بدلا من حفظها واحدة واحدة.

2. التنظيم في طبقات (MVC)

الواجهة التي تكبر دون بنية تصير طبق سباغيتي. والنمط الكلاسيكي هو MVC : النماذج (بنية البيانات والوصول إليها)، والمتحكمات (منطق معالجة كل طلب)، والمسارات (تعريف الروابط والطرق، التي تفوض إلى المتحكمات). مجلدات معتادة : routes/، وcontrollers/، وmodels/، إضافة إلى middlewares/، وservices/، وconfig/.

نستعمل موجه Express (express.Router()) لجمع المسارات حسب المورد في ملفات منفصلة (موجه للمنتجات، وموجه للطلبات)، مركبة على التطبيق. هذا الفصل يجعل الكود مفهوما وقابلا للصيانة والاختبار والتطور — ويسمح بالعمل جماعيا دون أن يدوس بعضنا على بعض.

3. الممارسات الجيدة لواجهة مصممة جيدا

ما يفصل الواجهة الاحترافية عن الواجهة التي « تشتغل » : روابط متسقة بصيغة الجمع، والطرق الصحيحة للأفعال الصحيحة، ورموز الحالة الصحيحة (أبدا 200 لخطأ)، ومعالجة نظيفة للأخطاء (انظر 4.4)، وترقيم الصفحات للقوائم، وترقيم الإصدار (/api/v1/...) لتطوير الواجهة دون كسر العملاء، وردود JSON منظمة. يطبق ساليم هذه القائمة على كل نقطة نهاية قبل أن يقول « انتهيت ».

خطأ شائع في الفهم: « واجهتي تشتغل، إذن هي مصممة جيدا : تصل البيانات إلى الواجهة الأمامية، وهذا هو الأساس ». لا. قد ترجع الواجهة البيانات الصحيحة وهي غير متسقة (أفعال في الروابط، وكل شيء بـ POST، و200 للأخطاء، والمنطق مكدس في المسارات) — ويُدفع ثمن هذا الدين حالما تكبر الواجهة الأمامية أو يصل عميل ثان. المصمم جيدا = متوقع، وقابل للصيانة، وقابل للاختبار — وليس شغالا في العرض فقط.
مسار طلب في الواجهة المسارات ← المتحكمات ← النماذج ← الرد المسارات الرابط + الطريقة(express.Router)المتحكمات منطقالمعالجةالنماذج البيانات (الذاكرة،Mongoose، Prisma)الرد JSON + رمزالحالة الصحيح
المسارات ← المتحكمات ← النماذج ← الرد
نص المخطط (SEO / قارئات الشاشة / الترجمة): 1) يحدد ساليم نقاط النهاية REST : يشير الرابط إلى المورد بصيغة الجمع، وتحمل طريقة HTTP الفعل. 2) يفصل المسارات (الروابط) والمتحكمات (المنطق) والنماذج (البيانات)، مع موجه Express لكل مورد. 3) ينطلق كل رد بـ JSON مع الرمز الصحيح : 201 بعد الإنشاء، و404 عند الغياب، و400 عند عدم الصلاحية. الدرس : متوقعة للواجهة الأمامية، وقابلة للصيانة للفريق.

🔎 مثال ملموس — 🔎 مثال ملموس — CRUD REST لمورد

جدول المطابقة الذي يعلقه ساليم فوق شاشته :

GET    /api/v1/produits      ← عرض القائمة (200)
GET    /api/v1/produits/:id  ← قراءة واحد (200، و404 عند الغياب)
POST   /api/v1/produits      ← إنشاء (201، و400 عند عدم الصلاحية)
PUT    /api/v1/produits/:id  ← تعديل (200، و404 عند الغياب)
DELETE /api/v1/produits/:id  ← حذف (204)

القراءة : خمسة أسطر، وخمسة أزواج طريقة + رابط، وخمسة رموز منتظرة.

الدرس : تحمل طريقة HTTP الفعل، وليس الرابط أبدا.

مفردات القسم

واجهة REST
أسلوب معماري لواجهات الويب : عرض موارد (روابط واضحة) تُدار بطرق HTTP حسب دلالتها، وردود بـ JSON مع رموز الحالة الصحيحة.

مثال بسيط: واجهة منتجات ساليم : GET للقراءة، وPOST للإنشاء، ورد بـ JSON.

ما ليس كذلك: مجرد سكريبت يرجع HTML مخلوطا بالمنطق.

رد فعل: تسمية المورد والطريقة والرمز المنتظر لكل نقطة نهاية.

المورد
كيان تعرضه الواجهة (منتجات، مستخدمون، طلبات)، يحدده رابط واضح بصيغة الجمع.

مثال بسيط: مورد منتجات ساليم يعيش تحت /api/v1/produits.

ما ليس كذلك: فعل لفظي مثل /creerProduit.

رد فعل: الرابط = اسم بصيغة الجمع، دون فعل.

MVC
فصل النماذج (البيانات) والمتحكمات (المنطق) والمسارات (الروابط) لكود قابل للصيانة والتطور.

مثال بسيط: يضع ساليم روابطه في routes/ ومنطقه في controllers/.

ما ليس كذلك: كل الكود مكدس في ملف app.js وحيد.

رد فعل: تعديل واحد يمس طبقة، لا الطبقات الثلاث.

express.Router()
أداة Express تجمع مسارات مورد في ملف منفصل، يُركَّب بعدها على التطبيق.

مثال بسيط: موجه منتجات ساليم مركب بـ app.use('/api/v1/produits', routeur).

ما ليس كذلك: إعلان كل المسارات مبعثرة في الملف الرئيسي.

رد فعل: ملف مسارات لكل مورد.

رمز الحالة
رقم HTTP يبلغ النتيجة : 200 نجاح، و201 أُنشئ، و204 دون محتوى، و400 غير صالح، و404 غائب، و500 خطأ في الخادم.

مثال بسيط: يرجع ساليم 201 بعد POST ناجح، و404 لمنتج مجهول.

ما ليس كذلك: رمز 200 منهجي حتى عند الخطأ.

رد فعل: يجب أن يفهم العميل النتيجة دون قراءة الجسم.

في التطبيق — عمليا — بناء واجهة REST منظمة

  1. صمموا نقاط نهاية مورد حسب REST : GET /items (عرض القائمة)، وGET /items/:id (قراءة)، وPOST /items (إنشاء)، وPUT /items/:id (تعديل)، وDELETE /items/:id (حذف).
  2. نظموا المشروع في طبقات : routes/ (الروابط)، وcontrollers/ (المنطق)، وmodels/ (البيانات) — كما فعل ساليم لكتالوجه.
  3. استعملوا express.Router() لجمع مسارات مورد في ملف، مركب على التطبيق (app.use('/api/v1/items', itemsRouter)).
  4. أرجعوا JSON مع رموز الحالة الصحيحة (201 بعد الإنشاء، و404 عند عدم الوجود، و400 عند عدم صلاحية البيانات) وتوقعوا ترقيم الصفحات وترقيم الإصدار.
تبنون واجهة REST كاملة ومنظمة تنظيما جيدا (CRUD والموارد وطرق HTTP الدلالية ورموز الحالة وMVC) — مُسلَّمة مركزية في خلفية Node.

نص جاهز للقول — عرض واجهته أمام الواجهة الأمامية (30 ثانية)

« معكم ساليم : تتبع واجهة المنتجات اتفاقيات REST — GET للقراءة، وPOST للإنشاء، وPUT للتعديل، وDELETE للحذف، تحت /api/v1/produits، وردود بـ JSON مع الرموز الصحيحة : 201 بعد الإنشاء، و404 عند الغياب، و400 عند عدم الصلاحية. والبنية في طبقات مسارات ومتحكمات ونماذج، لنضيف موارد دون كسر كل شيء. »

يُقال في مراجعة الكود ؛ واعرضوا جدول نقاط النهاية الخمس.

نموذج للنسخ واللصق — موجه CRUD بـ Express (كود مختبَر)

const express = require('express');
const routeur = express.Router();

let produits = [];
let prochainId = 1;

// GET /api/v1/produits ← عرض القائمة (200)
routeur.get('/', (req, res) => {
  res.json(produits);
});

// GET /api/v1/produits/:id ← قراءة واحد (200 / 404)
routeur.get('/:id', (req, res) => {
  const produit = produits.find((p) => p.id === Number(req.params.id));
  if (!produit) return res.status(404).json({ erreur: 'Produit introuvable' });
  res.json(produit);
});

// POST /api/v1/produits ← إنشاء (201 / 400)
routeur.post('/', (req, res) => {
  if (!req.body.nom) return res.status(400).json({ erreur: 'Le champ nom est requis' });
  const produit = { id: prochainId++, nom: req.body.nom };
  produits.push(produit);
  res.status(201).json(produit);
});

// PUT /api/v1/produits/:id ← تعديل (200 / 404)
routeur.put('/:id', (req, res) => {
  const produit = produits.find((p) => p.id === Number(req.params.id));
  if (!produit) return res.status(404).json({ erreur: 'Produit introuvable' });
  if (req.body.nom) produit.nom = req.body.nom;
  res.json(produit);
});

// DELETE /api/v1/produits/:id ← حذف (204 / 404)
routeur.delete('/:id', (req, res) => {
  const index = produits.findIndex((p) => p.id === Number(req.params.id));
  if (index === -1) return res.status(404).json({ erreur: 'Produit introuvable' });
  produits.splice(index, 1);
  res.status(204).end();
});

module.exports = routeur;
// التركيب (يتطلب express.json()) :
// const express = require('express');
// const app = express();
// app.use(express.json());
// app.use('/api/v1/produits', routeur);

نموذج أولي سريع

فصل خفيف يكفي (مسارات + منطق)، لكن حافظوا على اتفاقيات REST : لا تكلف شيئا وتتفادى إعادة الكتابة.

واجهة حقيقية في فريق

كما يفعل ساليم : طبقات كاملة (routes/ وcontrollers/ وmodels/ وservices/)، وموجه لكل مورد، ومراجعة كود على الرموز.

واجهة يستهلكها هاتف

اعتنوا بترقيم الصفحات وردود JSON المستقرة : يخزن الهاتف مؤقتا ويتألم من كل كسر للعقد.

واجهة يجب أن تتطور

رقموا الإصدار من البداية (/api/v1/) : ستأتي v2، وعملاء v1 لن ينكسروا.

حذر: الواجهة تعرض نظامكم للعالم : تحققوا دائما من المدخلات (انظر 4.4) ولا ترجعوا أبدا تفاصيل تقنية حساسة في الأخطاء. اختبروا كل نقطة نهاية بما فيها حالات الفشل (404 و400) قبل إعلان « انتهيت ». محتوى تعليمي، وأمثلة ببيانات وهمية.

✓ ممارسات جيدة

  • الرابط = مورد بصيغة الجمع، والفعل = طريقة HTTP.
  • التنظيم في طبقات حالما تصير الواجهة حقيقية.
  • إرجاع رمز الحالة الصحيح في كل مرة.

✗ أخطاء شائعة

  • وضع فعل في الرابط (/getProduits).
  • إرجاع 200 لكل شيء، حتى الأخطاء.
  • تكديس كل المنطق في ملفات المسارات.

النقاط الأساسية

  • واجهة REST : عرض موارد (روابط واضحة بصيغة الجمع) تُدار بـ طرق HTTP حسب دلالتها، بـ JSON مع رموز الحالة الصحيحة.
  • CRUD REST : ‏GET /produits (عرض القائمة)، وGET /produits/:id (قراءة)، وPOST (إنشاء، 201)، وPUT (تعديل)، وDELETE (حذف، 204).
  • التنظيم في طبقات (MVC) : النماذج والمتحكمات والمسارات + الخدمات والوسائط ؛ ويجمع express.Router() المسارات حسب المورد.
  • الممارسات الجيدة : روابط متسقة، ورموز صحيحة، ومعالجة أخطاء، وترقيم صفحات، وترقيم إصدار (/api/v1/). متوقعة واحترافية.

تحدي 48 ساعة — طبّق غدا

ابنوا موجه CRUD الكامل لمورد حقيقي واحد (نقاط النهاية الخمس، والرموز الصحيحة) واختبروا كل مسار بما فيه 404 و400.

قائمة الخروج من القسم
  • أستطيع ذكر نقاط CRUD الخمس مع رموزها.
  • أعرف فصل المسارات والمتحكمات والنماذج.
  • اختبرت موجها كاملا واحدا، بما فيه الإخفاقات.
تقييم ذاتي 1-5 : هل تحمل روابطي موارد دون فعل ؟هل رموز حالتي صحيحة على كل مسار ؟

أسئلة شائعة

ما الذي يميز واجهة REST « المصممة جيدا » عن الواجهة التي « تشتغل » فقط ؟

الاتفاقيات والبنية : روابط متسقة على الموارد (دون فعل)، وطرق HTTP الصحيحة حسب دلالتها، ورموز الحالة الصحيحة (201 و404 و400 — وليس 200 لكل شيء)، وطبقات MVC بدلا من السباغيتي، ومعالجة نظيفة للأخطاء (4.4)، والتحقق من المدخلات، وردود JSON منظمة، وترقيم الصفحات وترقيم الإصدار (/api/v1/). الواجهة المصممة جيدا متوقعة (تُخمَّن نقاط نهايتها)، وقابلة للصيانة وممتعة الاستهلاك — وهي حاسمة لأنها مصنوعة ليستعملها آخرون.

هل يجب فعلا التنظيم بـ MVC من البداية، حتى لواجهة صغيرة ؟

كيّفوا حسب المشروع، لكن افصلوا مبكرا : تكبر المشاريع غالبا دائما وتكلف إعادة هيكلة السباغيتي غاليا. نموذج أولي صغير : فصل خفيف (المسارات مقابل المنطق، مع express.Router()). واجهة حقيقية : طبقات كاملة من البداية (routes/ وcontrollers/ وmodels/ وservices/) — مفهومة، وقابلة للصيانة والاختبار والتعاون. لا هندسة مفرطة لتمرين، ولا خلط شامل « لأنها صغيرة ».

تحقّق من فهمك

في واجهة REST، من يحمل الفعل (الإنشاء، والحذف) ؟

موارد إضافية (اختياري، للتعمّق أكثر)

اختبار المستوى 4أسئلة جديدة وأصعب للتحقق من مكتسباتك