GraphQL هي لغة استعلام مفتوحة المصدر وبيئة تشغيل على الخادم تُحدّد كيفية تفاعل العملاء مع واجهات برمجة التطبيقات (APIs).
توفر GraphQL بديلاً أكثر كفاءة ومرونة مقارنةً بنقل الحالة التمثيلي (REST) وواجهات برمجة التطبيقات RESTful، ويُعالج عددًا من القيود المرتبطة بواجهات REST فعلى سبيل المثال توفِّر GraphQL إمكانية استهداف الموارد بدقة باستخدام استعلام واحد فقط.
تستخدم GraphQL صيغة بديهية تمكّن العملاء من إرسال استعلام واحد إلى واجهة برمجة التطبيقات (API) واستلام البيانات المطلوبة فقط (بدلاً من التنقّل بين نقاط نهاية متعددة مع عدد كبير من المعلمات). هذا الأسلوب الأكثر كفاءة في جلب البيانات يمكن أن يُحسّن من أداء النظام وسهولة الاستخدام للمطوّرين،
وتجعل هذه الميزات GraphQL مفيدة بشكل خاص لبناء واجهات برمجة التطبيقات في البيئات المعقدة ذات متطلبات الواجهة الأمامية سريعة التغير. ولا تُعدّ واجهات REST أو GraphQL متفوّقة بطبيعتها إحداهما على الأخرى، بل هما أداتان مختلفتان مخصصتان لمهام مختلفة.
في أوائل عام 2010، كانت شركة Facebook تشهد نموًا وتحولًا كبيرين. ولكن تنامي قاعدة المستخدمين وتزايد تعقيد بيئة تطبيق الأجهزة المحمولة جعل نهج RESTful الحالي - الذي يتطلب الوصول إلى نقاط نهاية متعددة لجلب جميع بيانات الاستعلام الضرورية - غير قابل للاستمرار.
واجهت واجهات REST صعوبات في التعامل مع واجهات مستخدم معقدة تعتمد على البيانات، وكانت غالبًا تُعاني من مشكلات زمن الانتقال وعدم كفاءة في جلب البيانات، خاصةً لمستخدمي الهواتف المحمولة ذوي خطط البيانات المحدودة أو المكلفة.
استجابةً لهذه التحديات، طوّر مهندسو Facebook GraphQL (بالتزامن مع منصة React لتطبيقات الصفحة الواحدة)، وتم إصداره كحل مفتوح المصدر عام 2015. وفي عام 2018، نُقلت إدارة الخدمة إلى مؤسسة GraphQL Foundation، والتي تضم شركات أعضاء مثل: AWS وGatsby وIntuit وIBM.
تدور ميزات جلب البيانات التصريحي (Declarative) في بنية GraphQL حول عدد من المكوّنات والعمليات الأساسية، لكل منها دور فريد في معالجة البيانات.
وشتمل ما يلي:
تعتمد GraphQL على نظام قوي للأنواع، حيث يتم تسجيل كل أنواع البيانات داخل لغة تعريف مخططات GraphQL (SDL). تحدد المخططات محددة الأنواع (Typed schemas) أنواع البيانات التي يمكن الاستعلام عنها في واجهة برمجة التطبيقات، والعلاقات بينها، والعمليات المتاحة للمستخدم.
بعبارة أخرى، فإن هذه المخططات تُعرّف قدرات واجهة برمجة التطبيقات وشكل البيانات التي يمكن للعملاء التفاعل معها. يحدد تكوين هذا المخطط في النهاية كيفية استخدام واجهة برمجة التطبيقات. عندما تَرِد الاستعلامات، تُستخدَم المخطط (Schema) في التحقق من صحة الطلب، ولا تنفّذ واجهة GraphQL سوى الاستعلامات التي تم التحقق من صحتها.
كل حقل في المخطط يرتبط بمفسّر (Resolver)، وهو المسؤول عن جلب البيانات وتحديد الاستجابة لمجموعة الحقول. تقوم المُفسّرات، والتي يمكنها استرجاع البيانات من قاعدة بيانات، أو خدمة سحابية، أو أي مصدر آخر، بتوفير التعليمات اللازمة لتحويل عملية GraphQL (مثل استعلام، أو تحوّل، أو اشتراك) إلى بيانات.
عند بدء تنفيذ حقل استعلام، يقوم النظام بإنشاء استدعاء للمُفسّر المقابل لإنتاج القيمة التالية. إذا كانت نتيجة الحقل قيمة عددية بسيطة (مثل سلسلة نصية أو رقم)، يتم إكمال التنفيذ. أما إذا كانت القيمة كائنًا (Object)، فسيحتوي الاستعلام على مزيد من الحقول لذلك الكائن. وتستمر هذه العملية حتى لا يتبقّى سوى الحقول العددية البسيطة (Scalar Fields).
تُسهِم المُفسّرات أيضًا في تنسيق البيانات، وتساعد النظام على دمج المعلومات من مصادر متعددة.
استعلام البيانات هو الطلب الذي يُرسله العميل إلى خادم GraphQL، ويُحدّد فيه البيانات التي يرغب في جلبها. تُعرّف الاستعلامات داخل نوع الاستعلام (Query Type)، وهو كائن خاص في التعليمات البرمجية يُحدّد نقطة الدخول العليا لكل طلب يمكن للعميل تنفيذه على الخادم. كما يُحدّد نوع الاستعلام أيضًا اسم كل نقطة دخول ونوع البيانات التي تُرجعها.
عند ورود استعلام، تقوم GraphQL بالتحقق من صحته وفقًا لتعريفات المخطط، وإذا كان صحيحًا، تقوم بتنفيذه. ويُطابق هيكل الاستعلام عادةً هيكل البيانات المُسترجعة، مما يجعل متطلبات البيانات واضحة ويمكن التنبؤ بها.
التحوّلات هي عمليات GraphQL تُستخدم لإنشاء أو تحديث أو حذف البيانات على الخادم. وهي تعادل عمليات POST وPUT وPATCH وDELETE في واجهات RESTful. بينما يمكن وصول المستخدمين إلى بعض الاستعلامات دون مصادقة، فإن التحوّلات تتطلّب دائمًا مصادقة، على سبيل المثال، باستخدام رمز واجهة برمجة تطبيقات مميز.
على غرار ما يحدث مع الاستعلامات، يتم التحقق من صحة التحوّلات (Mutations) مقابل المخطط وتعريفاته. وبعد أن يتم التحقق منها وتبدأ المعالجة، يقوم الخادم بإرجاع استجابة بتنسيق JSON.
على الرغم من أن واجهات برمجة التطبيقات من نوع GraphQL ظهرت كبديل أكثر كفاءة ومرونة، إلا أن REST ظل لفترة طويلة المعيار لبنى واجهات برمجة التطبيقات. REST هو نمط معماري منظم لتطبيقات الوسائط التشعبية المتصلة بالشبكة، تم تصميمه لاستخدام بروتوكول اتصال عميل/خادم قابل للتخزين المؤقت وعديم الحالة، (غالبًا HTTP).
يتعلق الاختيار بين GraphQL و REST في الغالب بتحديد الأداة الأنسب للمهمة المطروحة. تتيح كل من GraphQL وREST للعملاء التواصل مع الخوادم وطلب البيانات، لكن هناك اختلافات جوهرية تفسر انتشار أنظمة GraphQL.
تُصمَّم واجهات برمجة التطبيقات REST حول الموارد (على سبيل المثال، أي نوع من الكائنات أو البيانات أو الخدمات التي يمكن للعميل الوصول إليها) وتعمل من خلال تخصيص نقطة نهاية (URL) مختلفة لكل مورد. وتعتمد واجهات REST على بنية بيانات ثابتة لتحديد شكل وحجم البيانات التي تقدمها للعملاء.
عندما يطلب العميل موردًا، يُرسل الخادم مجموعة بيانات كاملة تتضمّن جميع البيانات المرتبطة بذلك المورد. فإذا كان العميل بحاجة إلى جزء فقط من البيانات، فإنه يحصل على كل البيانات، وهو ما يُعرف بعملية جلب البيانات الزائد (Over-fetching). أما إذا كانت البيانات المطلوبة تتوزع على موارد متعددة، فعادةً ما يضطر العميل إلى إجراء عدة طلبات API بسبب نقص البيانات المسترجعة (Under-fetching) من الطلب الأولي.
في المقابل، تستخدم GraphQL نقطة نهاية واحدة تُقدّم وصفًا كاملاً ومفهومًا للبيانات.تستطيع استعلامات GraphQL الوصول إلى خصائص الموارد وتتبع العلاقات بينها، مما يمكّن العميل من الحصول على كل ما يحتاجه من بيانات عبر طلب واحد فقط، متجنّبةً بذلك مشاكل الجلب الزائد أو الناقص.
وفي بنية RESTful، فإن تغيير بنية البيانات غالبًا ما يتطلب من الفريق إصدار نسخة جديدة من واجهة برمجة التطبيقات لتجنّب حدوث أخطاء في النظام أو تعطل الخدمة للمستخدم.
وهذا يعني أنه يجب على المطوّرين إنشاء نقطة نهاية جديدة في كل مرة تتغير فيها بنية البيانات، مما يؤدي إلى وجود عدّة إصدارات من واجهة برمجة تطبيقات ويُعقّد عملية الصيانة.
أما GraphQL، فتُلغي الحاجة إلى إصدار واجهات متعددة، إذ يمكن للعملاء تحديد احتياجاتهم من البيانات ضمن نص الاستعلام نفسه. وإذا تمت إضافة حقول جديدة إلى الخادم، فلن يتأثر العملاء الذين لا يستخدمون هذه الحقول. وبالمقابل، إذا تم إيقاف بعض الحقول تدريجيًا (Deprecated)، فيمكن للعملاء الاستمرار في طلبها إلى حين تحديث استعلاماتهم.
تستخدم واجهات برمجة تطبيقات REST APIs رموز حالة HTTP للإشارة إلى نجاح أو فشل الطلب. حيث يكون كل رمز حالة معنى محدد. يُعيد الطلب الناجح رمز الحالة 200، في حين قد يُعيد الخطأ من جهة العميل رمز 400، وقد يُعيد خطأ الخادم رمز 500.
أما GraphQL، فتتعامل مع الأخطاء بشكل مختلف حيث تُعيد كافة الطلبات، سواء نجحت أم فشلت، رمز الحالة 200 OK. ولا يتم الإبلاغ عن الأخطاء باستخدام رموز HTTP، بل يتم تضمين تفاصيل الخطأ في نص الاستجابة إلى جانب البيانات.
هذا النهج يتطلّب من العميل تحليل جسم الاستجابة لتحديد ما إذا كان الطلب ناجحًا، مما قد يجعل تصحيح الأخطاء (Debugging) في واجهات GraphQL أكثر صعوبة بعض الشيء.
لا تدعم REST التحديثات الفورية بشكل ضمنيّ. إذا احتاج تطبيق ويب أو تطبيق على الهاتف المحمول إلى وظائف تعمل في الوقت الفعلي باستخدام واجهة REST، فعادةً ما يضطر المطوّرون إلى تنفيذ تقنيات مثل الاستطلاع المستمر (حيث يقوم العميل باستمرار بإرسال طلبات إلى الخادم للحصول على بيانات جديدة)، أو أحداث الخادم المُرسلة، أو WebSockets، وهي تقنيات قد تُضيف قدرًا كبيرًا من التعقيد إلى التطبيق.
أما GraphQL، فتحتوي على دعم مضمَّن للتحديثات في الوقت الفعلي من خلال ما يُعرف بالاشتراكات (Subscriptions). تحافظ الاشتراكات على اتصال مستمر بالخادم، مما يسمح له بإرسال التحديثات تلقائيًا إلى العميل عند حدوث أحداث محدّدة، ما يمكّن العملاء من متابعة البيانات ذات الصلة في واجهة برمجة التطبيقات ذات الصلة.
يُعدّ النظام البنائي لواجهة REST راسخًا، ويوفّر مجموعة واسعة من الأدوات والمكتبات والأُطر التعليمية المتاحة للمطوّرين. ومع ذلك، يتطلّب العمل مع واجهات برمجة تطبيقات REST عادةً إدارة نقاط نهاية متعددة وفهم أنماط العمل الخاصة بكل واجهات برمجة تطبيقات.
أما GraphQL، وعلى الرغم من أنها تقنية أحدث، فقد شهدت نموًا هائلًا منذ إطلاقها، وأصبح لديها نظام متكامل من الأدوات والمكتبات التي تدعم تطوير الخدمات لكل من الواجهة الأمامية والخلفية.
أدوات مثل GraphiQL وApollo Studio وGraphQL Playground توفر بيئات تطوير متكاملة (IDEs) قوية تُستخدم داخل المتصفح لاستكشاف واجهات GraphQL واختبارها. كما توفّر GraphQL دعمًا قويًا لتوليد التعليمات البرمجية، مما يُبسّط تطوير الواجهات من جانب العميل (Client-side development).
تعتمد واجهات برمجة تطبيقات REST على آليات التخزين المؤقت المضمّنة في HTTP مثل ETags ورؤوس Last-Modified. وعلى الرغم من فعاليتها، قد تكون استراتيجيات التخزين المؤقت معقدة وتفتقر إلى تحسين الأداء في بعض سيناريوهات الاستخدام.
أما واجهات برمجة تطبيقات GraphQL، فقد تكون أكثر صعوبة في التخزين المؤقت، وذلك بسبب الطبيعة الديناميكية لاستعلاماتها. لكن استخدام الاستعلامات المُعتمدة مسبقًا (Persisted Queries) والتخزين المؤقت للاستجابات والتخزين المؤقت على مستوى الخادم يمكن أن يُخفف من هذه التحديات ويُوفر استراتيجيات فعّالة للتخزين المؤقت في بنى GraphQL.
منذ انتقال GraphQL إلى مؤسسة GraphQL Foundation، أنشأ المطوّرون تطبيقات بلغات برمجة متعددة، مثل JavaScript وPython وRuby وPHP وغيرها. وقد تبنّت العديد من الشركات الكبرى واجهات GraphQL، مثل GitHub وPinterest وPayPal وShopify وAirbnb وغيرها، مما مكّن عددًا أكبر من العملاء من، تبسيط تحديد البيانات المطلوبة، وتقليل الإفراط أو القصور في نقل بيانات الشبكة، وتحسين قدرة الأنظمة على جلب البيانات بفعالية.1
بالإضافة إلى ذلك، تدفع المؤسسات والمطورون نحو اعتماد نهج اتحاد مفتوح الوصول (Open Federation) لبنية GraphQL. في صيغتها الحالية، يعمل الاتحاد في GraphQL على دمج خدمات GraphQL مستقلة في واجهة واحدة موحدة، تُشكّل نقطة الدخول الوحيدة إلى جميع بيانات الواجهة الخلفية، وتُتيح جلب البيانات من خلال طلب واحد. ومع ذلك، فإن تنفيذ الاتحاد حاليًا يقتصر على جهة واحدة مزوّدة فقط.
استجابةً لذلك، يدعو مؤيدو GraphQL إلى اعتماد اتحاد غير حصري، يُمكّن من تجميع البيانات من واجهات GraphQL وواجهات غير GraphQL على حد سواء، بدلاً من الاقتصار على واجهات GraphQL فقط.2
1 IBM acquires GraphQL startup StepZen to step up its game in API Management, TechCrunch, 8 February 2023
2 Why GraphQL Needs an Open Federation Approach, The New Stack, 16 November 2023