إصدار واجهة برمجة التطبيقات وأفضل الممارسات
إصدار واجهة برمجة التطبيقات وأفضل الممارسات
لقد بنيتَ بالفعل واجهة برمجة تطبيقات REST تعمل بكفاءة. الآن تأتي الحرفية الاحترافية: كيف تطوّر تلك الواجهة بمرور الوقت دون أن تُخلّ بعمل العملاء الحاليين؟ وكيف تُسمّيها وتهيكلها لتبقى قابلة للصيانة مع نموّها؟ يتناول هذا الدرس الاستراتيجيات الثلاث السائدة للإصدار، واتفاقيات التسمية التي تجعل الواجهة توثّق نفسها بنفسها، ونظرة موجزة على HATEOAS — القيد الذي يرفع REST إلى أنقى صوره.
لماذا الإصدار مهم
بمجرد أن يعتمد العملاء على واجهتك البرمجية، فإن أي تغيير قد يكسر التوافق — حذف حقل، إعادة تسميته، تغيير معنى رمز حالة — قد يُعطّلهم بصمت أو بضجيج. يمنحك الإصدار وسيلةً لإدخال تلك التغييرات على نسخة جديدة مع الإبقاء على النسخة القديمة حية حتى يكتمل انتقال العملاء. الإصدار عقد: كل ما في نطاق الإصدار الواحد يسلك سلوكًا ثابتًا.
الاستراتيجية الأولى — الإصدار في مسار URI
يُدرج رقم الإصدار مباشرةً في مسار العنوان: /api/v1/products، /api/v2/products. هذه أكثر الاستراتيجيات وضوحًا وانتشارًا في الواجهات العامة (تستخدمها Twitter وGitHub وStripe جميعها).
في Spring Boot لا تحتاج إلا إلى بادئة على @RequestMapping:
يتشارك المتحكّمان طبقة الخدمة ذاتها؛ الفرق الوحيد هو في أشكال DTO وبادئة الرابط. ضع منطق الخدمة في مكان واحد — التكرار مسموح فقط في طبقة المتحكّم/DTO.
com.example.api.v1.controller، com.example.api.v2.controller. هذا يجعل الإصدارين قابلَين للتصفح جنبًا إلى جنب، ويُوضّح متى يصبح حذف إصدار آمنًا.
الاستراتيجية الثانية — الإصدار عبر ترويسة الطلب
يُمرَّر الإصدار في ترويسة HTTP مخصصة — شائعًا X-API-Version: 2 أو ترويسة Accept بنوع وسائط مخصص. يبقى الرابط نظيفًا (/api/products) ويمكن لنقطة وصول واحدة أن تتفرّع بحسب الترويسة، أو تستخدم خاصية headers في @GetMapping:
يحافظ الإصدار عبر الترويسة على نظافة الروابط، لكن له عيب حقيقي: الروابط لم تعد قابلة للإشارة المرجعية باستقلالية، ولا يمكن لشبكة CDN تخزينها مؤقتًا دون ضبط ترويسة Vary. كما أن الإصدار يصبح غير مرئي في شريط العنوان وفي السجلات ما لم تسجّل الترويسات صراحةً.
الاستراتيجية الثالثة — الإصدار عبر نوع الوسائط (ترويسة Accept)
هذا أكثر الأساليب انسجامًا مع REST: يُحدّد العميل التمثيل المطلوب عبر ترويسة Accept بنوع وسائط مخصص للمورّد، ويوجّه Spring إلى المعالج المناسب عبر produces:
Accept: */*، لذا يجب توثيق نوع الوسائط المحدد باستمرار، واختباره صراحةً، والتحقق من أن البوابة لا تحذف ترويسات Accept المخصصة. كثير من الفرق تعود إلى إصدار URI بعد أن يتصاعد احتكاك استيعاب العملاء.
اختيار الاستراتيجية — نظرة سريعة على المقايضات
- إصدار URI — عالي الوضوح، قابل للتخزين المؤقت بسهولة، سهل الاختبار في المتصفح. أقل "RESTية" قليلًا لأن الرابط يفترض أن يُعرّف موردًا لا إصدارًا. الاختيار الأمثل للواجهات العامة أو واسعة النطاق.
- إصدار الترويسة — روابط نظيفة، غير مرئي لشبكات CDN بدون ضبط
Vary، أصعب في الاختبار. جيد للواجهات الداخلية ذات العملاء المحكومين. - إصدار نوع الوسائط — أكثر توافقًا مع دلالات HTTP. احتكاك مرتفع لفرق المستهلكين؛ تجنّبه في الواجهات العامة.
لمعظم الفرق، إصدار URI هو الخيار العملي الافتراضي. GitHub وStripe وTwilio وأغلب الواجهات العامة الناضجة تستخدمه لهذا السبب تحديدًا.
اتفاقيات تسمية REST API
التسمية الجيدة تجعل الواجهة توثّق نفسها. هذه القواعد مُعتمَدة على نطاق واسع في الصناعة:
- استخدم الأسماء لا الأفعال —
/productsلا/getProducts. فعل HTTP هو الذي يوفّر الفعل. - صيغة الجمع لأسماء المجموعات —
/users،/orders. المجموعة مجموعة موارد. - أحرف صغيرة مفصولة بشرطة —
/product-categoriesلاproductCategoriesولاproduct_categories. الروابط حساسة لحالة الأحرف على بعض الخوادم؛ الأحرف الصغيرة تزيل الغموض. - التسلسل الهرمي يعكس العلاقات —
/users/{userId}/orders/{orderId}. ادمج الموارد الفرعية تحت أصلها، لكن لا تتجاوز مستويَين أو ثلاثة وإلا أصبحت الروابط مرهقة. - التصفية والترتيب والتصفح عبر معاملات الاستعلام —
/products?category=electronics&sort=price&page=2&size=20. أبقِ المسار نظيفًا؛ المسار هو هوية المورد، ومعاملات الاستعلام قيود اختيارية. - استخدم رموز حالة HTTP القياسية باتساق —
201 Createdمع ترويسةLocationبعد POST، و204 No Contentعند نجاح DELETE، و404 Not Foundعندما لا يوجد المورد (لا200مع جسم خطأ).
{ "status": 404, "error": "Not Found", "message": "Product 42 not found", "timestamp": "..." } — يتيح للعملاء كتابة معالج خطأ واحد لكامل واجهتك بدلًا من تخمين الشكل لكل نقطة وصول.
ملاحظة حول HATEOAS
يتضمن REST بحسب تعريف Roy Fielding قيدًا يُسمّى HATEOAS (Hypermedia As The Engine Of Application State). في واجهة HATEOAS، تتضمن كل استجابة روابط تُخبر العميل بما يمكنه فعله بعد ذلك — فلا يحتاج العميل إلى تضمين الروابط مسبقًا، بل يكتشفها في وقت التشغيل:
يوفّر Spring مكتبة Spring HATEOAS (المُبدِّل: spring-boot-starter-hateoas) مع EntityModel وCollectionModel وWebMvcLinkBuilder لبناء هياكل الروابط هذه دون إنشاء سلاسل نصية يدويًا.
يجعل HATEOAS الواجهة تصف نفسها بنفسها حقًا ويفصل العملاء عن هياكل الروابط المضمّنة. في الواقع العملي، تتجاهله معظم الفرق التي تبني واجهات خاصة أو موجّهة لشركاء محددين لأن التكلفة التشغيلية كبيرة وأغلب العملاء لا يتتبعون الروابط ديناميكيًا. قيمته الأكبر في الواجهات العامة ذات المستهلكين المتنوعين. معرفة وجوده — وإدراك نمط _links حين تصادفه — هو ما يهم هنا.
الخلاصة
إصدار مسار URI (/api/v1/) هو الاختيار العملي المعياري في الصناعة لمعظم الواجهات. استراتيجيتا الترويسة ونوع الوسائط تستبدلان وضوح الرابط بنقاء HTTP على حساب تعقيد العميل. اتفاقيات التسمية الصلبة — أسماء جمع، مسارات بأحرف صغيرة وشرطات، أفعال من HTTP، معاملات استعلام للتصفية — تحوّل الواجهة العاملة إلى واجهة مُصمَّمة جيدًا. HATEOAS هو المثل الأعلى لـ REST يستحق الفهم حتى لو لم تطبّقه من اليوم الأول. في الدرس الأخير من هذا البرنامج التعليمي ستجمع كل هذه المهارات معًا لبناء واجهة REST كاملة بجودة الإنتاج من الصفر.