الشبكات والاتصال

واجهات REST البرمجية

18 دقيقة الدرس 5 من 10

واجهات REST البرمجية

REST (نقل الحالة التمثيلية) هو النمط المعماري السائد لبناء واجهات برمجية قائمة على HTTP. وصفه روي فيلدينج لأول مرة في أطروحته للدكتوراه عام 2000، وهو ليس بروتوكولاً ولا معياراً — بل مجموعة من القيود التي، حين تُطبَّق، تنتج أنظمة قابلة للتوسع وسهلة الفهم والتطوير. كل واجهة برمجية عامة جربتها — Twitter وStripe وGitHub وTwilio — هي واجهة REST.

القيود الست لـ REST

حدّد فيلدينج ستة قيود. الأهم منها في التصميم اليومي اثنان:

  1. الواجهة الموحدة — تُعرَّف الموارد بمعرّفات URI ثابتة؛ وتجري التفاعلات عبر تمثيلات (JSON أو XML) وأساليب HTTP قياسية.
  2. عدم الحفاظ على الحالة (Statelessness) — كل طلب يحتوي على جميع المعلومات التي يحتاجها الخادم. لا يحتفظ الخادم بأي حالة جلسة بين الطلبات.
  3. فصل العميل عن الخادم — تنفصل متطلبات واجهة المستخدم عن متطلبات تخزين البيانات.
  4. قابلية التخزين المؤقت — تُعلن الاستجابات عن إمكانية تخزينها مؤقتاً، مما يتيح للوكلاء والعملاء التخزين بكفاءة.
  5. النظام ذو الطبقات — لا يستطيع العميل معرفة ما إذا كان يتحدث مباشرة مع الخادم أم عبر وسيط (موازن تحميل، CDN، بوابة).
  6. الكود عند الطلب (اختياري) — يجوز للخوادم إرسال كود قابل للتنفيذ (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 (الأفعال) عن نية العملية. استخدامها بشكل صحيح يتيح للذواكر المؤقتة والوكلاء والعملاء التصرف بشكل متوقع دون قراءة الوثائق.

HTTP verbs mapped to CRUD operations on a REST resource Method URI Example Meaning Properties GET GET /orders/42 Read a resource Safe · Idempotent Cacheable POST POST /orders Create new resource Neither safe nor idempotent PUT PUT /orders/42 Full replacement Idempotent PATCH PATCH /orders/42 Partial update Idempotent (if designed so) DELETE DELETE /orders/42 Remove resource Idempotent Safe = no side effects on the server. Idempotent = calling it N times has the same effect as calling it once.
أساليب HTTP وعقودها الدلالية — أساس تصميم واجهات REST.

الأداء الأحادي وأهميته

العملية أحادية الأداء (Idempotent) هي التي يُعطي تنفيذها عدة مرات نفس النتيجة التي يُعطيها تنفيذها مرة واحدة. هذا بالغ الأهمية في الأنظمة الموزعة لأن الشبكات غير موثوقة — يُعيد العملاء المحاولة عند فشل الطلبات.

  • DELETE /orders/42 يُستدعى مرتين: الاستدعاء الأول يحذف الطلبية، والثاني يُعطي 404. الطلبية لا تزال محذوفة — أحادي الأداء. ✓
  • POST /orders يُستدعى مرتين بسبب انتهاء مهلة الشبكة: قد يُنشئ طلبيتين — ليس أحادي الأداء. استخدم رأس مفتاح الأداء الأحادي (Idempotency-Key: <uuid>) لتتمكن من تصفية التكرارات.
  • PUT /orders/42 بجسم كامل: يستبدل المورد إلى نفس الحالة في كل مرة — أحادي الأداء. ✓
نصيحة تصميمية: تطلب واجهة Stripe للمدفوعات من العملاء إرسال رأس 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 بلا حالة يتدفق عبر نظام إنتاجي. لاحظ أن موازن التحميل يمكنه إرسال أي طلب إلى أي خادم تطبيق — لا توجد جلسة ثابتة لأن كل الحالة موجودة في قاعدة البيانات والرمز المميز.

Stateless REST request flow through a horizontally-scaled system Client Bearer JWT HTTPS Load Balancer any App Server instance 1 App Server instance 2 App Server instance 3 Cache Redis Database PostgreSQL miss No session stored on any App Server — the JWT carries identity; any instance handles any request
تدفق REST بلا حالة: رمز JWT في كل طلب يعني أن أي خادم يمكنه معالجته، مما يُتيح التوسع الأفقي بسلاسة.

تصميم واجهات 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 إذا كانت الجلب الزائد تكلفة حقيقية.
مشكلة شائعة — عناوين URL بأسلوب RPC: الواجهات التي تستخدم POST /createOrder أو GET /getUser?id=7 أو POST /deleteProduct ليست REST — بل هي RPC عبر HTTP. تُفقد مزايا التخزين المؤقت، وتُربك الوكلاء العكسيين الذين يُحسّن على أساس الفعل، وتُضلل المستهلكين. إذا وجدت نفسك تضع أفعالاً في عناوين URL، توقف وصمّم المورد بدلاً من ذلك.

REST مقابل "شبه REST"

عملياً، قليل جداً من الواجهات البرمجية العامة تستوفي القيود الست لفيلدينج — وخاصةً HATEOAS. ما يسميه معظم المطورين "واجهة REST" هو بدقة أكبر واجهة HTTP موجّهة نحو الموارد. هذا مقبول. القيود التي تهم أكثر عند التوسع هي عدم الحفاظ على الحالة (للتوسع الأفقي)، والواجهة الموحدة (لقابلية التخزين المؤقت والتوقع)، والاستخدام الصحيح لأفعال HTTP ورموز الحالة. ابدأ من هنا.

سيتناول الدرسان القادمان RPC/gRPC (حيث تصبح ثرثرة REST عائقاً في الاستدعاءات الداخلية بين الخدمات المصغرة) وWebSockets (حيث يُخفق نموذج طلب-استجابة في REST مع البيانات الفورية). معرفة REST بعمق تُضيء تلك المقايضات.