حكاية فنجان قهوة وكابوس الـ “Endpoints”
أذكرها وكأنها البارحة، كنا في المراحل الأخيرة من تطوير تطبيق جوال لأحد العملاء المهمين. الأجواء كانت مشحونة، والموعد النهائي يلوح في الأفق مثل غيمة سوداء. كنت مسؤولًا عن الفريق الخلفي (Backend)، وكان زملائي في الفريق الأمامي (Frontend) يعملون على قدم وساق لإنهاء واجهات المستخدم.
في صباح أحد الأيام، دخل عليّ “خليل”، قائد الفريق الأمامي، وعلى وجهه علامات اليأس والإرهاق. وضع فنجان قهوته على طاولتي وقال بنبرة فيها عتب: “يا أبو عمر، مش قادرين نكمّل هيك! إشي بيجلط!”.
سألته بهدوء: “خير يا خليل، شوفي؟”.
بدأ يشرح: “يا زلمة، شاشة ملف المستخدم لحالها بدها 3 طلبات API مختلفة! طلب عشان أجيب معلومات المستخدم الأساسية، وطلب ثاني عشان أجيب آخر 10 منشورات إله، وطلب ثالث عشان أجيب عدد المتابعين. التطبيق بطيء والمستخدم رح يكرهنا!”. ثم أضاف: “وفوق كل هاد، في شاشة ثانية بس بحتاج اسم المستخدم وصورته، بس الـ API تبعكم برجعلي كل تاريخ حياته من يوم ما انولد! إحنا بنغرق في بيانات ما بتلزمنا”.
كان خليل على حق. لقد وصف بكلماته البسيطة المعضلة الكلاسيكية التي كنا نعيشها مع REST API: مشكلتي الـ Under-fetching (الحاجة لعدة طلبات للحصول على البيانات المطلوبة) والـ Over-fetching (الحصول على بيانات أكثر من اللازم).
تلك المحادثة كانت نقطة التحول. كانت الشرارة التي دفعتنا للبحث عن حل جذري، وكان هذا الحل يحمل اسمًا واحدًا: GraphQL.
كابوس الواجهات التقليدية: تشريح مشكلة الـ Over/Under-fetching
قبل أن نغوص في عالم GraphQL، خلّينا نحكي بصراحة ونفصّل المشكلة اللي كان “خليل” وفريقه بواجوهها. هذه المشاكل هي صميم بنية REST API التقليدية.
الـ Under-fetching: ماراثون الطلبات المتعددة
تخيل أنك تريد عرض صفحة ملف شخصي لمستخدم، تحتاج فيها إلى:
- معلومات المستخدم (الاسم، الصورة الشخصية).
- آخر 3 منشورات له.
- قائمة بـ 5 من أصدقائه.
في عالم REST النموذجي، سيبدو الأمر كالتالي:
- الطلب الأول:
GET /api/users/123للحصول على معلومات المستخدم. - الطلب الثاني:
GET /api/users/123/posts?limit=3للحصول على منشوراته. - الطلب الثالث:
GET /api/users/123/friends?limit=5للحصول على أصدقائه.
النتيجة؟ العميل (التطبيق) مجبر على إرسال ثلاثة طلبات منفصلة للشبكة وانتظار ثلاث استجابات. هذا يؤدي إلى بطء في تحميل الواجهة وتجربة مستخدم سيئة، خاصة على اتصالات الإنترنت البطيئة في تطبيقات الجوال.
الـ Over-fetching: حمولة زائدة لا لزوم لها
الآن، لننظر إلى الوجه الآخر للعملة. لنفترض أنك في شاشة أخرى، كل ما تحتاجه هو عرض قائمة بأسماء المستخدمين. تقوم بإرسال طلب إلى: GET /api/users.
لكن الـ Endpoint هذا مصمم ليكون شاملًا، فيرجع لك لكل مستخدم في القائمة مجموعة هائلة من البيانات:
[
{
"id": 123,
"username": "abu_omar_dev",
"firstName": "عمر",
"lastName": "أحمد",
"email": "abu_omar@example.com",
"dateOfBirth": "1985-10-26T00:00:00.000Z",
"address": {
"street": "شارع يافا",
"city": "القدس",
"country": "فلسطين"
},
"createdAt": "2020-01-01T10:00:00.000Z",
"lastLogin": "2023-10-27T12:30:00.000Z"
// ... و 20 حقل آخر
},
// ... باقي المستخدمين
]
كل ما كنت تحتاجه هو username، لكنك استلمت “تاريخ حياة” كل مستخدم. أنت تستهلك بيانات وباندويث (Bandwidth) بدون أي داعٍ، وتجبر العميل على تحليل بيانات ضخمة هو في غنى عنها.
GraphQL: لغة الاستعلام التي منحت القوة للعميل
هنا يأتي دور GraphQL. ببساطة شديدة، GraphQL هي لغة استعلام (Query Language) لواجهات برمجة التطبيقات، وهي أيضًا بيئة تشغيل لتنفيذ هذه الاستعلامات على بياناتك الموجودة.
الفكرة الجوهرية التي غيرت كل شيء: بدلًا من أن يحدد الخادم (Backend) شكل البيانات التي يرسلها، يقوم العميل (Frontend) بتحديد شكل البيانات التي يريد استقبالها بالضبط.
نقطة نهاية واحدة (Single Endpoint) تحكم كل شيء
في GraphQL، عادة ما يكون لديك نقطة نهاية واحدة فقط (مثلاً /graphql). كل تواصلك مع الخادم يتم من خلالها. لا مزيد من /users، /posts، /comments. كل شيء يذهب إلى مكان واحد.
العميل هو من يقرر: قوة الاستعلام (Query)
لحل مشكلة “خليل” في شاشة ملف المستخدم، بدلًا من إرسال 3 طلبات، سيرسل الفريق الأمامي طلبًا واحدًا فقط إلى /graphql، وسيبدو هذا الطلب (الذي يسمى Query) كالتالي:
query GetUserProfile {
user(id: "123") {
name
profilePicture
posts(last: 3) {
title
content
}
friends(first: 5) {
name
}
}
}
والأجمل من ذلك؟ ستكون الاستجابة من الخادم عبارة عن ملف JSON يطابق تمامًا بنية الطلب الذي أرسلته. لا زيادة ولا نقصان:
{
"data": {
"user": {
"name": "أبو عمر",
"profilePicture": "url/to/image.jpg",
"posts": [
{ "title": "مقدمة عن GraphQL", "content": "..." },
{ "title": "لماذا Rust لغة المستقبل؟", "content": "..." },
{ "title": "الذكاء الاصطناعي في حياتنا", "content": "..." }
],
"friends": [
{ "name": "خليل" },
{ "name": "سارة" },
{ "name": "محمد" },
{ "name": "فاطمة" },
{ "name": "علي" }
]
}
}
}
لاحظ الجمال هنا: طلب واحد، استجابة واحدة، وكل البيانات التي تحتاجها بالضبط. مشكلة الـ Under-fetching حُلّت. ومشكلة الـ Over-fetching؟ إذا أردت اسم المستخدم وصورته فقط، ببساطة اطلب ذلك:
query GetUserHeader {
user(id: "123") {
name
profilePicture
}
}
الخادم سيرسل لك هذين الحقلين فقط. القوة الآن في يد المطور الأمامي.
نصائح عملية من “أبو عمر”
الشغلة مش سحر يا جماعة، هي تقنية رائعة لكن تحتاج لفهم وتطبيق صحيح. من خلال تجربتنا، تعلمت بعض الدروس التي أحب أن أشاركها معكم.
نصيحة 1: لا تهدم بيتك القديم! ابدأ بالتغليف (Wrapping)
أكبر خطأ قد ترتكبه هو محاولة إعادة كتابة كل واجهات REST API القديمة باستخدام GraphQL من اليوم الأول. هذا انتحار تقني!
الحل العملي: ابدأ ببناء طبقة GraphQL “تغلف” واجهات REST الموجودة لديك. كل حقل في GraphQL (يسمى Resolver) يمكن أن يقوم داخليًا باستدعاء الـ Endpoint المناسب في REST API القديمة. بهذه الطريقة، يحصل الفريق الأمامي على كل فوائد GraphQL فورًا، بينما تقوم أنت بترحيل المنطق تدريجيًا خلف الكواليس.
نصيحة 2: الـ Caching يختلف، فكن حذرًا
واجهات REST تستفيد من Caching على مستوى HTTP بسهولة (لأن GET /api/users/123 هو طلب فريد). مع GraphQL، كل الطلبات تذهب إلى POST /graphql، مما يجعل الـ Caching على مستوى الشبكة أصعب.
الحل العملي: اعتمد على مكتبات الـ Caching الذكية في العميل (Client-side) مثل Apollo Client أو Relay. هذه المكتبات تقوم بعمل رائع في فهم استعلامات GraphQL وتخزين نتائجها مؤقتًا وتحديث الواجهة بذكاء.
نصيحة 3: GraphQL ليست دائمًا الحل الأمثل
نعم، لقد قلتها. مع كل حبي لـ GraphQL، هي ليست الحل لكل المشاكل. واجهات REST لا تزال ممتازة وبسيطة جدًا لبعض الحالات، مثل:
- الواجهات الداخلية بين الخدمات المصغرة (Microservices) التي لا تحتاج لهذه المرونة.
- واجهات بسيطة جدًا تعتمد على الموارد (Resource-based) مثل واجهة للتحكم في الطابعات أو أجهزة IoT.
الحل العملي: استخدم الأداة المناسبة للمهمة المناسبة. لا تقع في فخ “المطرقة الذهبية” وتعتقد أن GraphQL يجب أن تستخدم في كل مكان.
نصيحة 4: استثمر في الأدوات (Tooling)
جمال GraphQL يكمن في نظامها البيئي. أدوات مثل GraphiQL أو Apollo Studio هي بمثابة ملعب تفاعلي لواجهة برمجة التطبيقات الخاصة بك. تسمح للمطورين باستكشاف الـ Schema (هيكل البيانات المتاح)، وبناء الاستعلامات، وتجربتها مباشرة من المتصفح.
الحل العملي: قم بإعداد بيئة استكشاف (Explorer) مع الـ API الخاص بك من اليوم الأول. ستصبح هذه الأداة أفضل صديق للمطورين الأماميين، وستقلل من الأسئلة التي تصلك بشكل كبير.
الخلاصة: هل يجب أن تنتقل إلى GraphQL؟ 🤔
العودة إلى قصتنا، بعد أن تبنينا GraphQL، تغيرت ديناميكية العمل تمامًا. “خليل” وفريقه أصبحوا أكثر إنتاجية وسعادة. التطبيق أصبح أسرع، والطلبات على الخوادم أصبحت أكثر كفاءة. لم تكن رحلة سهلة، وتطلبت منا تعلم مفاهيم جديدة، لكن النتيجة كانت تستحق العناء بكل تأكيد.
إذًا، هل يجب عليك استخدام GraphQL؟
استخدمها إذا:
- لديك تطبيقات متعددة (ويب، جوال، تلفاز ذكي) تستهلك نفس الـ API باحتياجات مختلفة.
- واجهات المستخدم لديك معقدة وتحتاج إلى جلب بيانات متداخلة من مصادر مختلفة.
- فريقك الأمامي يشتكي باستمرار من بطء الـ API أو الحاجة لتعديلات مستمرة في الـ Backend.
GraphQL ليست مجرد تقنية جديدة لامعة، بل هي نقلة نوعية في طريقة تفكيرنا وتصميمنا لواجهات برمجة التطبيقات. إنها تعيد القوة والمرونة إلى حيث يجب أن تكون: في يد المطور الذي يبني تجربة المستخدم. لا تخف من تجربتها، ابدأ صغيرًا، وتعلم من أخطائك، وسترى بنفسك كيف يمكنها أن تنقذك من جحيم الطلبات الزائدة والبيانات الناقصة. 👍