ما هو GraphQL؟
November 4, 2022
ما هو GraphQL؟ وما مدى شعبيته؟
GraphQL هي لغة استعلام موجهة للواجهات البرمجية (API) تم إصدارها من قبل فيسبوك في عام 2015. على عكس تصميمات API الأخرى، يسمح GraphQL للعملاء بتكوين عبارات استعلام بناءً على بنية بيانات متفق عليها مسبقًا، وتمكن الخادم من تحليل العبارة وإرجاع البيانات المطلوبة فقط. بهذه الطريقة، يوفر GraphQL ثراءً ومرونةً مع تجنب فقدان الأداء الناتج عن البيانات الزائدة، مما يجعله خيارًا رائعًا للتطبيقات التي تحتاج إلى التعامل مع العديد من كائنات البيانات المعقدة.
في عام 2018، أصدر GraphQL مواصفات كاملة وإصدارًا مستقرًا. في نفس العام، تبرعت فيسبوك بمشروع GraphQL إلى مؤسسة GraphQL تحت مظلة مؤسسة لينكس. منذ ذلك الحين، تم اعتماد GraphQL في العديد من المشاريع مفتوحة المصدر والمنظمات التجارية. حتى الآن، هناك عدة تطبيقات رئيسية لـ GraphQL على جانب العميل في السوق. كما تتوفر تطبيقات على جانب الخادم في جميع لغات البرمجة الرئيسية على جانب الخادم، وحتى في اللغات المتخصصة مثل D و R.
بعض السيناريوهات الحقيقية والتحديات لـ GraphQL
أشهر مثال على GraphQL هو واجهة برمجة التطبيقات (API) الخاصة بـ GitHub باستخدام GraphQL.
قبل اعتماد GraphQL، قدمت GitHub واجهة برمجة تطبيقات REST لعرض البيانات الغنية التي تم إنشاؤها بواسطة ملايين المشاريع المستضافة، والتي كانت ناجحة لدرجة أنها أصبحت نموذجًا يحتذى به عند تصميم واجهات برمجة تطبيقات REST. ومع ذلك، مع زيادة عدد كائنات البيانات وحجم الحقول داخل الكائنات، بدأت واجهة برمجة تطبيقات REST تظهر المزيد من العيوب. على جانب الخادم، كان على GitHub وضع حدود صارمة على تكرار الاستدعاءات لتقليل التكاليف بسبب كمية البيانات التي يتم إنشاؤها مع كل استدعاء. على جانب المطورين، كان عليهم التعامل مع هذا القيد حيث أن الاستدعاء الواحد يعيد الكثير من البيانات، لكن معظمها غير مفيد. للحصول على معلومات معينة، غالبًا ما يحتاج المطورون إلى إطلاق استعلامات متعددة ثم كتابة الكثير من التعليمات البرمجية لدمج البيانات ذات المعنى من نتائج الاستعلامات في المحتوى المطلوب. في هذه العملية، عليهم أيضًا أن يضعوا قيودًا على "عدد الاستدعاءات".
لذلك، اعتمدت GitHub GraphQL بمجرد إصداره. أصبحت GitHub "سفيرة" لـ GraphQL، حيث قدمت تطبيقه لآلاف المطورين. أصبحت واجهة برمجة تطبيقات GraphQL الآن الخيار الأول لـ GitHub. منذ الإعلان الأول لدعم GraphQL، نشرت GitHub عدة مقالات حول GraphQL كل عام. لتمكين المطورين من الانتقال إلى GraphQL، كتبت GitHub تطبيق استعلام تفاعلي خصيصًا لهذا الغرض: https://docs.github.com/en/graphql/overview/explorer. يمكن للمطورين تعلم كيفية كتابة GraphQL من خلال هذا التطبيق.
ومع ذلك، فإن GraphQL ليس حلًا سحريًا. مؤخرًا، أعلنت GitHub عن إيقاف دعم تطبيقها الخاص لواجهة برمجة تطبيقات GraphQL للـ package API. كما بدأ الكثيرون مناقشة بعض عيوب GraphQL. العديد من مشاكل GraphQL تنبع من حقيقة أن هيكلها يختلف كثيرًا عن معيار HTTP، مما يجعل من الصعب تعيين بعض مفاهيم GraphQL في هيكل مثل مسار HTTP/رأس. معاملة GraphQL كواجهة برمجة تطبيقات HTTP عادية تتطلب عملًا تطويريًا إضافيًا. نتيجة لذلك، سيضطر المطورون الذين يرغبون في إدارة واجهات برمجة تطبيقات GraphQL الخاصة بهم إلى استخدام بوابة API تدعم GraphQL.
كيف يدعم APISIX GraphQL
حاليًا، يدعم APISIX التوجيه الديناميكي من خلال بعض خصائص GraphQL. بهذه القدرة، يمكننا قبول طلبات GraphQL محددة فقط أو توجيه طلبات GraphQL مختلفة إلى خوادم upstream مختلفة.
لنأخذ عبارة GraphQL التالية كمثال:
query getRepo {
owner {
name
}
repo {
created
}
}
يستخرج APISIX الخصائص التالية لـ GraphQL للتوجيه:
- graphql_operation
- graphql_name
- graphql_root_fields
في عبارة GraphQL أعلاه:
graphql_operation
يتوافق معquery
graphql_name
يتوافق معgetRepo
graphql_root_fields
يتوافق مع["owner", "repo"]
لنقم بإنشاء مسار لإظهار قدرات APISIX على التوجيه الدقيق لـ GraphQL:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"methods": ["POST"],
"uri": "/graphql",
"vars": [
["graphql_operation", "==", "query"],
["graphql_name", "==", "getRepo"],
["graphql_root_fields", "has", "owner"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:2022": 1
}
}
}'
بعد ذلك، استخدم طلبًا يحتوي على عبارة GraphQL للوصول:
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
owner {
name
}
repo {
created
}
}'
HTTP/1.1 200 OK
...
يمكننا أن نرى أن الطلب وصل إلى خادم upstream حيث تطابقت عبارة الاستعلام مع جميع الشروط الثلاثة.
على العكس، إذا قمنا بالوصول بعبارة غير متطابقة، على سبيل المثال، إذا لم يتم تضمين حقل owner:
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
repo {
created
}
}'
HTTP/1.1 404 Not Found
...
لن تتطابق مع قاعدة التوجيه المقابلة.
يمكننا إنشاء مسار إضافي يسمح بتوجيه العبارات التي لا تحتوي على حقل owner إلى خادم upstream آخر:
curl http://127.0.0.1:9180/apisix/admin/routes/2 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"methods": ["POST"],
"uri": "/graphql",
"vars": [
["graphql_operation", "==", "query"],
["graphql_name", "==", "getRepo"],
["graphql_root_fields", "!", "has", "owner"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"192.168.0.1:2022": 1
}
}
}'
curl -i -H 'content-type: application/graphql' \
-X POST http://127.0.0.1:9080/graphql -d '
query getRepo {
repo {
created
}
}'
HTTP/1.1 200 OK
...
آفاق دعم APISIX المستقبلي لـ GraphQL
بالإضافة إلى التوجيه الديناميكي، قد يقدم APISIX في المستقبل المزيد من العمليات بناءً على حقول محددة في GraphQL. على سبيل المثال، تحتوي واجهة برمجة تطبيقات GraphQL الخاصة بـ GitHub على صيغة محددة للحد من المعدل، ويمكننا تطبيق قواعد مماثلة لتحويل طلب GraphQL واحد إلى عدد مقابلة من "الاستدعاءات الافتراضية" لتحقيق الحد من المعدل الخاص بـ GraphQL.
يمكننا أيضًا التفكير في المشكلة بطريقة مختلفة. يوفر التطبيق نفسه واجهة برمجة تطبيقات REST، وتقوم البوابة بتحويل طلبات GraphQL إلى طلبات REST وتحويل استجابات REST إلى استجابات GraphQL في الطبقة الخارجية. يمكن لواجهة برمجة تطبيقات GraphQL المقدمة بهذه الطريقة أداء وظائف مثل RBAC، الحد من المعدل، التخزين المؤقت، وما إلى ذلك، دون تطوير إضافات خاصة. من الناحية الفنية، هذه الفكرة ليست صعبة التنفيذ. بعد كل شيء، في عام 2022، تميل واجهات برمجة تطبيقات REST إلى توفير مواصفات OpenAPI كـ schema، وهو مجرد تحويل بين schema GraphQL و schema OpenAPI، بالإضافة إلى تصفية الحقول الخاصة بـ GraphQL. (بالطبع، يجب أن أعترف أنني لم أجربها بنفسي. ربما هناك تحديات في بعض التفاصيل التي لم يتم التغلب عليها بعد.)
سيجد القراء اليقظون أن واجهات برمجة تطبيقات GraphQL المحولة بهذه الطريقة يمكنها العمل على نموذج واحد فقط في كل مرة، مما لا يلبي متطلبات المرونة لـ GraphQL وهو مجرد واجهة برمجة تطبيقات REST في ثوب GraphQL. ومع ذلك، لدى GraphQL مفهوم يسمى schema stitching الذي يسمح للمنفذين بدمج عدة schemas معًا.
كمثال، لدينا واجهتي برمجة تطبيقات، واحدة تسمى GetEvent والأخرى تسمى GetLocation، والتي تعيد النوعين Event و Location على التوالي.
type Event {
id: string
location_id: string
}
type Location {
id: string
city: string
}
type Query {
GetEvent(id: string): Event
GetLocation(id: string): Location
}
يمكننا إضافة تكوين يجمع بين هاتين الواجهتين في واجهة برمجة تطبيقات جديدة تسمى GetEventWithLocation، والتي تبدو كالتالي:
type EventWithLocation {
id: string
location: Location
}
type Query {
GetEventWithLocation(id: string): EventWithLocation
}
يتم تنفيذ عملية الـ schema stitching بواسطة البوابة. في المثال أعلاه، تقوم البوابة بتقسيم الواجهة إلى قسمين، حيث تستدعي GetEvent للحصول على location_id ثم GetLocation للحصول على البيانات المدمجة.
باختصار، من خلال تحويل REST إلى GraphQL، يمكن تحويل كل واجهة برمجة تطبيقات REST إلى نموذج GraphQL مقابلة؛ وبمساعدة schema stitching، يمكن دمج عدة نماذج في واجهة برمجة تطبيقات GraphQL واحدة. بهذه الطريقة، يمكننا بناء واجهة برمجة تطبيقات GraphQL غنية ومرنة على أساس واجهة برمجة تطبيقات REST الحالية وإدارة الإضافات المحددة على مستوى واجهة برمجة تطبيقات REST. هذا التصميم يحل أيضًا بعض مشاكل تنسيق واجهات برمجة التطبيقات. كما في المثال أعلاه، نأخذ ناتج واجهة برمجة تطبيقات واحدة (Event.location_id) كمدخل لواجهة برمجة تطبيقات أخرى (Location.id).