قصة قهوة الصباح وتطبيق “الدكان”
يا جماعة الخير، والله زمان عن هذيك الأيام. أذكر مرة كنا نشتغل على تطبيق لمشروع “الدكان”، وهو متجر إلكتروني يبيع منتجات حرفية يدوية. التطبيق كان بسيطًا في فكرته، لكنه كان كابوسًا في أدائه على الشبكات البطيئة، خاصة في الواجهة الرئيسية.
الواجهة الرئيسية كانت تحتاج تعرض شوية معلومات من كل بستان زهرة: اسم المستخدم وصورته الشخصية في الأعلى، قائمة بأحدث 5 منتجات مع صورها المصغرة وأسعارها، وعدد الإشعارات غير المقروءة. فريق الواجهات الخلفية، جماعة شاطرين والله، عملوا لنا واجهات REST API “مرتبة” حسب رأيهم:
/api/users/me: للحصول على معلومات المستخدم./api/products?latest=5: للحصول على أحدث المنتجات./api/notifications/count: للحصول على عدد الإشعارات.
المشكلة؟ تطبيق الموبايل كان يضطر يرسل 3 طلبات مختلفة للشبكة (API Calls) بس عشان يرسم شاشة واحدة! هذا ما نسميه “التضور جوعًا للبيانات” أو Under-fetching. كل طلب له وقت استجابة، وتكاليف شبكة، وانتظار… والنتيجة؟ شاشة تظهر بالتقسيط، والمستخدم يمل ويترك التطبيق. ولما حكينا مع فريق الواجهات الخلفية، اقترحوا حلًا “عبقريًا”: عمل نقطة وصول (Endpoint) واحدة اسمها /api/home-screen ترجع كل شيء! وهنا وقعنا في الفخ الثاني.
نقطة الوصول الجديدة كانت ترجع “كوكتيل” بيانات ضخم. مثلًا، كائن المستخدم (User object) كان يرجع فيه كل تاريخه وعناوينه وسلة مشترياته، بينما نحن لا نريد سوى اسمه وصورته. والمنتجات كانت ترجع مع كل تفاصيلها ووصفها الطويل، ونحن لا نريد سوى الصورة والسعر. هذا هو جحيم “الغرق في البيانات” أو Over-fetching. التطبيق صار يحمّل بيانات بالهبل ما إلها لزوم، وهذا يستهلك باقة الإنترنت للمستخدم ويبطئ كل شيء.
كنت أجلس ذات صباح، أشرب فنجان قهوتي وأتصفح الإنترنت، وإذ بي أقرأ عن تقنية اسمها GraphQL. قرأت الجملة الأولى في موقعهم: “لغة استعلام لواجهة برمجة التطبيقات”. الفكرة بدت بسيطة وعبقرية: بدلًا من أن يقرر الخادم (Server) ما هي البيانات التي سيرسلها، يقوم العميل (Client) بطلب ما يحتاجه بالضبط، لا أكثر ولا أقل. وقتها لمعت عيناي وقلت: “هذا هو الحل يا جماعة!”.
ما هو الجحيم الذي كنا نعيشه؟ (مشاكل REST التقليدية)
قبل أن نغوص في حل GraphQL، دعونا نفصّل أكثر في المشاكل التي جعلت حياتنا صعبة مع REST APIs في سيناريوهات معينة.
h3: الغرق في البيانات (Over-fetching)
تخيل أنك تريد فقط معرفة أسماء المستخدمين في نظامك. في REST، قد يكون لديك نقطة وصول مثل GET /api/users. المشكلة أن هذه النقطة قد تكون مصممة لترجع كائن المستخدم الكامل:
// طلب REST: GET /api/users/1
// الاستجابة التي تصلك (أنت تحتاج الاسم فقط!)
{
"id": 1,
"name": "أبو عمر",
"username": "abu_omar_dev",
"email": "abu.omar@example.com",
"address": {
"street": "شارع يافا",
"city": "القدس",
"zipcode": "91000"
},
"phone": "059-XXXXXXX",
"website": "abuomar.dev",
"company": {
"name": "برمجة بلا حدود",
"catchPhrase": "نحول القهوة إلى كود"
},
"posts_count": 120,
"followers_count": 5000
// ... والمزيد من البيانات غير اللازمة
}
كل هذه البيانات الإضافية هي عبء على الشبكة، تزيد من وقت التحميل، وتستهلك بطارية الجهاز، خاصة على الهواتف المحمولة. أنت طلبت معلومة بحجم “نملة”، فأعطاك الخادم “فيلًا”.
h3: التضور جوعًا للبيانات (Under-fetching) والطلبات المتتالية
هذه هي المشكلة المعاكسة، وهي أكثر شيوعًا في التطبيقات المعقدة. لنعد لمثال تطبيق “الدكان”. لعرض الصفحة الرئيسية، كنت تحتاج:
- طلب
GET /api/postsللحصول على قائمة المقالات. - تصلك قائمة المقالات، لكن كل مقال يحتوي على
authorIdفقط. - الآن، لكل مقال في القائمة، عليك إرسال طلب جديد
GET /api/users/{authorId}للحصول على اسم الكاتب.
إذا كانت الصفحة تعرض 10 مقالات، فهذا يعني أنك سترسل 1 (للمقالات) + 10 (للكتاب) = 11 طلبًا للشبكة! هذه المشكلة تعرف بـ “N+1 Query Problem” وهي قاتلة لأداء التطبيق.
وظهر النور في آخر النفق: مرحبًا GraphQL!
GraphQL ليست مكتبة، ولا إطار عمل، بل هي “مواصفة” أو “لغة استعلام” (Query Language) للـ APIs، تم تطويرها في فيسبوك عام 2012 وأصبحت مفتوحة المصدر في 2015. الفكرة الأساسية بسيطة بشكل يبعث على الدهشة: أعطِ العميل القدرة على طلب البيانات التي يحتاجها بالضبط.
بدلًا من وجود عشرات نقاط الوصول (Endpoints) الثابتة كما في REST، توفر GraphQL عادةً نقطة وصول واحدة فقط (مثل /graphql). العميل يرسل “استعلامًا” (Query) لهذه النقطة، يصف فيه شكل البيانات التي يريدها، والخادم يرد بنفس الشكل تمامًا.
h3: كيف يعمل هذا السحر؟ المكونات الأساسية
- Schema (المخطط): هو عقد ملزم بين العميل والخادم. يصف كل البيانات المتاحة في الـ API وأنواعها والعلاقات بينها. هو بمثابة “الكتالوج” الذي يتصفحه المطور ليعرف ما يمكنه طلبه.
- Query (الاستعلام): هو ما يرسله العميل لطلب البيانات. يشبه إلى حد كبير كائن JSON بدون قيم.
- Mutation (التعديل): تشبه الـ Query، لكنها تستخدم لتعديل البيانات (إنشاء، تحديث، حذف).
- Resolvers (المُحلِّلات): هي “العضلات” في الخادم. لكل حقل في المخطط، هناك دالة (resolver function) مسؤولة عن جلب بيانات هذا الحقل من قاعدة البيانات أو من أي مصدر آخر.
كلام نظري ما بطعمي خبز: خلينا نشوف الكود
لنتخيل أننا نريد حل مشكلة الـ Under-fetching التي واجهناها في عرض المقالات وأسماء كتابها.
h3: الطريقة التقليدية (REST API)
كما ذكرنا، كنا سنحتاج لعدة طلبات:
// الطلب الأول
GET /api/posts
// الطلب الثاني (بعد استلام رد الطلب الأول)
GET /api/users/101 // لكاتب المقال الأول
// الطلب الثالث
GET /api/users/102 // لكاتب المقال الثاني
// وهكذا...
h3: طريقة الشغل النظيف (GraphQL)
الآن، مع GraphQL، يرسل العميل طلبًا واحدًا فقط يصف كل ما يحتاجه:
# هذا هو استعلام GraphQL
query GetPostsAndAuthors {
posts {
title
publishedDate
author {
name
avatarUrl
}
}
}
والخادم، بعد تلقي هذا الاستعلام، سيفهمه ويقوم بتجميع البيانات المطلوبة ويرسل استجابة JSON تبدو مطابقة تمامًا لشكل الطلب:
// هذه هي الاستجابة من الخادم (في طلب واحد!)
{
"data": {
"posts": [
{
"title": "مقالتي الأولى عن GraphQL",
"publishedDate": "2023-10-27",
"author": {
"name": "أبو عمر",
"avatarUrl": "https://example.com/abu_omar.jpg"
}
},
{
"title": "لماذا يجب أن تتعلم TypeScript",
"publishedDate": "2023-10-26",
"author": {
"name": "مطور آخر",
"avatarUrl": "https://example.com/another_dev.jpg"
}
}
]
}
}
لاحظ الجمال هنا: طلب واحد، استجابة واحدة، لا بيانات زائدة (no over-fetching)، ولا حاجة لطلبات إضافية (no under-fetching). العميل أخذ بالضبط ما طلبه. شغل من الآخر!
نصائح من “الختيار”: متى تستخدم GraphQL؟
بعد سنوات من العمل مع هذه التقنية، تعلمت أنها ليست “الحل السحري” لكل شيء. “ما في إشي بحل كل المشاكل”. إليك بعض النصائح من خبرتي العملية:
- التطبيقات المعقدة أولًا: إذا كان لديك تطبيق (خاصة موبايل أو Single Page Application) يحتاج بيانات من مصادر متعددة في شاشة واحدة، فـ GraphQL هو صديقك الصدوق.
- تعدد العملاء: عندما يكون لديك عدة واجهات (ويب، iOS، أندرويد، ساعة ذكية) ولكل منها متطلبات بيانات مختلفة، تتيح لك GraphQL خدمة الجميع من خلال API واحد مرن دون الحاجة لتخصيص نقاط وصول لكل عميل.
- لا تخف من منحنى التعلم: نعم، إعداد خادم GraphQL (المخطط والمحللات) يتطلب جهدًا في البداية أكبر من إعداد نقطة وصول REST بسيطة. لكن هذا الجهد يؤتي ثماره على المدى الطويل في سهولة الصيانة والتطوير.
- متى لا تستخدمها؟: إذا كان تطبيقك بسيطًا جدًا، أو لديك خدمة مصغرة (microservice) تؤدي وظيفة واحدة فقط (مثل خدمة إرسال الإيميلات)، فقد يكون استخدام REST API تقليدي أبسط وأسرع في التنفيذ. لا تعقّد الأمور بدون داعٍ.
نصيحة إضافية: GraphQL رائعة في “تغليف” الواجهات القديمة. يمكنك بناء واجهة GraphQL جديدة أمام واجهات REST القديمة لديك، مما يمنحك كل مزايا GraphQL دون الحاجة لإعادة كتابة كل شيء من الصفر.
الخلاصة: هل نرمي REST في البحر؟
بالتأكيد لا! REST لم تمت ولن تموت قريبًا. هي تقنية قوية، بسيطة، ومناسبة جدًا لآلاف السيناريوهات. لكن GraphQL جاءت لتقدم حلًا أنيقًا لمجموعة من المشاكل التي كانت تؤرقنا كمطورين، خاصة في عالم التطبيقات الحديثة التي تزداد تعقيدًا يومًا بعد يوم.
الخلاصة يا جماعة، أن تكون في جعبتك الأداتان (REST و GraphQL) وتعرف متى تستخدم كل منهما هو ما يجعلك مطورًا محترفًا. لا تكن متعصبًا لتقنية واحدة، بل افهم المشكلة التي بين يديك واختر لها السلاح المناسب.
أتمنى أن تكون هذه الرحلة السريعة قد أوضحت لكم قوة GraphQL وكيف يمكنها أن تنقذكم من جحيم البيانات. جربوها في مشروعكم القادم، ولن تندموا. 👍