4.1بناء واجهة برمجة تطبيقات REST كاملة (CRUD)
الأساسي في 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 منظمة. يطبق ساليم هذه القائمة على كل نقطة نهاية قبل أن يقول « انتهيت ».
🔎 مثال ملموس — 🔎 مثال ملموس — 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 منظمة
- صمموا نقاط نهاية مورد حسب REST : GET /items (عرض القائمة)، وGET /items/:id (قراءة)، وPOST /items (إنشاء)، وPUT /items/:id (تعديل)، وDELETE /items/:id (حذف).
- نظموا المشروع في طبقات : routes/ (الروابط)، وcontrollers/ (المنطق)، وmodels/ (البيانات) — كما فعل ساليم لكتالوجه.
- استعملوا express.Router() لجمع مسارات مورد في ملف، مركب على التطبيق (app.use('/api/v1/items', itemsRouter)).
- أرجعوا JSON مع رموز الحالة الصحيحة (201 بعد الإنشاء، و404 عند عدم الوجود، و400 عند عدم صلاحية البيانات) وتوقعوا ترقيم الصفحات وترقيم الإصدار.
نص جاهز للقول — عرض واجهته أمام الواجهة الأمامية (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 لن ينكسروا.
✓ ممارسات جيدة
- الرابط = مورد بصيغة الجمع، والفعل = طريقة 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 الخمس مع رموزها.
- أعرف فصل المسارات والمتحكمات والنماذج.
- اختبرت موجها كاملا واحدا، بما فيه الإخفاقات.
أسئلة شائعة
ما الذي يميز واجهة REST « المصممة جيدا » عن الواجهة التي « تشتغل » فقط ؟
الاتفاقيات والبنية : روابط متسقة على الموارد (دون فعل)، وطرق HTTP الصحيحة حسب دلالتها، ورموز الحالة الصحيحة (201 و404 و400 — وليس 200 لكل شيء)، وطبقات MVC بدلا من السباغيتي، ومعالجة نظيفة للأخطاء (4.4)، والتحقق من المدخلات، وردود JSON منظمة، وترقيم الصفحات وترقيم الإصدار (/api/v1/). الواجهة المصممة جيدا متوقعة (تُخمَّن نقاط نهايتها)، وقابلة للصيانة وممتعة الاستهلاك — وهي حاسمة لأنها مصنوعة ليستعملها آخرون.
هل يجب فعلا التنظيم بـ MVC من البداية، حتى لواجهة صغيرة ؟
كيّفوا حسب المشروع، لكن افصلوا مبكرا : تكبر المشاريع غالبا دائما وتكلف إعادة هيكلة السباغيتي غاليا. نموذج أولي صغير : فصل خفيف (المسارات مقابل المنطق، مع express.Router()). واجهة حقيقية : طبقات كاملة من البداية (routes/ وcontrollers/ وmodels/ وservices/) — مفهومة، وقابلة للصيانة والاختبار والتعاون. لا هندسة مفرطة لتمرين، ولا خلط شامل « لأنها صغيرة ».
في واجهة REST، من يحمل الفعل (الإنشاء، والحذف) ؟