إضافة وظائف ديناميكية وغير متزامنة إلى مواقع JAMstack

نشرت: 2022-03-10
ملخص سريع ↬ يمكن أن يؤدي تخطي الخوادم واستخدام JAMstack لإنشاء مواقع الويب والتطبيقات وتقديمها إلى توفير الوقت والمال والصداع من خلال السماح لنا بتقديم أصول ثابتة فقط على CDN. لكن المفاضلة بين التخلي عن عمليات النشر التقليدية القائمة على الخادم تعني أن الأساليب القياسية للتفاعلات الديناميكية وغير المتزامنة في مواقعنا وتطبيقاتنا لم تعد متاحة.

هل يعني ذلك أن مواقع JAMstack لا يمكنها التعامل مع التفاعلات الديناميكية؟ بالطبع لا!

تعتبر مواقع JAMstack رائعة لإنشاء تفاعلات غير متزامنة وديناميكية للغاية. مع بعض التعديلات الصغيرة على طريقة تفكيرنا في الكود الخاص بنا ، يمكننا إنشاء تفاعلات ممتعة وغامرة باستخدام أصول ثابتة فقط!

من الشائع بشكل متزايد رؤية مواقع الويب التي تم إنشاؤها باستخدام JAMstack - أي مواقع الويب التي يمكن تقديمها كملفات HTML ثابتة مبنية من JavaScript و Markup و APIs. تحب الشركات JAMstack لأنه يقلل من تكاليف البنية التحتية ، ويسرع التسليم ، ويقلل من الحواجز أمام تحسينات الأداء والأمان لأن شحن الأصول الثابتة يزيل الحاجة إلى توسيع نطاق الخوادم أو الحفاظ على قواعد البيانات متاحة بشكل كبير (مما يعني أيضًا عدم وجود خوادم أو قواعد بيانات يمكنها يتم اختراقها). يحب المطورون JAMstack لأنه يقلل من تعقيد الحصول على موقع ويب مباشر على الإنترنت: لا توجد خوادم لإدارتها أو نشرها ؛ يمكننا كتابة تعليمات برمجية للواجهة الأمامية ويتم نشرها مباشرة ، مثل السحر .

("Magic" في هذه الحالة هي عمليات نشر ثابتة آلية ، وهي متاحة مجانًا من عدد من الشركات ، بما في ذلك Netlify ، حيث أعمل.)

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

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

أساسيات JAMstack

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

المزيد بعد القفز! أكمل القراءة أدناه ↓

ما الذي يجعل موقع JAMstack "ثابتًا"؟

تقوم متصفحات الويب اليوم بتحميل ملفات HTML و CSS و JavaScript ، تمامًا كما فعلت في التسعينيات.

موقع JAMstack ، في جوهره ، هو مجلد مليء بملفات HTML و CSS وجافا سكريبت.

هذه "أصول ثابتة" ، مما يعني أننا لسنا بحاجة إلى خطوة وسيطة لإنشائها (على سبيل المثال ، تحتاج مشاريع PHP مثل WordPress إلى خادم لإنشاء HTML عند كل طلب).

هذه هي القوة الحقيقية لـ JAMstack: فهي لا تتطلب أي بنية تحتية متخصصة للعمل. يمكنك تشغيل موقع JAMstack على جهاز الكمبيوتر المحلي الخاص بك ، عن طريق وضعه على شبكة توصيل المحتوى المفضلة لديك (CDN) ، واستضافته بخدمات مثل GitHub Pages - يمكنك حتى سحب المجلد وإفلاته في عميل FTP المفضل لديك لتحميله للاستضافة المشتركة.

الأصول الثابتة لا تعني بالضرورة الخبرات الثابتة

نظرًا لأن مواقع JAMstack مصنوعة من ملفات ثابتة ، فمن السهل افتراض أن التجربة على تلك المواقع ثابتة ، كما تعلمون. لكن ليست هذه هي المسألة!

JavaScript قادر على عمل الكثير من الأشياء الديناميكية. بعد كل شيء ، تعد أطر عمل JavaScript الحديثة ملفات ثابتة بعد أن نمر بخطوة الإنشاء - وهناك المئات من الأمثلة على تجارب مواقع الويب الديناميكية بشكل لا يصدق والتي تدعمها.

هناك اعتقاد خاطئ شائع بأن كلمة "ثابت" تعني غير مرن أو ثابت. ولكن كل ما تعنيه كلمة "ثابت" حقًا في سياق "المواقع الثابتة" هو أن المتصفحات لا تحتاج إلى أي مساعدة في تقديم محتواها - يمكنهم استخدامها محليًا دون معالجة الخادم لخطوة المعالجة أولاً.

أو ضع بطريقة أخرى:

"الأصول الثابتة" لا تعني التطبيقات الثابتة ؛ هذا يعني عدم وجود خادم مطلوب.

"

هل يستطيع JAMstack القيام بذلك؟

إذا سأل شخص ما عن إنشاء تطبيق جديد ، فمن الشائع رؤية اقتراحات لأساليب JAMstack مثل Gatsby و Eleventy و Nuxt وغيرها من الأدوات المماثلة. من الشائع أيضًا ظهور اعتراضات: "لا تستطيع مولدات المواقع الثابتة عمل _______" ، حيث يعتبر _______ شيئًا ديناميكيًا.

ولكن - كما تطرقنا في القسم السابق - يمكن لمواقع JAMstack التعامل مع المحتوى الديناميكي والتفاعلات!

فيما يلي قائمة غير كاملة بالأشياء التي سمعتها مرارًا وتكرارًا يدعي البعض أن JAMstack لا يمكنه التعامل معها بالتأكيد:

  • تحميل البيانات بشكل غير متزامن
  • التعامل مع ملفات المعالجة ، مثل معالجة الصور
  • اقرأ من واكتب إلى قاعدة بيانات
  • تعامل مع مصادقة المستخدم وحماية المحتوى المحمي بتسجيل الدخول

في الأقسام التالية ، سننظر في كيفية تنفيذ كل من مهام سير العمل هذه على موقع JAMstack.

إذا كنت لا تستطيع الانتظار لرؤية JAMstack الديناميكي قيد التشغيل ، فيمكنك التحقق من العروض التوضيحية أولاً ، ثم العودة ومعرفة كيفية عملها.

ملاحظة حول العروض التوضيحية :

تتم كتابة هذه العروض التوضيحية بدون أي أطر. هم فقط HTML و CSS وجافا سكريبت قياسي. لقد تم تصميمها مع المتصفحات الحديثة (مثل Chrome و Firefox و Safari و Edge) في الاعتبار والاستفادة من الميزات الأحدث مثل وحدات JavaScript النمطية وقوالب HTML وواجهة Fetch API. لم تتم إضافة أي polyfills ، لذلك إذا كنت تستخدم متصفحًا غير مدعوم ، فمن المحتمل أن تفشل العروض التوضيحية.

تحميل البيانات من واجهة برمجة تطبيقات تابعة لجهة خارجية بشكل غير متزامن

"ماذا لو احتجت إلى الحصول على بيانات جديدة بعد إنشاء ملفاتي الثابتة؟"

في JAMstack ، يمكننا الاستفادة من العديد من مكتبات الطلبات غير المتزامنة ، بما في ذلك Fetch API المضمنة ، لتحميل البيانات باستخدام JavaScript في أي وقت.

عرض توضيحي: ابحث عن واجهة برمجة تطبيقات تابعة لجهة خارجية من موقع JAMstack

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

للتعامل مع ذلك ، نحتاج إلى:

  1. إنشاء نموذج حيث يمكن للأشخاص كتابة مصطلح البحث الخاص بهم ،
  2. استمع إلى إرسال النموذج ،
  3. احصل على مصطلح البحث من تقديم النموذج ،
  4. إرسال طلب غير متزامن إلى Rick & Morty API باستخدام مصطلح البحث ،
  5. عرض نتائج الطلب على الصفحة.

أولاً ، نحتاج إلى إنشاء نموذج وعنصر فارغ يحتوي على نتائج البحث الخاصة بنا ، والتي تبدو كالتالي:

 <form> <label for="name">Find characters by name</label> <input type="text" name="name" required /> <button type="submit">Search</button> </form> <ul></ul>

بعد ذلك ، نحتاج إلى كتابة دالة تتعامل مع عمليات إرسال النماذج. هذه الوظيفة سوف:

  • منع سلوك إرسال النموذج الافتراضي
  • احصل على مصطلح البحث من إدخال النموذج
  • استخدم Fetch API لإرسال طلب إلى Rick & Morty API باستخدام مصطلح البحث
  • اتصل بوظيفة المساعد التي تعرض نتائج البحث على الصفحة

نحتاج أيضًا إلى إضافة مستمع حدث في النموذج الخاص بحدث الإرسال الذي يستدعي وظيفة المعالج.

إليك ما يبدو عليه هذا الرمز تمامًا:

 <script type="module"> import showResults from './show-results.js'; const form = document.querySelector('form'); const handleSubmit = async event => { event.preventDefault(); // get the search term from the form input const name = form.elements['name'].value; // send a request to the Rick & Morty API based on the user input const characters = await fetch( `https://rickandmortyapi.com/api/character/?name=${name}`, ) .then(response => response.json()) .catch(error => console.error(error)); // add the search results to the DOM showResults(characters.results); }; form.addEventListener('submit', handleSubmit); </script>

ملاحظة: لمواصلة التركيز على سلوكيات JAMstack الديناميكية ، لن نناقش كيفية كتابة وظائف الأداة المساعدة مثل showResults. تم التعليق على الكود جيدًا ، لذا تحقق من المصدر لمعرفة كيفية عمله!

مع وضع هذا الرمز ، يمكننا تحميل موقعنا في متصفح وسنرى النموذج الفارغ مع عدم ظهور أي نتائج:

استمارة البحث فارغة
نموذج البحث الفارغ (معاينة كبيرة)

إذا أدخلنا اسم حرف (مثل "rick") ونقرنا على "بحث" ، فسنرى قائمة بالأحرف التي تحتوي أسماؤها على "rick" معروضة:

نموذج بحث مليء بـ "rick" مع عرض الأحرف المسماة "Rick" أدناه.
نرى نتائج البحث بعد ملء النموذج. (معاينة كبيرة)

يا! هل قام هذا الموقع الثابت بتحميل البيانات بشكل ديناميكي؟ دلاء المقدسة!

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

التعامل مع مهام الحوسبة باهظة الثمن خارج جهاز المستخدم

في العديد من التطبيقات ، نحتاج إلى القيام بأشياء تستهلك الكثير من الموارد ، مثل معالجة صورة. في حين أن بعض هذه الأنواع من العمليات ممكنة باستخدام JavaScript من جانب العميل فقط ، فليس من الضروري بالضرورة جعل أجهزة المستخدمين لديك تقوم بكل هذا العمل. إذا كانوا يستخدمون جهازًا منخفض الطاقة أو يحاولون إطالة آخر 5٪ من عمر البطارية ، فمن المحتمل أن يكون جعل أجهزتهم يقوم بمجموعة من الأعمال تجربة محبطة بالنسبة لهم.

فهل هذا يعني أن تطبيقات JAMstack لم يحالفها الحظ؟ لا على الاطلاق!

يرمز الحرف "A" في JAMstack إلى واجهات برمجة التطبيقات. هذا يعني أنه يمكننا إرسال هذا العمل إلى واجهة برمجة التطبيقات وتجنب تدوير مراوح الكمبيوتر الخاصة بمستخدمينا إلى إعداد "التمرير".

قد تقول "لكن انتظر". "إذا كان تطبيقنا يحتاج إلى القيام بعمل مخصص ، وهذا العمل يتطلب واجهة برمجة تطبيقات ، ألا يعني هذا أننا نبني خادمًا فقط؟"

بفضل قوة الوظائف التي لا تحتاج إلى خادم ، لسنا مضطرين لذلك!

الوظائف التي لا تحتوي على خادم (وتسمى أيضًا "وظائف lambda") هي نوع من واجهة برمجة التطبيقات (API) دون الحاجة إلى أي نموذج معياري للخادم. نحصل على وظيفة JavaScript قديمة بسيطة ، وجميع أعمال النشر والتوسيع والتوجيه وما إلى ذلك يتم إلغاؤها لمزودنا الذي لا يحتاج إلى خادم.

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

"

الوظائف التي لا تحتاج إلى خادم هي زبدة الفول السوداني لـ JAMstack الخاصة بنا: فهي تفتح عالماً كاملاً من الوظائف الديناميكية عالية القدرة دون أن تطلب منا أبدًا التعامل مع كود الخادم أو المطورين.

عرض توضيحي: تحويل صورة إلى تدرج الرمادي

لنفترض أن لدينا تطبيقًا يحتاج إلى:

  • قم بتنزيل صورة من URL
  • تحويل تلك الصورة إلى التدرج الرمادي
  • قم بتحميل الصورة المحولة إلى GitHub repo

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

بدلاً من ذلك ، يمكننا إرسال عنوان URL ليتم تحويله إلى وظيفة بدون خادم ، والتي ستقوم بالعبء الثقيل بالنسبة لنا وإرسال عنوان URL مرة أخرى إلى صورة محولة.

بالنسبة لوظيفة عدم وجود خادم ، سنستخدم وظائف Netlify. في كود موقعنا ، نضيف مجلدًا على مستوى الجذر يسمى "وظائف" وننشئ ملفًا جديدًا يسمى "convert-image.js" بالداخل. ثم نكتب ما يسمى المعالج ، وهو ما يستقبل - وكما خمنت - يتعامل مع الطلبات إلى وظيفتنا التي لا تحتاج إلى خادم.

لتحويل صورة ، تبدو كالتالي:

 exports.handler = async event => { // only try to handle POST requests if (event.httpMethod !== 'POST') { return { statusCode: 404, body: '404 Not Found' }; } try { // get the image URL from the POST submission const { imageURL } = JSON.parse(event.body); // use a temporary directory to avoid intermediate file cruft // see https://www.npmjs.com/package/tmp const tmpDir = tmp.dirSync(); const convertedPath = await convertToGrayscale(imageURL, tmpDir); // upload the processed image to GitHub const response = await uploadToGitHub(convertedPath, tmpDir.name); return { statusCode: 200, body: JSON.stringify({ url: response.data.content.download_url, }), }; } catch (error) { return { statusCode: 500, body: JSON.stringify(error.message), }; } };

تقوم هذه الوظيفة بما يلي:

  1. يتحقق للتأكد من إرسال الطلب باستخدام طريقة HTTP POST
  2. ينتزع عنوان URL للصورة من نص POST
  3. يقوم بإنشاء دليل مؤقت لتخزين الملفات التي سيتم تنظيفها بمجرد الانتهاء من تنفيذ الوظيفة
  4. يستدعي وظيفة مساعدة تقوم بتحويل الصورة إلى تدرج رمادي
  5. يستدعي وظيفة مساعدة تقوم بتحميل الصورة المحولة إلى GitHub
  6. يُرجع كائن استجابة برمز حالة HTTP 200 وعنوان URL للصورة التي تم تحميلها حديثًا

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

بعد ذلك ، نحتاج إلى إضافة نموذج سيتم استخدامه لإرسال عناوين URL للمعالجة ومكانًا لإظهار ما قبل وبعد:

 <form action="/.netlify/functions/convert-image" method="POST" > <label for="imageURL">URL of an image to convert</label> <input type="url" name="imageURL" required /> <button type="submit">Convert</button> </form> <div></div>

أخيرًا ، نحتاج إلى إضافة مستمع حدث إلى النموذج حتى نتمكن من إرسال عناوين URL إلى وظيفة بدون خادم لدينا للمعالجة:

 <script type="module"> import showResults from './show-results.js'; const form = document.querySelector('form'); form.addEventListener('submit', event => { event.preventDefault(); // get the image URL from the form const imageURL = form.elements['imageURL'].value; // send the image off for processing const promise = fetch('/.netlify/functions/convert-image', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ imageURL }), }) .then(result => result.json()) .catch(error => console.error(error)); // do the work to show the result on the page showResults(imageURL, promise); }); </script>

بعد نشر الموقع (مع مجلد "الوظائف" الجديد الخاص به) على Netlify و / أو بدء تشغيل Netlify Dev في CLI الخاص بنا ، يمكننا رؤية النموذج في متصفحنا:

نموذج تحويل صورة فارغة
نموذج فارغ يقبل عنوان URL للصورة (معاينة كبيرة)

إذا أضفنا عنوان URL للصورة إلى النموذج ثم نقرنا على "تحويل" ، فسنرى "معالجة ..." للحظة أثناء حدوث التحويل ، ثم سنرى الصورة الأصلية ونظيرتها ذات التدرج الرمادي الذي تم إنشاؤه حديثًا:

نموذج مليء بعنوان URL للصورة ، يظهر الصورة الأصلية أدناه على اليسار والصورة المحولة إلى اليمين
يتم تحويل الصورة من الألوان الكاملة إلى التدرج الرمادي. (معاينة كبيرة)

يا دانغ! تعامل موقع JAMstack الخاص بنا مع بعض الأعمال الجادة جدًا ولم يكن علينا التفكير في الخوادم مرة واحدة أو استنزاف بطاريات مستخدمينا!

استخدم قاعدة بيانات لتخزين واسترداد الإدخالات

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

قد تفكر ، "إذن هذا كل شيء ، أليس كذلك؟ الخدعة قد انتهت؟ من المؤكد أن موقع JAMstack - الذي أخبرتنا أنه مجرد مجموعة من الملفات في مجلد - لا يمكن توصيله بقاعدة بيانات! "

Au نقيض.

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

وبالمثل ، يمكننا استخدام أدوات قاعدة البيانات كخدمة (DBaaS) (مثل Fauna) للقراءة والكتابة إلى قاعدة بيانات دون الحاجة إلى إعداد واحدة أو استضافتها بأنفسنا.

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

العرض التوضيحي: قم بإنشاء صفحة عريضة

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

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

  1. قم بإنشاء حساب Fauna على https://fauna.com
  2. انقر فوق "إنشاء قاعدة بيانات جديدة"
  3. امنح قاعدة البيانات اسمًا (على سبيل المثال ، "dynamic-jamstack-demos")
  4. انقر فوق "إنشاء"
  5. انقر فوق "الأمان" في القائمة اليمنى في الصفحة التالية
  6. انقر فوق "مفتاح جديد"
  7. تغيير قائمة الأدوار المنسدلة إلى "الخادم"
  8. أضف اسمًا للمفتاح (مثل "Dynamic JAMstack Demos")
  9. قم بتخزين المفتاح في مكان آمن لاستخدامه مع التطبيق
  10. انقر فوق "حفظ"
  11. انقر فوق "GraphQL" في القائمة اليمنى
  12. انقر على "مخطط الاستيراد"
  13. قم بتحميل ملف يسمى db-schema.gql يحتوي على الكود التالي:
 type Signature { name: String! } type Query { signatures: [Signature!]! }

بمجرد تحميل المخطط ، تصبح قاعدة بياناتنا جاهزة للاستخدام. (عنجد.)

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

لتجربتها ، يمنحنا خيار "GraphQL" في القائمة اليمنى مستكشف GraphQL مع توثيق للاستعلامات والطفرات المتاحة التي تسمح لنا بإجراء عمليات CRUD.

ملاحظة : لن ندخل في تفاصيل حول استعلامات وطفرات GraphQL في هذا المنشور ، ولكن إيف بورسيلو كتبت مقدمة ممتازة لإرسال استعلامات وطفرات GraphQL إذا كنت تريد كتابًا تمهيديًا عن كيفية عملها.

مع استعداد قاعدة البيانات للعمل ، يمكننا إنشاء وظيفة بدون خادم تخزن التوقيعات الجديدة في قاعدة البيانات:

 const qs = require('querystring'); const graphql = require('./util/graphql'); exports.handler = async event => { try { // get the signature from the POST data const { signature } = qs.parse(event.body); const ADD_SIGNATURE = ` mutation($signature: String!) { createSignature(data: { name: $signature }) { _id } } `; // store the signature in the database await graphql(ADD_SIGNATURE, { signature }); // send people back to the petition page return { statusCode: 302, headers: { Location: '/03-store-data/', }, // body is unused in 3xx codes, but required in all function responses body: 'redirecting...', }; } catch (error) { return { statusCode: 500, body: JSON.stringify(error.message), }; } };

تقوم هذه الوظيفة بما يلي:

  1. ينتزع قيمة التوقيع من نموذج بيانات POST
  2. يستدعي وظيفة المساعد التي تخزن التوقيع في قاعدة البيانات
  3. يحدد طفرة GraphQL للكتابة إلى قاعدة البيانات
  4. يرسل الطفرة باستخدام وظيفة مساعد GraphQL
  5. يعيد التوجيه إلى الصفحة التي قدمت البيانات

بعد ذلك ، نحتاج إلى وظيفة بدون خادم لقراءة جميع التوقيعات من قاعدة البيانات حتى نتمكن من إظهار عدد الأشخاص الذين يدعمون عريضتنا:

 const graphql = require('./util/graphql'); exports.handler = async () => { const { signatures } = await graphql(` query { signatures { data { name } } } `); return { statusCode: 200, body: JSON.stringify(signatures.data), }; };

ترسل هذه الوظيفة استعلامًا وتعيده.

ملاحظة مهمة حول المفاتيح الحساسة وتطبيقات JAMstack :

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

بمجرد إعداد وظائفنا بدون خادم ، يمكننا إضافة نموذج يتم إرساله إلى الوظيفة لإضافة توقيع ، وعنصر لإظهار التوقيعات الحالية ، وقليلًا من JS لاستدعاء الوظيفة للحصول على التوقيعات ووضعها في شاشة العرض الخاصة بنا جزء:

 <form action="/.netlify/functions/add-signature" method="POST"> <label for="signature">Your name</label> <input type="text" name="signature" required /> <button type="submit">Sign</button> </form> <ul class="signatures"></ul> <script> fetch('/.netlify/functions/get-signatures') .then(res => res.json()) .then(names => { const signatures = document.querySelector('.signatures'); names.forEach(({ name }) => { const li = document.createElement('li'); li.innerText = name; signatures.appendChild(li); }); }); </script>

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

نموذج عريضة فارغ مع قائمة التوقيعات أدناه
نموذج فارغ يقبل توقيع رقمي (معاينة كبيرة)

ثم ، إذا أضفنا توقيعنا ...

نموذج عريضة باسم في الحقل ، لكن لم يتم تقديمه بعد
نموذج العريضة مع ملء الاسم (معاينة كبيرة)

... وأرسله ، سنرى اسمنا مرفقًا في أسفل القائمة:

نموذج عريضة فارغ مع وجود توقيع جديد في أسفل القائمة
يتم مسح نموذج الالتماس ويضاف التوقيع الجديد إلى أسفل القائمة. (معاينة كبيرة)

الكلب diggity الساخن! لقد كتبنا للتو تطبيق JAMstack الكامل المدعوم من قاعدة البيانات مع 75 سطرًا من التعليمات البرمجية و 7 أسطر من مخطط قاعدة البيانات!

حماية المحتوى مع مصادقة المستخدم

"حسنًا ، أنت بالتأكيد عالق هذه المرة" ، ربما تفكر. "لا توجد طريقة يمكن أن يتعامل بها موقع JAMstack مع مصادقة المستخدم. كيف سيعمل هذا ، حتى؟! "

سأخبرك كيف يعمل ، يا صديقي: من خلال وظائفنا الموثوقة التي لا تحتاج إلى خادم و OAuth.

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

ملاحظة: لن نتعمق في كيفية عمل OAuth ، لكن Aaron Parecki كتب نظرة عامة قوية عن OAuth تغطي التفاصيل وسير العمل.

في تطبيقات JAMstack ، يمكننا الاستفادة من OAuth و JSON Web Tokens (JWTs) التي توفرها لنا لتحديد المستخدمين وحماية المحتوى والسماح للمستخدمين المسجلين فقط بمشاهدته.

العرض التوضيحي: طلب تسجيل الدخول لعرض المحتوى المحمي

إذا احتجنا إلى إنشاء موقع لا يعرض سوى المحتوى للمستخدمين الذين قاموا بتسجيل الدخول ، فنحن بحاجة إلى بعض الأشياء:

  1. موفر هوية يدير المستخدمين وتدفق تسجيل الدخول
  2. عناصر واجهة المستخدم لإدارة تسجيل الدخول وتسجيل الخروج
  3. وظيفة بدون خادم تتحقق من وجود مستخدم قام بتسجيل الدخول باستخدام JWTs وتعيد محتوى محميًا إذا تم توفيره

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

لتمكينه:

  • قم بزيارة لوحة معلومات Netlify الخاصة بك
  • اختر الموقع الذي يحتاج إلى مصادقة من قائمة المواقع الخاصة بك
  • انقر فوق "هوية" في التنقل العلوي
  • انقر فوق الزر "تمكين الهوية"

يمكننا إضافة Netlify Identity إلى موقعنا عن طريق إضافة ترميز يعرض المحتوى الذي تم تسجيل الخروج منه ويضيف عنصرًا لإظهار المحتوى المحمي بعد تسجيل الدخول:

 <div class="content logged-out"> <h1>Super Secret Stuff!</h1> <p> only my bestest friends can see this content</p> <button class="login">log in / sign up to be my best friend</button> </div> <div class="content logged-in"> <div class="secret-stuff"></div> <button class="logout">log out</button> </div>

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

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

بعد ذلك ، نحتاج إلى إضافة رمز لجعل زر تسجيل الدخول يعمل ، وتحميل المحتوى المحمي ، وإظهاره على الشاشة:

 <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script> <script> const login = document.querySelector('.login'); login.addEventListener('click', () => { netlifyIdentity.open(); }); const logout = document.querySelector('.logout'); logout.addEventListener('click', () => { netlifyIdentity.logout(); }); netlifyIdentity.on('logout', () => { document.querySelector('body').classList.remove('authenticated'); }); netlifyIdentity.on('login', async () => { document.querySelector('body').classList.add('authenticated'); const token = await netlifyIdentity.currentUser().jwt(); const response = await fetch('/.netlify/functions/get-secret-content', { headers: { Authorization: `Bearer ${token}`, }, }).then(res => res.text()); document.querySelector('.secret-stuff').innerHTML = response; }); </script>

إليك ما يفعله هذا الرمز:

  1. يقوم بتحميل عنصر واجهة مستخدم Netlify Identity ، وهو عبارة عن مكتبة مساعدة تقوم بإنشاء نموذج تسجيل الدخول ، ويتعامل مع سير عمل OAuth باستخدام Netlify Identity ، ويمنح تطبيقنا إمكانية الوصول إلى معلومات المستخدم الذي قام بتسجيل الدخول
  2. يضيف مستمعًا للأحداث إلى زر تسجيل الدخول الذي يؤدي إلى فتح نموذج تسجيل الدخول إلى Netlify Identity
  3. يضيف مستمع حدث إلى زر تسجيل الخروج الذي يستدعي طريقة تسجيل الخروج من Netlify Identity
  4. يضيف معالج حدث لتسجيل الخروج لإزالة الفئة المصادق عليها عند تسجيل الخروج ، والتي تخفي المحتوى الذي تم تسجيل الدخول إليه وتعرض المحتوى الذي تم تسجيل الخروج منه
  5. يضيف معالج حدث لتسجيل الدخول:
    1. يضيف الفئة المصادق عليها لإظهار المحتوى الذي تم تسجيل الدخول إليه وإخفاء المحتوى الذي تم تسجيل الخروج منه
    2. الاستيلاء على JWT للمستخدم الذي قام بتسجيل الدخول
    3. لاستدعاء وظيفة بدون خادم لتحميل محتوى محمي ، وإرسال JWT في عنوان التفويض
    4. يضع المحتوى السري في عنصر div الأسري حتى يتمكن المستخدمون المسجلون من رؤيته

في الوقت الحالي ، لا توجد وظيفة بدون خادم نستدعيها في هذا الرمز. لنقم بإنشائه بالكود التالي:

 exports.handler = async (_event, context) => { try { const { user } = context.clientContext; if (!user) throw new Error('Not Authorized'); return { statusCode: 200, headers: { 'Content-Type': 'text/html', }, body: `

أنت مدعو ، $ {user.user_metadata.full_name}!

إذا كنت تستطيع قراءة هذا ، فهذا يعني أننا أفضل الأصدقاء.

فيما يلي التفاصيل السرية لحفلة عيد ميلادي:
jason.af/party

"، } ؛ } catch (خطأ) { إرجاع { رمز الحالة: 401 ، النص الأساسي: "غير مصرح به" ، } ؛ } } ؛

تقوم هذه الوظيفة بما يلي:

  1. للتحقق من وجود مستخدم في وسيطة سياق الوظيفة التي لا تحتاج إلى خادم
  2. يطرح خطأ إذا لم يتم العثور على مستخدم
  3. يُعيد المحتوى السري بعد التأكد من أن المستخدم الذي قام بتسجيل الدخول قد طلب ذلك

سوف تكتشف وظائف Netlify JWTs في رؤوس التفويضات وتضع هذه المعلومات تلقائيًا في السياق - وهذا يعني أنه يمكننا التحقق من JWTs الصالحة دون الحاجة إلى كتابة تعليمات برمجية للتحقق من JWTs!

عندما نقوم بتحميل هذه الصفحة في متصفحنا ، سنرى صفحة تسجيل الخروج أولاً:

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

إذا نقرنا على الزر لتسجيل الدخول ، فسنرى أداة Netlify Identity:

نافذة مشروطة تُظهر علامتي تبويب التسجيل وتسجيل الدخول مع عرض نموذج تسجيل الدخول
توفر أداة تعريف Netlify تجربة تسجيل الدخول / الاشتراك بالكامل. (معاينة كبيرة)

بعد تسجيل الدخول (أو التسجيل) ، يمكننا رؤية المحتوى المحمي:

عرض تسجيل الدخول يعرض معلومات حول حفلة عيد ميلاد
بعد تسجيل الدخول ، يمكننا رؤية المحتوى المحمي. (معاينة كبيرة)

رائع! لقد أضفنا للتو تسجيل دخول المستخدم والمحتوى المحمي إلى تطبيق JAMstack!

ما العمل التالي

يعد JAMstack أكثر من مجرد "مواقع ثابتة" - يمكننا الاستجابة لتفاعلات المستخدم ، وتخزين البيانات ، والتعامل مع مصادقة المستخدم ، وأي شيء آخر نريد القيام به على موقع ويب حديث. وكل ذلك دون الحاجة إلى توفير خادم أو تكوينه أو نشره!

ماذا تريد أن تبني باستخدام JAMstack؟ هل ما زلت غير مقتنع بأن JAMstack يمكنه التعامل مع أي شيء؟ أحب أن أسمع عن ذلك - تابعني على Twitter أو في التعليقات!