اذهب إلى المحتوى

رشا سعد

يزداد الاعتماد على التطبيقات الذكية في جوانب حياتنا يومًا بعد يوم، ومع هذا الانتشار يزداد تعقيدها، وتتشعب وظائفها، فتصبح الحاجة ملحةً أكثر لابتكار تقنيات جديدة تحسِّن التفاعل بين خوادم التطبيقات والعملاء، ولعل أبرزها في السنوات الأخيرة GraphQL، وهي لغة استعلام مفتوحة المصدر لواجهات برمجة التطبيقات APIs وبيئة تشغيل لتنفيذ الاستعلامات، طورتها شركة فيسبوك Facebook في عام 2012 بهدف التغلب على نقاط الضعف في بنية REST التقليدية، وطرحتها للاستخدام العام في 2015، وأكثر ما يميز GraphQL جودة أدائها وكونها لغة تصريحية declarative وموجهة كليًا لتلبية طلبات العميل وما يحتاجه حقًا من معلومات.

فما هي مفاهيم GraphQL الأساسية؟ وما أوجه التشابه والاختلاف بينها وبين REST؟

ما هي GraphQL؟

GraphQL هي اختصار للعبارة Graph Query Language وتعني لغة استعلام بيانية، وهي مختلفة قليلًا عن لغات الاستعلام الأخرى مثل SQL وغيرها، فهي لا تتخاطب مع قاعدة بياناتك مباشرةً إنما تصف نموذج التواصل بين العميل وخادم واجهة برمجة التطبيقات API، ولديها مجموعة مواصفات قياسية بمثابة معيار موحد يحدد خصائصها وقواعد استخدامها، وبما أنك تتبع مواصفات GraphQL، فيمكنك استخدامها مع أي لغة برمجة، ومع أي قاعدة بيانات، ومع جميع أنواع العملاء إذا كانوا قادمين من تطبيق ويب أو تطبيق هاتف محمول، فهي كما ذكرنا مفتوحة المصدر لا تقتصر على أنواع معينة. يُعد Apollo GraphQL من أشهر تطبيقات خادم وعميل GraphQL التجارية وأكثرها انتشارًا بين المطورين، وستجد في هذا المقال على أكاديمية حسوب مثالًا عمليًّا عن طريقة بنائه.

خصائص GraphQL

سنعرض بعضًا من خصائص GraphQL الأساسية، مثل: استعلاماتها التصريحية declarative والهرمية hierarchical، وكونها ذات قواعد صارمة في التعامل مع أنواع البيانات strongly-typed، وأيضًا استقرائية introspective تسمح بالكشف عن مواصفات مخططاتها الداخلية Schema ليستفيد منها طالب الاستعلام.

تصريحية Declarative

تعني التصريحية أن العميل سيحدد أو يصرح عن الحقول التي يريد الاستعلام عنها فقط ويطلبها من الخادم، والخادم بدوره سيرجعها هي بالذات دون أي معلومات إضافية. ألقِ نظرةً على المثال التالي لإيضاح الأمر.

لنفترض أنك تطلب واجهة برمجية API للعبة ما وتستعلم عن حقول محددة لأحد شخصياتها، على سبيل المثال الاسم name والتصنيف race لشخصية المحارب warrior صاحب المعرف رقم "1"، فسيكون الطلب وفق الآتي:

{
  warrior(id: "1") {
    name
    race
  }
}

ستعيد الاستجابة المُعادة من تنسيق JSON كائنًا لنسميه data يتضمن الحقلين المطلوبين فقط من بيانات المحارب رقم "1":

{
  "data": {
    "warrior": {
      "name": "Merlin",
      "race": "HUMAN"
    }
  }
}

تتضمن هذه الاستجابة ما يطلبه العميل فقط دون زيادة أو نقصان وتمنح تطبيقك كفاءةً أعلى وأداءً أفضل على الشبكة موازنةً ببدائل GraphQL الأخرى مثل REST التي تُعيد للعميل كامل بيانات العنصر المُستَعلم عنه فتسبب ضغطًا على الشبكة.

هرمية Hierarchical

يمكنك طلب استعلامات هرمية من GraphQL أي الاستعلام عن أصل وفروعه، وستصلك البيانات المعادة من الخادم بنفس الهرمية التي طلبتها؛ فطلب الاستعلام يحدد شكل الاستجابة. لو عُدنا للمثال السابق واستبدلنا الاستعلام عن اسم المحارب وتصنيفه بالاستعلام عن اسمه وأسلحتهweapons وبالتحديد عن اسم كل سلاح name ودرجة قوته الهجومية attack:

{
  warrior(id: "1") {
    name
    weapons {
      name
      attack
    }
  }
}

ستتضمن الاستجابة الآن اسم المحارب ومصفوفة كائنات الأسلحة weapons مرتبةً كما طلبناها في الاستعلام warrior، وقد استطاعت GraphQL إحضارها بطلب استعلام واحد فقط، رغم أن بيانات الأسلحة ومقاتلي اللعبة تكون مخزنة غالبًا في جداول منفصلة ضمن قاعدة البيانات وهذه نقطة قوتها. نذكرك هنا أن GraphQL ليست معنية مطلقًا بطريقة تخزين البيانات في قاعدة البيانات إنما بتحديد نموذج الاستعلام فقط.

ألقِ نظرةً على الاستجابة الهرمية لاستعلامنا:

{
    "data": {
        "warrior": {
            "name": "Merlin",
            "weapons": [
                {
                    "name": "Sword",
                    "attack": 4
                },
                {
                    "name": "Bow",
                    "attack": 3
                },
                {
                    "name": "Axe",
                    "attack": 2
                }
            ]
        }
    }
}

صارمة في تحديد الأنواع Strongly-typed

توصف GraphQL بأنها صارمة في التعامل مع أنواع البيانات، ولديها نظام خاص لتحديد الأنواع يسمى نظام النوع، يصف إمكانات الخادم أي أنواع البيانات التي يقبلها، وتتدرج من البيانات المفردة Scalars وهي بيانات أولية، مثل: الأعداد الصحيحة والسلاسل النصية والقيم المنطقية، وصولًا إلى أنواع البيانات المعقدة مثل الكائنات التي تتكون من مجموعة حقول من البيانات الأولية.

يبين المثال التالي إنشاء نوع في مخطط GraphQL اسمه Weapon، وهو كائن يمتلك حقولًا أولية من نوع نص String وعدد صحيح Int:

type Weapon{
  name: String!
  attack: Int
  range: Int
}

إذًا، نظام النوع هو المسؤول عن صحة تعريف مخطط GraphQL، ويُقيّم الخادم بواسطته إذا كان طلب الاستعلام الذي كتبته مقبولًا أم لا قبل تنفيذه، ثم يُخضِعه للتحقق للتأكد من سلامته قواعديًا وخلوه من الأخطاء.

ذاتية التوثيق Self-documenting

يدعم خادم GraphQL خاصية الاستقراء Introspection، وهذا يعطي عملاءه والبرامج المتصلة معه القدرة على استقراء مخططاته الداخلية والاستعلام عن بنيتها، ويُسهل أيضًا تطوير أدوات مساعدة للتعامل معه نحو GraphiQL التي توفر بيئة تطوير متكاملة IDE وبيئة تجريبية Playground تعمل ضمن المتصفح، وغيرها من أدوات التوثيق الآلية.

هذا مثال بسيط عن استخدام الاستقراء لاستكشاف معلومات إضافية عن النوع Weapon باستعمال الكلمة المفتاحية schema__:

{
  __schema {
    types {
      name
      kind
      description
    }
  }
}

سيجيبك خادم GraphQL بصيغة JSON المعتادة وفق الآتي:

{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "Weapon",
          "kind": "OBJECT",
          "description": "A powerful weapon that a warrior can use to defeat enemies."
        }
      ]
    }
  }
}

موجهة بطلبات العميل Client-driven

يتركز جُلّ عمل المطور عند بناء GraphQL API في الواجهة الخلفية، فيُعرّف المخطط schema وينفّذه، ويهيئ نقطة الوصول الوحيدة endpoint مع الواجهة البرمجية التي تًميّز GraphQL عن غيره وتُعدّ نقطة قوته، أما من طرف العميل فيمكنه طلب البيانات التي يريدها بدقة عبر الاستعلامات التصريحية، ومهما تغيرت مواصفات الاستعلامات، يستطيع مطور الواجهة الأمامية مواكبتها وإنجاز تصاميم تكرارية سريعة لتطبيقه دون أي تعديل إضافي على الواجهة الخلفية.

بنية GraphQL

يعمل GraphQL في طبقة التطبيقات Application layer وسيطًا بين العميل والبيانات، إذ يصف خادم GraphQL إمكانات الاستعلام التي تتيحها الواجهة البرمجية API، ويحدد العميل متطلبات طلب الاستعلام حسب احتياجه.

الخادم Server

يعمل GraphQL في طبقة التطبيق، وهو غير مرتبط ببروتوكول نقل محدد، لكنه يستخدم غالبًا بروتوكول HTTP، ولديه نقطة اتصال وحيدة تسمى عادةً graphql/ وتتيح الوصول لكل موارد الخادم.

يمكنك برمجة خادم GraphQL بأي لغة برمجة، فعلى سبيل المثال تساعدك البرمجية الوسيطة express-graphql على إنشاء GraphQL API على خادم Express HTTP في بيئة Node؛ وفيما يخص قواعد البيانات فلا يقتصر خادم GraphQL على نوعٍ محدد منها، إذ يمكنه التعامل مع البيانات المخزنة في MySQL أو PostgreSQL أو MongoDB أو حتى القادمة من مصادر أخرى عبر نقاط اتصال لواجهات REST التقليدية، فالمهم في الأمر أن تُعرّف البيانات في مخطط GraphQL صحيح يبين الاستعلامات التي يستطيع العميل طلبها من الواجهة البرمجية API.

العميل Client

تدعى الطلبات المرسلة من عميل GraphQL إلى الخادم بالمستندات documents، وقد تكون طلبات قراءة فقط أي استعلامات، أو طلبات كتابة تسبب تعديلًا على البيانات وهذه تسمى طفرات mutations.

يمكنك توجيهها بطلب XMLHttpRequest بسيط، أو بعملية fetch لجلب البيانات من متصفح الويب، أو بالاعتماد على أدوات عميل متقدمة نحو عميل Apollo أو ريلي فيسبوك التي تقدم لك مميزات مختلفة مثل التخزين المؤقت.

إليك مثال لاستخدام الدالة fetch في جلب البيانات من نقطة الوصول graphql/، وقد مُرِّر مستند GraphQL بهيئة سلسلة نصية ضمن متن الطلب POST:

async function fetchwarriors() {
  const response = await fetch('/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query: `{
    warriors {
      id
      name
    },
  }`,
    }),
  })
  const warriors = await response.json()

  return warriors
}

fetchwarriors()

وهذه هي الاستجابة:

{
  "data": {
    "warriors": [
      { "id": "1", "name": "Merlin" },
      { "id": "2", "name": "Gandalf" }
    ]
  }
}

مقارنة بين GraphQL و REST

كلاهما يعملان للهدف نفسه، وهو تبادل البيانات بين تطبيقات مختلفة؛ إذ عرّفنا GraphQL في بداية المقال على أنها لغة استعلام بيانية وبيئة تشغيل لتنفيذ الاستعلامات؛ أما REST فهي اختصارٌ للعبارة Representational State Transfer وهي معمارية شهيرة لمشاركة البيانات عبر الويب، و RESTful API هي واجهة برمجة تطبيقات تتبع معايير REST، مثل: انعدام الحالة stateless، وقابلية التخزين في ذاكرة التخزين المخبئية Cache، واستقلال التقنيات المستخدمة في جانبي العميل والخادم عن بعضها، إضافةً إلى الواجهة المعيارية الموحدة التي تستخدم معرفات فريدة مثل عناوين URIs وغيرها.

تُعد GraphQL أحدث من REST وقد بُنيت في الأساس لمعالجة نقاط ضعفها بإنشاء واجهة API عالية الكفاءة وموجهة للعميل. لنبدأ الآن الموازنة بينهما لنعرف مزايا وعيوب كل تقنية:

  • البنية: تحتوي بنية REST نقاط وصول متعددة للتخاطب مع الخادم، أما GraphQL فتستخدم نقطة وصول وحيدة، وستعطيك مخططًا للبيانات التي طلبتها باستعلام واحد مهما كانت معقدة ومتشعبة، أما في REST فستحتاج عددًا من الاستعلامات للحصول على البيانات المتشعبة نفسها. إذًا، تتفوق GraphQL على REST في تخفيف الضغط على الشبكة.

  • جلب البيانات: عندما تستعلم عن تفصيل ما من واجهة REST تجيبك بأكثر مما طلبت، وترسل لك مجموعة البيانات المرتبطة بهذا التفصيل كاملةً كما هي معرفة على الخادم، حتى لو كنت تحتاج رقمًا واحدًا منها فقط، وفي حالاتٍ أخرى لا تكون استجابتها كافية، فعلى سبيل المثال قد لا تعطيك نقطة الوصول الخاصة بقائمة ما على الخادم جميع الخصائص التي تريد معرفتها عن القائمة فستحتاج نقاط أخرى معها أيضًا، بينما تخلصك GraphQL باستعلاماتها التصريحية من هذا الإفراط أو التقصير في جلب البيانات، وتحضرها لك بالهيكلية التي يحددها العميل دون زيادة أو نقصان.

  • التعامل مع الأخطاء: لا ينحصر استخدام GraphQL مع بروتوكول HTTP فقط، بل يمكنها الاعتماد على غيره أيضًا، لذا فهي لا تعتمد على رموز استجابة HTTP لعرض أخطاء طلباتها للعميل، فمعظم الطلبات العائدة من نقاط وصول GraphQL ستحمل الرمز 200 سواء كانت صحيحة أو خاطئة، وستجد ضمن استجابة الطلبات الخاطئة رسائل واضحة عن أخطائك جنبًا إلى جنب مع البيانات data وهذا بفضل خاصية الأخطاء errors، أما واجهة RESTful API فتعتمد كليًا على رموز استجابة HTTP، فيشير الرمز 200 دومًا للطلبات الصحيحة، وتعبّر رموز 400 عن الطلبات الخاطئة دون أي تفاصيل عن طبيعة الخطأ.

  • الإصدارات: تسعى GraphQL في جميع تعديلاتها لتجنب التغيرات الجذرية breaking changes، التي من المحتمل أن تسبب أخطاء في جانب العميل، ويحاول مطوروها الحفاظ على التوافقية مع الإصدارات السابقة، فهي توفر إمكانية زيادة ميزات جديدة على الواجهة بإضافة أنواع بيانات جديدة وحقول جديدة بدون الحاجة لإنشاء إصدار جديد، على عكس REST التي تتعامل مع أي تعديل على أنه إصدار جديد لنقاط الوصول، ويُشار للإصدارات صراحةً في عناوين URL، فتجد فيها رموزًا مثل: V1/ أو V2/، ولهذه الآلية أيضًا مشكلاتها. يمكنك عمومًا التعامل مع تغيُّر الإصدارات في التقنيتين وإن كانت طريقة REST أكثر تقليدية.

  • التخبئة Caching: تُعد التخبئة مبدأ من مبادئ REST، ولكونها تعتمد على توابع HTTP لجلب البيانات من نقاط وصولها المتعددة، فبوسعها الاستفادة من ميزة التخبئة -إحدى أساسيات HTTP- وعدم إعادة جلب الموارد نفسها من الخادم في كل مرة يحتاجها العميل. بالمقابل لا تستفيد GraphQL من ميزة التخبئة المُضمّنة في HTTP، لأنها تستخدم نقطة وصول وحيدة لجميع الطلبات، وكل طلب من طلباتها مخصص وفريد عن غيره، ومع ذلك يستطيع عملاء GraphQL استخدام التخبئة بصورة مبسطة باستعمال المُعرّف العمومي للكائن.

لنختتم الفقرة الآن بجدول يلخص أوجه الشبه والاختلاف بين GraphQL و REST، واضعًا في حسبانك إمكانية استخدامهما معًا في مشروع واحد، فتكون GraphQL مثل بوابة أو نقطة تجميع تتلقى البيانات الواردة من خدمات REST.

الميزة GraphQL REST
الوصف GraphQL هي لغة استعلام لواجهات برمجة التطبيقات، ووقت تشغيل من جانب الخادم لتنفيذ الاستعلامات REST هي نمط معماري لتصميم خدمات الويب
جلب البيانات عبر نقطة وصول وحيدة تستقبل طلبات الاستعلام المحددة بدقة من طرف العميل والمنقولة ببروتوكول HTTP عبر عدة نقاط وصول HTTP تعيد للعميل مجموعة بيانات محددة مسبقًا من قبل الخادم
الإصدارات غير شائعة شائعة
رموز استجابة HTTP كافة الاستجابات حتى الخاطئة منها تعود بالرمز 200 تطبق رموز استجابة HTTP بدلالتها المعروفة
التحقق التحقق من البيانات الوصفية مضمن في GraphQL المطور يجري عمليات التحقق بنفسه يدويًا
التوثيق التوثيق ذاتي بفضل وجود نظام النوع وميزة الاستقراء لا يوجد توثيق ذاتي لكن يمكنك الاستفادة من أدوات خاصة مثل OpenAPI
التخبئة غير متاح متاح
أساليب الطلب جميع الطلبات سواء كانت استعلامات أو طفرات أو اشتراكات تُرسل بأسلوب POST عبر بروتوكول HTTP تستخدم كل أساليب HTTP مثل GET و POST و PUT و PATCH و DELETE وغيرها
صيغة الاستجابة JSON بأي صيغة مثل JSON و XML و HTML وغير ذلك

الخاتمة

كان هذا المقال الأول من سلسلة مقالات تتناول إدارة البيانات باستخدام GraphQL، وقد تعرفنا فيه على GraphQL لغة الاستعلام مفتوحة المصدر الخاصة بواجهات برمجة التطبيقات، هذه اللغة التي طورتها فيسبوك موجهةً إياها كليًا لتلبية طلبات العميل، عبر كتابة استعلامات تصريحية يوضح فيها ما يحتاجه فعلًا، لتتجاوز بذلك مشكلات REST التقليدية، مثل الإفراط أو التقصير في جلب البيانات، إضافةً إلى انخفاض الكفاءة على الشبكة.

لا يعتبر المقال GraphQL بديًلا لواجهات REST، فلكلٍ منهما تطبيقات خاصة تتميز فيها عن الأخرى، وأيضًا طريقة خاصة في إدارة البيانات المتبادلة بين العميل والخادم، وقد عرضنا هنا طريقة GraphQL في عرض البيانات، تابع معنا بقية مقالات السلسلة لتطلع على المزيد.

ترجمة -وبتصرف- للمقال An Introduction to GraphQL لصاحبته Tania Rascia.

اقرأ المزيد


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...