FlowingDev

GraphQL، بنسخته الأنيقة: اللغة السرية للاستعلامات والمخططات المُرتبة

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

جرّب الأداة: منسق GraphQL

في جملة واحدة

تنسيق GraphQL هو فن تطبيق قواعد تنسيق متناسقة على الاستعلامات (queries) والتعديلات (mutations) والمخططات (schemas)، لتحويل فوضى من الأقواس والحقول إلى تحفة فنية سهلة القراءة والصيانة.

المشكلة التي يحلها

قديمًا، إذا أردت جلب بيانات من خادم لتطبيق الويب الجديد الرائع الخاص بك، كنت على الأرجح ستستخدم REST API. كنت ستطلب من نقطة وصول (endpoint) مثل /users/123 للحصول على بيانات المستخدم، و /users/123/posts للحصول على منشوراته. المشكلة؟ قد تحصل على بيانات مستخدم أكثر بكثير مما تحتاج (over-fetching)، أو قد تضطر للقيام برحلات متعددة ذهابًا وإيابًا للحصول على كل البيانات التي تحتاجها فعلاً (under-fetching).

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

# عطني بس اسم المستخدم 42 وعناوين أول 3 منشورات له
query GetUserNameAndPosts {
  user(id: "42") {
    name
    posts(first: 3) {
      title
    }
  }
}

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

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

كيف تعمل تحت الغطاء

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

التحليل (Parsing): من نص إلى شجرة

أولاً، يجب على المنسق قراءة السلسلة النصية الخام لكود GraphQL وفهم ما هيتها. لا يمكنه فقط البحث عن { وإضافة سطر جديد. بل يحتاج إلى معرفة ما إذا كان هذا القوس يفتح استعلامًا، أو تعريف نوع (type definition)، أو كائن إدخال (input object).

تسمى هذه العملية التحليل (parsing). يقوم المنسق بتقسيم المدخلات إلى رموز (tokenize)، أي تقسيمها إلى أجزاء ذات معنى مثل query، user، (، id، :، "42"، )) ثم يبني شجرة بنية مجردة (Abstract Syntax Tree - AST). الـ AST هي بنية بيانات تشبه الشجرة تمثل البنية النحوية للكود.

لاستعلام بسيط مثل:

query { user { name } }

قد تبدو الـ AST شيئًا كهذا (بطريقة مبسطة ومفاهيمية):

- Document
  - Definition (OperationDefinition, type: query)
    - SelectionSet
      - Selection (Field)
        - name: "user"
        - SelectionSet
          - Selection (Field)
            - name: "name"

لم يعد النص مجرد نص؛ بل أصبح كائنًا منظمًا يمكن للبرنامج التعامل معه بذكاء.

قواعد التنسيق

بمجرد أن يحصل المنسق على الـ AST، يمكنه التنقل عبر الشجرة وتطبيق قواعد التنسيق الخاصة به. هذه القواعد هي جوهر عملية التنسيق وغالبًا ما تكون موضوع نقاشات المطورين (غير المجدية في الغالب). تشمل القواعد الشائعة:

  • المسافة البادئة (Indentation): كم عدد المسافات (أو التابات، إذا كنت من الوحوش الذين يستخدمونها) التي يجب استخدامها لكل مستوى من التداخل. المعيار شبه العالمي هو مسافتان.
  • فواصل الأسطر (Line Breaks): متى يجب وضع الأشياء على سطر جديد. هل يجب أن يكون القوس الافتتاحي { على نفس السطر مع اسم الحقل أم على سطر جديد؟ (معظم المنسقات تضعه على نفس السطر).
  • المسافات (Spacing): ضمان وجود مسافة متناسقة حول العوامل مثل النقطتين الرأسيتين وداخل الأقواس.
  • فرز الحقول (Field Sorting): بالنسبة للمخططات الكبيرة، يمكن لبعض المنسقات حتى فرز الحقول أبجديًا لتسهيل العثور عليها.

أدوات مثل Prettier اشتهرت بكونها "صاحبة رأي" (opinionated) - فهي تتخذ هذه القرارات عنك، حتى لا تضطر إلى الجدال حولها. الهدف ليس إيجاد الأسلوب "المثالي" الوحيد، بل اختيار أسلوب واحد وتطبيقه بلا هوادة.

الطباعة الجميلة (Pretty-Printing): من شجرة إلى نص مجددًا

بعد تطبيق القواعد، تكون المهمة النهائية للمنسق هي أخذ الـ AST المعدلة وتحويلها مرة أخرى إلى سلسلة نصية. تسمى هذه العملية الطباعة الجميلة (pretty-printing). يتجول المنسق في الشجرة، وعند كل عقدة (مثل Field أو SelectionSet)، يطبع النص المقابل، مع إضافة المسافة البادئة وفواصل الأسطر الصحيحة وفقًا للقواعد.

والنتيجة هي سلسلة نصية من GraphQL منسقة بشكل جميل.

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

قصص من الواقع

حكاية جلسة تصحيح الأخطاء في منتصف الليل

كانت ياسمين، مهندسة الواجهات الخلفية، في نوبة العمل. في الساعة 1:30 صباحًا، انطلق تنبيه: تعديل GraphQL حرج يفشل في بيئة الإنتاج. الدليل الوحيد كان إدخال سجل يحتوي على الاستعلام الدقيق الذي أرسله العميل - سطر واحد غير مفهوم بطول 3000 حرف، تم نسخه ولصقه من حزمة JavaScript مضغوطة. حدقت في جدار النص ...customer{address{...، محاولةً العثور على الجزء المشوه. غابت عيناها. محبطة، ألقت السلسلة النصية بأكملها في منسق GraphQL. على الفور، ازدهر الاستعلام إلى بنية من 70 سطرًا، منسقة بشكل مثالي. وها هو، واضح وضوح الشمس في السطر 47: خطأ إملائي في اسم حقل حاسم، adress بدلاً من address. كان الإصلاح تافهًا، لكنها لم تستطع حتى رؤية المشكلة حتى تم تنسيقها.

الدرس: سهولة القراءة هي الخطوة الأولى والأهم نحو القدرة على تصحيح الأخطاء. المنسق يحول كتلة نصية مبهمة إلى شيء يمكن للإنسان تحليله فعليًا.

طلب السحب (Pull Request) الذي رفض الاندماج

كان فريق صغير يبني واجهة خلفية جديدة لمتجر إلكتروني باستخدام GraphQL. كان مطوران، ليام وأوليفيا، يعملان على ميزة جديدة. قام ليام بتهيئة محرره لاستخدام مسافة بادئة من 4 مسافات. أما أوليفيا، من محبي المسافتين، فكان لديها إعداد مختلف. عندما قدم ليام طلب السحب الخاص به، راجعته أوليفيا، وأجرت بعض التغييرات المنطقية، ودفعت تعديلها (commit). كان "الفرق" (diff) الناتج بحرًا من اللونين الأحمر والأخضر. تم تمييز كل سطر تقريبًا على أنه متغير، ببساطة لأن محرريهما كانا يتصارعان حول المسافات البيضاء. التغييرات الفعلية ذات المعنى ضاعت تمامًا في الضوضاء. اضطر القائد التقني إلى قضاء ساعة في فك تشابك الفوضى. في اليوم التالي، أضاف منسق GraphQL آلي إلى pre-commit hook الخاص بهم. الآن، يتم تنسيق كل الكود بنفس المعيار تمامًا قبل أن يتم عمل commit له.

الدرس: التنسيق الآلي يقضي على الجدالات حول الأسلوب ويحافظ على نظافة سجل نظام التحكم بالإصدارات، مما يركز المراجعات على ما يهم: المنطق.

المخطط (Schema) الذي بدا كطبق سباغيتي

نما مخطط GraphQL لشركة ناشئة بشكل عضوي على مدى ثلاث سنوات. تمت إضافة الأنواع أينما كان هناك مكان، وكانت الحقول بدون ترتيب معين، والتعليقات متفرقة. بالنسبة لموظف جديد، كانت محاولة فهم نموذج بيانات الـ API أشبه بمحاولة فك تشابك درج مليء بالكابلات القديمة. قرروا إجراء تجربة: قاموا بتمرير ملف schema.graphql بأكمله إلى منسق. لم تقم الأداة فقط بوضع مسافات بادئة لكل شيء بشكل صحيح، بل قامت أيضًا بفرز جميع الحقول داخل كل نوع أبجديًا. فجأة، أصبح id دائمًا الحقل الأول. تم تجميع الحقول المهملة (deprecated) معًا. الهيكل بأكمله اتضح فجأة. لم يكن أجمل فقط؛ بل أصبح الآن قطعة توثيق مفيدة.

الدرس: المخطط المنسق جيدًا يعمل كتوثيق حي. يكشف عن بنية وهدف الـ API الخاص بك، مما يجعله أسهل في الفهم للجميع.

أخطاء وفخاخ شائعة

  • الجدال حول الأسلوب. أكبر فخ هو إضاعة ساعات في النقاش حول التابات مقابل المسافات أو أين يجب أن يوضع القوس. قيمة التنسيق تكمن في الاتساق. اختر أداة شائعة وذات رأي مثل Prettier، اتفقوا على استخدامها، وامضوا قدمًا.
  • نسيان التنسيق قبل عمل commit. إذا كانت عملية التنسيق يدوية، سينساها الناس. هذا يؤدي إلى الفروقات الفوضوية التي كنت تحاول تجنبها. ادمج التنسيق في pre-commit hook (باستخدام أدوات مثل Husky و lint-staged) لجعله تلقائيًا وبدون مجهود.
  • الخلط بين التنسيق والتدقيق (linting). المنسق يجعل كودك يبدو متناسقًا. أما المدقق (linter) (مثل eslint-plugin-graphql) فيفحص كودك بحثًا عن أخطاء محتملة أو ممارسات سيئة، مثل استخدام حقل مهمل أو كتابة استعلام غير فعال. أنت بحاجة إلى كليهما. المنسق ينظف المطبخ؛ أما المدقق فيتأكد مما إذا كنت قد تركت الموقد مشتعلًا.
  • تنسيق الكود المُنشأ آليًا. بعض مسارات العمل تنشئ ملفات مخطط GraphQL أو استعلامات من مصدر آخر (مثل مخطط قاعدة بيانات أو لغة برمجة مختلفة). غالبًا ما يكون تنسيق الناتج مضيعة للوقت، حيث سيتم الكتابة فوق تغييراتك في المرة التالية التي يتم فيها إنشاء الكود. قم بتنسيق المصدر بدلاً من ذلك.
  • إرسال استعلامات منسقة "جميلة" في بيئة الإنتاج. في حين أن المسافات البادئة الجميلة رائعة للتطوير، إلا أنها بايتات ضائعة عبر الشبكة. يجب أن تقوم عملية البناء (build process) الخاصة بك بتصغير (minify) استعلامات GraphQL قبل إرسالها من تطبيق العميل إلى الخادم.

لماذا يجب أن يلفت انتباهك

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

إنها أداة أساسية لتطوير البرمجيات الاحترافية تنطبق بشكل مثالي على GraphQL. الأمر لا يتعلق بجعل الأشياء "جميلة" من أجل الجمال فقط. بل يتعلق بـ:

  • الوضوح: جعل الكود أسهل في القراءة والفهم.
  • القابلية للصيانة: جعل الكود أسهل في التغيير وتصحيح الأخطاء.
  • التعاون: تقليل الاحتكاك بين أعضاء الفريق عن طريق أتمتة الخيارات الأسلوبية.

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

تعمق أكثر

انتهينا من النظرية. حان وقت التطبيق — 100% في متصفحك.

جرّب الأداة: منسق GraphQL