GraphQL — هو لغة استعلام للواجهات البرمجية (API) وبيئة تنفيذ لتنفيذ تلك الاستعلامات، طورتها Facebook في 2012 وأصبحت مفتوحة المصدر في 2015. وخلافًا لـ REST، حيث يحدد الخادم هيكل الاستجابة، يسمح GraphQL للعميل بتحديد البيانات التي يحتاجها بدقة، مما يقضي بالكامل على مشكلتي overfetching و underfetching. وفقًا لـ State of JavaScript Survey (2025)، يستخدم 35% من المطورين المشاركين GraphQL، وبين الشركات الكبرى قامت GitHub، Shopify، Airbnb و The New York Times باعتماده. يدعم GraphQL ثلاثة أنواع من العمليات: query (قراءة)، mutation (كتابة) و subscription (تحديثات فورية عبر WebSocket).
النقاط الرئيسية
GraphQL — هو مواصفة وبيئة تنفيذ للواجهات البرمجية تمنح العميل السيطرة الكاملة على البيانات التي يتلقاها. طورها مهندسو Facebook لحل مشاكل تطبيق News Feed المحمول، ونشرت المواصفة كمعيار مفتوح في 2015. منذ 2018، يتم إدارة GraphQL بواسطة GraphQL Foundation بدعم من Linux Foundation وشركات مثل Apollo، AWS، GitHub، SAP وغيرها.
وخلافًا لـ REST، حيث يعيد كل endpoint هيكلاً ثابتًا للبيانات، يستخدم GraphQL endpoint واحد يقبل سلسلة استعلام. يصف العميل في الاستعلام الحقول التي يحتاجها، ويعيد الخادم تلك الحقول بالضبط. على سبيل المثال، الاستعلام { user(id: "1") { name email } } سيعيد فقط اسم المستخدم وبريده الإلكتروني، دون حقول إضافية مثل address، phone أو createdAt التي كان لابد من الحصول عليها في REST.
GraphQL غير مرتبط بأي قاعدة بيانات أو لغة محددة. تحدد المواصفة فقط شكل الاستعلامات والاستجابات. توجد تطبيقات خادم على Node.js (graphql-js، Apollo Server)، Kotlin (graphql-kotlin، Netflix DGS Framework)، Python (Graphene، Strawberry)، Ruby (graphql-ruby) ولغات أخرى. تتوفر مكتبات عميل لجميع المنصات الرئيسية، بما في ذلك Apollo Client لـ iOS، Android والويب.
تتكون معمارية GraphQL من ثلاثة مكونات رئيسية: المخطط (Schema)، الحاسمات (Resolvers) ومحرك GraphQL (GraphQL Engine). يحدد المخطط أنواع البيانات المتاحة، والاستعلامات التي يمكن تنفيذها، والوسائط التي تقبلها. الحاسمات هي دوال جانب الخادم تعيد بيانات لكل حقل في المخطط. يتلقى المحرك الاستعلام الوارد، يتحقق من صحته بالنسبة للمخطط، يستدعي الحاسمات المناسبة ويجمع الاستجابة.
عملية معالجة الاستعلام تبدو كالتالي:
الميزة الرئيسية لمعمارية GraphQL هي الدقّ على مستوى الحقل. في REST، يحصل المطور على جميع حقول المورد (وربما بيانات زائدة) أو يلجأ إلى إمتدادات مثل ?fields=name,email. في GraphQL، هذا التصفية مبني في اللغة: كل استعلام يحدد بوضوح الحقول المطلوبة، ويعيد الخادم تلك الحقول بالضبط. هذا مهم بشكل خاص لتطبيقات الهواتف المحمولة، حيث يؤثر حجم البيانات المنقولة مباشرة على سرعة التحميل واستهلاك البيانات.
GraphQL يحدد ثلاثة أنواع من العمليات، كل منها يتوافق مع سيناريو تفاعل محدد. Query — لقراءة البيانات، مماثل لـ GET في REST. Mutation — لتعديل البيانات (إنشاء، تحديث، حذف)، مماثل لـ POST/PUT/DELETE. Subscription — للتحديثات الفورية عبر WebSocket، وليس له نظير مباشر في REST التقليدي (يتطلب حلولاً إضافية مثل WebSocket أو Server-Sent Events).
تركيب الاستعلام الأساسي بديهي:
// استعلام بسيط مع وسيط
query {
user(id: "42") {
name
email
avatarUrl
}
}
// موتاسية تعيد بيانات معدلة
mutation {
updateProfile(name: "إيفان") {
id
name
updatedAt
}
}
// Subscription — يستمع للتحديثات الفورية
subscription {
newMessage(chatId: "chat_1") {
id
text
sender { name }
}
}
Query يتم تنفيذه بالتوازي — جميع الحقول في نفس المستوى تتحمل في نفس الوقت. هذا يسمح بتحميل بيانات مترابطة (المستخدم ومنشوراته) في طلب واحد دون عدة round-trips. Mutation يتم تنفيذه بالتسلسل — الموتاسيات في طلب واحد تتنفذ واحدة تلو الأخرى بترتيب الإعلان. Subscription ينشئ اتصالاً دائمًا عبر WebSocket، يرسل الخادم من خلاله بيانات عند وقوع حدث.
يمكن للعمليات أن تقبل متغيرات لفصل البيانات عن الاستعلام، توجيهات (@include، @skip) لإدراج الحقول بشرط، و قطعات لإعادة استخدام مجموعات الحقول. هذه الإمكانيات تجعل استعلامات GraphQL مرنة وقابلة لإعادة الاستخدام، وهو أمر مهم بشكل خاص في المشاريع الكبيرة ذات الشاشات والمكونات المتعددة.
في جوهر GraphQL يكمن نظام أنواع يصف جميع البيانات وعمليات API الممكنة. المخطط هو وصف للأنواع التي يمكن للخادم إعادتها والاستعلامات التي يقبلها. يكتب المخطط بلغة Schema Definition Language (SDL) ويعمل كعقد بين العميل والخادم. يمكن للعميل الحصول على المخطط من خلال الاستبصار (introspection) — استعلام خاص __schema يعيد وصفًا كاملًا للواجهة البرمجية.
مثال مخطط لمدونة:
// SDL — Schema Definition Language
type User {
id: ID!
name: String!
email: String
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
user(id: ID!): User
posts(page: Int): [Post!]!
}
علامة التعجب (!) تعني حقلًا غير قابل للفراغ، من المضمون وجوده في الاستجابة. الأقواس المربعة [ ] تدل على قائمة. يدعم GraphQL الأنواع القياسية (Int، Float، String، Boolean، ID)، الأنواع الكائنة، enum، union، interface وأنواع الإدخال (لوسائط الموتاسيات). توثق الكتابة الصارمة ذاتيًا للواجهة البرمجية وتسمح لأدوات العميل بتوليد الكود: أنواع TypeScript، فئات بيانات Kotlin، هياكل Swift.
الاستبصار (Introspection) هو ميزة فريدة لـ GraphQL غائبة في REST. يمكن للعميل إرسال استعلام إلى المخطط والحصول على وصف كامل لجميع الأنواع والحقول والوسائط والتوجيهات. هذا هو أساس أدوات مثل GraphiQL و Apollo Studio، التي تولد تلقائيًا التوثيق والإكمال التلقائي للمطورين. كما يسمح الاستبصار بكتابة اختبارات تلقائية تتحقق من مطابقة المخطط للهيكل المتوقع.
الاختيار بين GraphQL و REST هو أحد القرارات المعمارية الرئيسية عند تصميم API. كلا النهجين له نقاط قوة وضعف، ويعتمد الاختيار على متطلبات المشروع المحددة. يتفوق REST في البساطة والعمومية، ويتفوق GraphQL في المرونة وكفاءة الاستعلامات. دعنا ننظر إلى جدول المقارنة.
| المعيار | REST | GraphQL |
|---|---|---|
| هيكل الاستجابة | ثابت، يحدده الخادم | مرن، يحدده العميل |
| Overfetching | غالبًا — الخادم يعيد جميع الحقول | لا — العميل يطلب فقط الحقول المطلوبة |
| عدد الطلبات | عدة round-trips | طلب واحد لكل البيانات |
| التخزين المؤقت | تخزين مؤقت HTTP أصلي | يتطلب تكويناً يدويًا |
| الأنمطة | غير مبنية (تعتمد على التنسيق) | صارمة، عبر مخطط SDL |
| الأدوات | curl، Postman، Swagger | GraphiQL، Apollo Studio، الاستبصار |
| رفع الملفات | أصلي عبر multipart | يتطلب بروتوكولات إضافية |
| الأداء | قابل للتنبؤ، أسهل في التحسين | يعتمد على تعقيد الاستعلامات المتداخلة |
العيب الرئيسي في GraphQL هو تعقيد التخزين المؤقت. في REST، يعمل تخزين HTTP المؤقت على مستوى URL: طلب واحد إلى /api/users/42 يعيد نفس الهيكل دائمًا، ويمكن تخزين الاستجابة مؤقتًا باستخدام URL. في GraphQL، تذهب جميع الطلبات إلى endpoint واحد، ويعتمد هيكل الاستجابة على نص الطلب. لحل هذه المشكلة، يستخدم Apollo Client مخزنًا مؤقتًا موحدًا على جانب العميل، يقسم الاستجابات إلى كيانات فردية حسب المعرف (id) ويحدثها تلقائيًا عند استقبال بيانات جديدة.
جانب آخر مهم هو مشكلة N+1. عند طلب بيانات متداخلة (على سبيل المثال، منشورات المستخدم وتعليقات كل منشور)، قد ينفذ GraphQL استعلام SQL منفصل لكل عنصر في القائمة. يتم حلها باستخدام DataLoader — أداة لتجميع وتخزين استعلامات قاعدة البيانات مؤقتًا، تقوم بتجميع الطلبات الفردية في دفعة واحدة. في REST، هذه المشكلة أقل وضوحًا لأن المطور يتحكم في هيكل الاستجابة على جانب الخادم.
لننظر إلى أمثلة عملية لاستخدام GraphQL في تطبيق محمول باستخدام Kotlin و Apollo Client. توضح الأمثلة سيناريوهات نموذجية: تحميل بيانات شاشة الملف الشخصي (query)، إنشاء منشور جديد (mutation) والاشتراك في التعليقات الجديدة (subscription). كل مثال يشمل كلا من استعلام GraphQL والكود على جانب العميل.
استعلام GraphQL واحد يحمل المستخدم، آخر منشوراته والعدد الإجمالي للمتابعين. في REST، كان سيتطلب ذلك 2-3 طلبات على الأقل: /users/42، /users/42/posts، /users/42/stats. GraphQL يدمجها في round-trip واحد، مما يقلل وقت تحميل الشاشة على الاتصالات البطيئة.
// استعلام GraphQL (في ملف .graphql)
query ProfileScreen($userId: ID!) {
user(id: $userId) {
name
bio
avatarUrl
posts(limit: 10) {
id
title
createdAt
}
followersCount
followingCount
}
}
// استدعاء على العميل (Apollo Client + Kotlin)
val response = apolloClient
.query(ProfileScreenQuery(userId = "42"))
.execute()
binding.nameText.text = response.data?.user?.name
لا تقوم الموتاسية بإنشاء المورد فقط، بل تعيد أيضًا بياناته الحالية لتحديث واجهة المستخدم. يستخدم Apollo Client حقل __typename لتوحيد المخزن المؤقت — سيقوم العميل تلقائيًا بتحديث سجل Post في المخزن المؤقت عند استقبال استجابة ناجحة من الموتاسية.
// موتاسية GraphQL
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
createdAt
author {
id
name
}
}
}
// استدعاء موتاسية بنوع input
val input = CreatePostInput(
title = "منشور جديد عن GraphQL",
content = "GraphQL يبسط العمل مع API..."
)
val result = apolloClient
.mutation(CreatePostMutation(input))
.execute()
ميزة مهمة لـ GraphQL على REST في سياق تطوير التطبيقات المحمولة هي التوليد التلقائي للكود. Apollo Client لـ Kotlin (Apollo GraphQL) يولد فئات آمنة من حيث الأنمطة من ملفات .graphql خلال وقت التجميع. إذا غير الخادم المخطط، لن يتم تجميع المشروع حتى يتم تحديث الاستعلامات. هذا يمنع أخطاء وقت التنفيذ النموذجية في REST، حيث قد لا يلاحظ تغير هيكل الاستجابة أثناء التطوير.
يشمل النظام البيئي لـ GraphQL عدة مكتبات وأدوات رئيسية تبسط التطوير والتشغيل. Apollo Client هو أكثر مكتبة عميل شعبية، تدعم React، iOS، Android و Kotlin Multiplatform. Relay من Facebook هو بديل لتطبيقات React بنهج فريد لإدارة البيانات والتخزين المؤقت. يعتمد الاختيار بين Apollo و Relay على المنصة ومتطلبات الأداء.
على جانب الخادم، الروادد هم Apollo Server (Node.js)، Netflix DGS Framework (Kotlin/Java) و graphql-ruby. لتطوير المخطط واختبار الاستعلامات، يستخدم GraphiQL — IDE تفاعلي مضمن في المتصفح. يوفر Apollo Studio مقاييس الأداء، تتبع الاستعلامات وإدارة المخطط لبيئة الإنتاج. بشكل منفصل، يستحق GraphQL Code Generator الذكر — أداة تولد أنواع TypeScript، Kotlin، Swift و Dart من مخطط SDL.
لتطوير التطبيقات المحمولة، يهم بشكل خاص Apollo Kotlin (Apollo GraphQL) — مكتبة مكتوبة بالكامل بلغة Kotlin مع دعم للمعاصل المتوازي (coroutines)، Flow و Multiplatform. تسمح باستخدام استعلامات GraphQL موحدة لـ Android و iOS في مشاريع Kotlin Multiplatform. تقوم Apollo Kotlin بتوحيد المخزن المؤقت، تدعم الأخطاء على مستوى الحقل (أخطاء جزئية) وتولد تلقائيًا نماذج بيانات من ملفات .graphql. هذا يجعل GraphQL الخيار المفضل للمشاريع المحمولة الكبيرة حيث تكون سرعة التطوير وأمان الأنواع مهمة.
الأسئلة المتكررة
GraphQL لا يستبدل REST، بل يقدم نهجًا بديلًا. REST أفضل للواجهات البرمجية CRUD البسيطة، التخزين المؤقت HTTP والواجهات العامة ذات الحمل المتوقع. GraphQL أمثل للواجهات المعقدة ذات البيانات المترابطة المتعددة.
الهجرة ممكنة تدريجيًا: يمكن لـ GraphQL أن يعمل كطبقة (gateway) أمام خدمات REST القائمة. تقوم العديد من الشركات بإضافة GraphQL جوانب لـ REST دون إيقاف API القديم. الاستبدال الكامل يتطلب إعادة كتابة الحاسمات.
N+1 تحدث عندما يتم تنفيذ استعلام منفصل لقاعدة البيانات لكل عنصر في القائمة. يتم حلها باستخدام DataLoader — مكتبة تقوم بتجميع الطلبات الفردية في واحدة وتخزن النتائج مؤقتًا خلال طلب HTTP واحد.
مواصفة GraphQL لا تحدد رفع الملفات بشكل مباشر. في الممارسة، يتم استخدام: تشفير base64 (بسيط ولكن غير فعال للملفات الكبيرة)، طلبات multipart بحسب بروتوكول graphql-multipart-request-spec أو endpoint REST منفصل للملفات.
أمان GraphQL يتطلب تدابير إضافية: تحديد عمق التداخل، حدود تعقيد الاستعلام، تحديد المعدل على مستوى العملية. قد تكشف الاستبصار العام للمخطط عن هيكل البيانات — يوصى بتعطيله في بيئة الإنتاج.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا