واجهات REST البرمجية
واجهات REST البرمجية
REST (نقل الحالة التمثيلية) هو النمط المعماري السائد لبناء واجهات برمجية قائمة على HTTP. وصفه روي فيلدينج لأول مرة في أطروحته للدكتوراه عام 2000، وهو ليس بروتوكولاً ولا معياراً — بل مجموعة من القيود التي، حين تُطبَّق، تنتج أنظمة قابلة للتوسع وسهلة الفهم والتطوير. كل واجهة برمجية عامة جربتها — Twitter وStripe وGitHub وTwilio — هي واجهة REST.
القيود الست لـ REST
حدّد فيلدينج ستة قيود. الأهم منها في التصميم اليومي اثنان:
- الواجهة الموحدة — تُعرَّف الموارد بمعرّفات URI ثابتة؛ وتجري التفاعلات عبر تمثيلات (JSON أو XML) وأساليب HTTP قياسية.
- عدم الحفاظ على الحالة (Statelessness) — كل طلب يحتوي على جميع المعلومات التي يحتاجها الخادم. لا يحتفظ الخادم بأي حالة جلسة بين الطلبات.
- فصل العميل عن الخادم — تنفصل متطلبات واجهة المستخدم عن متطلبات تخزين البيانات.
- قابلية التخزين المؤقت — تُعلن الاستجابات عن إمكانية تخزينها مؤقتاً، مما يتيح للوكلاء والعملاء التخزين بكفاءة.
- النظام ذو الطبقات — لا يستطيع العميل معرفة ما إذا كان يتحدث مباشرة مع الخادم أم عبر وسيط (موازن تحميل، CDN، بوابة).
- الكود عند الطلب (اختياري) — يجوز للخوادم إرسال كود قابل للتنفيذ (JavaScript) إلى العملاء.
الموارد ومعرّفات URI
في REST، المورد هو أي مفهوم يمكن تسميته وتحديد عنوانه. المستخدم مورد. الطلبية مورد. ألبوم الصور مورد. الموارد أسماء، وليست أفعالاً.
معرّف URI يُسمّي المورد. تصميم URI هو أول مكان تفشل فيه واجهات REST. اتبع هذه القواعد:
- استخدم الأسماء لا الأفعال.
/usersوليس/getUsers./orders/42/cancelمشكوك فيها — الأفضل هيPOST /orders/42/cancellations(بمعالجة الإلغاء نفسه كمورد). - استخدم الأسماء الجمع للمجموعات.
/articles،/products،/invoices. - استخدم التداخل للتعبير عن الملكية، لكن لا تتجاوز مستويين.
/users/7/postsجيدة./users/7/posts/3/comments/1/likesكابوس صيانة — سوّها إلى/likes/99. - استخدم kebab-case للكلمات المتعددة.
/order-itemsلا/orderItemsولا/order_items. - احتفظ بمعاملات الاستعلام للتصفية والترتيب وترقيم الصفحات.
GET /products?category=electronics&sort=price&page=2.
أفعال HTTP
تُعبّر أساليب HTTP (الأفعال) عن نية العملية. استخدامها بشكل صحيح يتيح للذواكر المؤقتة والوكلاء والعملاء التصرف بشكل متوقع دون قراءة الوثائق.
الأداء الأحادي وأهميته
العملية أحادية الأداء (Idempotent) هي التي يُعطي تنفيذها عدة مرات نفس النتيجة التي يُعطيها تنفيذها مرة واحدة. هذا بالغ الأهمية في الأنظمة الموزعة لأن الشبكات غير موثوقة — يُعيد العملاء المحاولة عند فشل الطلبات.
DELETE /orders/42يُستدعى مرتين: الاستدعاء الأول يحذف الطلبية، والثاني يُعطي404. الطلبية لا تزال محذوفة — أحادي الأداء. ✓POST /ordersيُستدعى مرتين بسبب انتهاء مهلة الشبكة: قد يُنشئ طلبيتين — ليس أحادي الأداء. استخدم رأس مفتاح الأداء الأحادي (Idempotency-Key: <uuid>) لتتمكن من تصفية التكرارات.PUT /orders/42بجسم كامل: يستبدل المورد إلى نفس الحالة في كل مرة — أحادي الأداء. ✓
Idempotency-Key في طلبات POST. يخزّن الخادم المفتاح، وإذا رأى نسخة مكررة خلال 24 ساعة، يُعيد الاستجابة الأصلية دون احتساب رسوم على البطاقة مرة أخرى. تبنَّ هذا النمط لأي نقطة نهاية غير أحادية الأداء تتعامل مع المدفوعات أو البريد الإلكتروني أو أي آثار جانبية لا رجعة فيها.
رموز حالة HTTP
تُوصّل رموز الحالة نتيجة الطلب. استخدمها بدقة — لا تُعيد 200 OK مع رسالة خطأ في الجسم.
- 2xx نجاح —
200 OK،201 Created(أضف رأسLocationيشير إلى المورد الجديد)،204 No Content(للحذف بدون جسم استجابة). - 3xx إعادة توجيه —
301 Moved Permanently،304 Not Modified(يُفعّل التخزين المؤقت من جانب العميل عبرETag/If-None-Match). - 4xx خطأ العميل —
400 Bad Request،401 Unauthorized(غير مُصادَق)،403 Forbidden(مُصادَق لكن غير مُصرَّح له)،404 Not Found،409 Conflict،422 Unprocessable Entity(أخطاء التحقق)،429 Too Many Requests(تحديد معدل الطلبات). - 5xx خطأ الخادم —
500 Internal Server Error،503 Service Unavailable(استخدمه لاستجابات قاطع الدائرة).
عدم الحفاظ على الحالة عند التوسع — تدفق حقيقي
يُظهر المخطط التالي طلب REST بلا حالة يتدفق عبر نظام إنتاجي. لاحظ أن موازن التحميل يمكنه إرسال أي طلب إلى أي خادم تطبيق — لا توجد جلسة ثابتة لأن كل الحالة موجودة في قاعدة البيانات والرمز المميز.
تصميم واجهات REST نظيفة — إرشادات عملية
بما يتجاوز الأساسيات، تُميّز عدة ممارسات الواجهات التي يُحبها المطورون عن تلك التي يخشونها:
- ضع إصدارات من اليوم الأول. استخدم بادئة URL (
/v1/،/v2/) أو رأسAccept. تغيير شكل الاستجابة بدون إصدارات يُعطّل كل عميل موجود. - أعِد أشكال أخطاء متسقة. يجب أن تحتوي كل استجابة خطأ على نفس بنية JSON:
error.codeوerror.messageوبشكل اختياريerror.details. Stripe وGitHub يفعلان ذلك بشكل مثالي. - قسّم المجموعات إلى صفحات. لا تُعيد أبداً قوائم غير محدودة. استخدم ترقيم الصفحات بالمؤشر لتدفقات البيانات الكبيرة (
?after=cursor) والإزاحة/الحد للجداول البسيطة (?page=3&per_page=25). - استخدم HATEOAS بانتقائية. الوسائط التشعبية كمحرك حالة التطبيق تعني تضمين روابط في الاستجابات. مفيد للاكتشاف؛ تجاهله إذا كانت العملاء تطبيقات موبايل مرتبطة ارتباطاً وثيقاً تتحكم فيها بالكامل.
- اعرض فقط ما يحتاجه العملاء. تجنب إعادة كائنات بـ40 حقلاً حين يستخدم تطبيق الموبايل 5 منها فقط. فكّر في حقول متفرقة (
?fields=id,name,price) أو GraphQL إذا كانت الجلب الزائد تكلفة حقيقية.
POST /createOrder أو GET /getUser?id=7 أو POST /deleteProduct ليست REST — بل هي RPC عبر HTTP. تُفقد مزايا التخزين المؤقت، وتُربك الوكلاء العكسيين الذين يُحسّن على أساس الفعل، وتُضلل المستهلكين. إذا وجدت نفسك تضع أفعالاً في عناوين URL، توقف وصمّم المورد بدلاً من ذلك.
REST مقابل "شبه REST"
عملياً، قليل جداً من الواجهات البرمجية العامة تستوفي القيود الست لفيلدينج — وخاصةً HATEOAS. ما يسميه معظم المطورين "واجهة REST" هو بدقة أكبر واجهة HTTP موجّهة نحو الموارد. هذا مقبول. القيود التي تهم أكثر عند التوسع هي عدم الحفاظ على الحالة (للتوسع الأفقي)، والواجهة الموحدة (لقابلية التخزين المؤقت والتوقع)، والاستخدام الصحيح لأفعال HTTP ورموز الحالة. ابدأ من هنا.
سيتناول الدرسان القادمان RPC/gRPC (حيث تصبح ثرثرة REST عائقاً في الاستدعاءات الداخلية بين الخدمات المصغرة) وWebSockets (حيث يُخفق نموذج طلب-استجابة في REST مع البيانات الفورية). معرفة REST بعمق تُضيء تلك المقايضات.