GraphQL Resolvers: أفضل الممارسات

من graphql.org

هذا المنشور هو الجزء الأول من سلسلة من أفضل الممارسات والملاحظات التي أجريناها أثناء بناء واجهات برمجة تطبيقات GraphQL في PayPal. في المشاركات القادمة ، سنشارك أفكارنا حول: تصميم المخطط ، معالجة الأخطاء ، رؤية الإنتاج ، تحسين التكامل من جانب العميل والأدوات للفرق.

ربما تكون قد شاهدت منشورنا السابق "GraphQL: قصة نجاح لـ PayPal Checkout" حول رحلة PayPal من REST إلى GraphQL. يغطس هذا المنشور في التفاصيل بعض أفضل الممارسات لبناء أدوات حل سريعة وقابلة للاختبار ومرنة بمرور الوقت.

ما هو محلل؟

لنبدأ في نفس الأساس. ما هو محلل؟

تعريف محلل
كل حقل في كل نوع مدعوم بوظيفة تسمى محلل.

محلل هو وظيفة تعمل على حل قيمة لنوع أو حقل في مخطط. يمكن أن تعيد المحاليل كائنات أو أدوات قياس مثل السلاسل والأرقام والمنطقية ، وما إلى ذلك. في حالة إرجاع كائن ، يستمر التنفيذ في الحقل الفرعي التالي. إذا تم إرجاع العددية (عادة في عقدة ورقة) ، اكتمال التنفيذ. إذا تم إرجاع خالية ، فإن التنفيذ يتوقف ولا يستمر.

يمكن أن تكون الحلول غير متزامنة أيضًا! يمكنهم حل القيم من واجهة REST API أخرى ، قاعدة بيانات ، ذاكرة تخزين مؤقت ، ثابت ، إلخ.

في وقت لاحق ، سنتعرف على سلسلة من الأمثلة التي توضح كيفية بناء أدوات حل سريعة وقابلة للاختبار ومرنة.

تنفيذ الاستعلامات

لفهم الحلول بشكل أفضل ، تحتاج إلى معرفة كيفية تنفيذ الاستعلامات.

يمر كل استعلام GraphQL عبر ثلاث مراحل. يتم تحليل الاستعلامات والتحقق من صحتها وتنفيذها.

  1. تحليل - يتم تحليل استعلام إلى شجرة بناء جملة مجردة (أو AST). ASTs قوية بشكل لا يصدق وخلف أدوات مثل ESLint ، بابل ، وما إلى ذلك. إذا كنت تريد أن ترى ما يشبه GraphQL AST ، تحقق من astexplorer.net وتغيير JavaScript إلى GraphQL. سترى استعلامًا على اليسار و AST على اليمين.
  2. التحقق من الصحة - يتم التحقق من صحة AST مقابل المخطط. يتحقق من بناء جملة الاستعلام الصحيح وإذا كانت الحقول موجودة.
  3. التنفيذ - يمر وقت التشغيل عبر AST ، بدءًا من جذر الشجرة ، ويستدعي المحللون ، ويجمع النتائج ، وينبعث JSON.

في هذا المثال ، سنشير إلى هذا الاستعلام:

الاستعلام للرجوع إليه لاحقًا

عندما يتم تحليل هذا الاستعلام ، يتم تحويله إلى AST أو شجرة.

الاستعلام ممثلة كشجرة

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

يتم تنفيذ حقول استعلام الجذر ، مثل المستخدم والألبوم ، بشكل متوازٍ ولكن دون ترتيب معين. عادةً ما يتم تنفيذ الحقول بالترتيب الذي تظهر به في الاستعلام ، لكن ليس من الآمن افتراض ذلك. نظرًا لأن الحقول يتم تنفيذها بشكل متوازٍ ، فمن المفترض أن تكون خالية من التأثيرات الذرية والذرية.

أبحث عن قرب في محللات

في الأقسام القليلة التالية ، سنستخدم JavaScript ، ولكن يمكن كتابة خوادم GraphQL بأي لغة تقريبًا.

يحل مع أربع حجج - الجذر ، الحجج ، السياق ، المعلومات

بشكل أو بآخر ، يتلقى كل محلل في كل لغة هذه الوسائط الأربعة:

  • الجذر - النتيجة من النوع السابق / الأصل
  • args - الوسائط المقدمة إلى الحقل
  • سياق - كائن قابل للتغيير الذي يتم توفيره لجميع محللات
  • معلومات - معلومات خاصة بالحقل ذات صلة بالاستعلام (نادراً ما تستخدم)

تعد هذه الوسائط الأربعة أساسية لفهم كيفية تدفق البيانات بين المحللون.

محللات الافتراضي

قبل المتابعة ، تجدر الإشارة إلى أن خادم GraphQL يحتوي على مُحلِّلات افتراضية مضمّنة ، لذلك لا يتعين عليك تحديد وظيفة محلل لكل حقل. سيبحث محلل افتراضي في الجذر للعثور على خاصية بنفس اسم الحقل. يبدو أن التنفيذ يبدو كالتالي:

تنفيذ محلل افتراضي

جلب البيانات في محللات

أين يجب علينا جلب البيانات؟ ما هي المقايضات مع خياراتنا؟

في الأمثلة القليلة التالية ، سنعود إلى هذا المخطط:

يحتوي حقل الحدث على وسيطة معرّف مطلوبة ، ويقوم بإرجاع حدث

تمرير البيانات بين محللات

السياق هو كائن قابل للتغيير يتم توفيره لجميع المجهزين. تم إنشاؤه وتدميره بين كل طلب. إنه مكان رائع لتخزين بيانات Auth الشائعة ، والنماذج / أدوات جلب البيانات الشائعة لواجهات برمجة التطبيقات وقواعد البيانات ، إلخ.

عندما تتعلم عن السياق لأول مرة ، قد يكون التفكير الأولي هو استخدام السياق كذاكرة تخزين مؤقت للأغراض العامة. هذا غير مستحسن ، ولكن فيما يلي الشكل الذي قد يبدو عليه التنفيذ.

تمرير البيانات بين محللات باستخدام السياق. هذا غير مستحسن!

عندما يتم استدعاء العنوان ، نقوم بتخزين نتيجة الحدث في السياق. عندما يتم استدعاء photoUrl ، فإننا نخرج الحدث من السياق ونستخدمه. هذا الرمز غير موثوق. ليس هناك ما يضمن أن العنوان سيتم تنفيذه قبل photoUrl.

يمكن أن نصلح كلاً من محللي الحل للتحقق من وجود الحدث في السياق. إذا كان الأمر كذلك ، استخدمه. بخلاف ذلك ، نأتي به ونخزنه في وقت لاحق ، ولكن لا تزال هناك مساحة كبيرة للأخطاء.

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

تمرير البيانات من الوالدين إلى الطفل

الوسيطة الجذر هي لتمرير البيانات من محللات الأصل إلى محللات تابعة.

على سبيل المثال ، إذا كنت تقوم ببناء نوع حدث حيث جميع حقول الحدث
تعتمد على نفس البيانات ، قد ترغب في جلبها مرة واحدة في حقل الحدث ،
بدلا من في كل مجال من مجالات الحدث.

يبدو وكأنه فكرة جيدة ، أليس كذلك؟ هذه طريقة سريعة للبدء في بناء أدوات حل المشكلات ولكن قد تواجه بعض المشكلات. لنفهم السبب.

بالنسبة إلى الأمثلة أدناه ، سنعمل مع نوع الحدث الذي يحتوي على حقلين.

نوع الحدث مع اثنين من الحقول: العنوان و photoUrl

يمكن جلب معظم حقول الأحداث من واجهة برمجة تطبيقات للأحداث ، حتى نتمكن من جلبها في محلل أحداث المستوى الأعلى وتقديم النتائج لعناويننا ومحللي photoUrl.

يحل محلل الأحداث من المستوى الأعلى البيانات ، ويوفر نتائج لمحللي حقل العنوان و photoUrl

والأفضل من ذلك ، لا نحتاج إلى تحديد الحلين السفليين.
يمكننا استخدام أدوات التحليل الافتراضية لأن الكائن تم إرجاعه بواسطة getEvent ()
لديه الملكية و photoUrl الممتلكات.

يتم حل المعرف والعنوان باستخدام محللات الإعداد الافتراضية

ما المشكلة في هذا؟

هناك سيناريوهان قد تصادف فيهما ...

السيناريو رقم 1: جلب البيانات متعددة الطبقات

دعنا نقول بعض المتطلبات تأتي وتحتاج إلى عرض الحضور لهذا الحدث. نبدأ بإضافة حقل الحضور إلى الحدث.

نوع الحدث مع حقل حضور إضافي

عندما تجلب تفاصيل الحاضرين ، يكون لديك خياران: إحضار تلك البيانات في محلل الأحداث ، أو محلل الحضور.

سنختبر الخيار الأول: إضافته إلى محلل الأحداث.

يستدعي محلل الأحداث واجهات برمجة التطبيقات (APIs) ، وجلب تفاصيل الحدث وتفاصيل الحضور

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

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

لدينا خيار واحد آخر للاختبار بجلب الحاضرين داخل محلل الحضور.

يحضر محلل الحضور تفاصيل الحضور من واجهة برمجة تطبيقات الحضور

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

السيناريو رقم 2: مشكلة N + 1

نظرًا لأن البيانات يتم جلبها على مستوى الحقل ، فإننا نواجه خطر التجاوز. الجلب ومشكلة N + 1 هو موضوع شائع في عالم GraphQL. يحتوي Shopify على مقالة رائعة تشرح N + 1 جيدًا.

كيف يؤثر هذا علينا هنا؟

لتوضيح ذلك بشكل أفضل ، سنضيف حقل أحداث جديد يُرجع جميع الأحداث.

إرجاع حقل الأحداث جميع الأحداث.الاستعلام عن جميع الأحداث مع عنوانهم والحضور

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

يتم تضخيم هذه المشكلة في مؤسسة كبيرة حيث يمكن أن تتخلص الطلبات وتسبب ضغوطًا غير ضرورية على نظامك.

لحل هذه المشكلة ، نحتاج إلى تجميع الطلبات وإلغاء خداعها!

في JavaScript ، بعض الخيارات الشائعة هي dataloader و Apollo.

إذا كنت تستخدم لغة أخرى ، فمن المحتمل وجود شيء يمكنك التقاطه. حتى نلقي نظرة حولك قبل حل هذا بنفسك.

في صميمها ، تقع هذه المكتبات في أعلى طبقة الوصول إلى البيانات الخاصة بك وستقوم بتخزين البيانات الصادرة مؤقتًا وإلغاء خداعتها باستخدام ميزة debending أو memoization. إذا كنت مهتمًا بما تبدو عليه المذكرة غير المتزامنة ، فتحقق من دانيال برين الرائع!

جلب البيانات على مستوى المجال

في وقت سابق ، رأينا أنه من السهل التعرض للإحراق من خلال الجلب بمحللات "الوالدين إلى الطفل".

هل هناك بديل أفضل؟

دعنا ننفذ خيار الوالدين إلى الطفل مرة أخرى. ماذا لو عكسنا ذلك بحيث تكون حقولنا التابعة مسؤولة عن جلب البيانات الخاصة بهم؟

الحقول مسؤولة عن جلب البيانات الخاصة بها.
لماذا هذا بديل أفضل؟

هذا الكود سهل العقل. أنت تعرف بالضبط أين يتم جلب البريد الإلكتروني. وهذا يجعل لسهولة التصحيح.

هذا الرمز هو أكثر قابلية للاختبار. ليس عليك اختبار محلل الأحداث عندما تريد حقًا اختبار محلل العنوان.

بالنسبة للبعض ، قد يبدو التكرار getEvent مثل رائحة رمز. لكن امتلاك شفرة بسيطة وسهلة الفهم وأكثر قابلية للاختبار أمر يستحق القليل من الازدواجية.

ولكن ، لا تزال هناك مشكلة محتملة هنا. إذا كان عميل يستعلم عن العنوان و photoUrl ، فإننا نتسبب في طلب إضافي واحد على واجهة برمجة تطبيقات الأحداث الخاصة بنا مع getEvent. كما رأينا سابقًا في مشكلة N + 1 ، ينبغي لنا إلغاء الطلبات على مستوى الإطار باستخدام مكتبات مثل dataloader ومصادر بيانات Apollo.

إذا جلبنا البيانات على مستوى الحقل وطلبات إلغاء التحميل ، فسيكون لدينا رمز يسهل تصحيحه واختباره ، ويمكننا جلب البيانات على النحو الأمثل دون التفكير في الأمر.

أفضل الممارسات

  • يجب استخدام إحضار البيانات من الأب إلى الطفل ونقلها بشكل ضئيل.
  • استخدم المكتبات مثل dataloader لإلغاء طلبات المتلقين للمعلومات.
  • كن على علم بأي ضغوط تسببها على مصادر البيانات الخاصة بك.
  • لا تقم بتحويل "السياق". يضمن ثابت ، وأقل عربات التي تجرها الدواب رمز.
  • اكتب محللات قابلة للقراءة ، قابلة للصيانة ، قابلة للاختبار. ليس ذكي جدا.
  • اجعل المحلات الخاصة بك رقيقة قدر الإمكان. استخرج البيانات التي تجلب المنطق إلى وظائف مزامنة قابلة لإعادة الاستخدام.

ترقب!

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

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

نقوم بالتوظيف! إذا كنت ترغب في العمل على بنية تحتية أمامية أو GraphQL أو React في PayPal ، فإن DM على Twitter علىmark_stuart!