يُعَد Express إطار عمل ويب شائع الاستخدام وغير مشتبث برأيه Unopinionated، أي لديه آراء حول الطريقة الصحيحة للتعامل مع أيّ مهمة معينة، ويدعم التطور السريع أو حل المشاكل في مجال معين، ومكتوب بلغة جافا سكريبت Javascript ومُستضاف في بيئة تشغيل Node.js. سنوضح في هذه السلسلة من المقالات المتفرعة عن السلسة الرئيسية تعلم تطوير الويب بعض الفوائد الرئيسية لإطار عمل Express، وكيفية إعداد بيئة التطوير وتطبيق مهام تطوير ونشر الويب الشائعة.
المتطلبات الأساسية
يجب قبل البدء التعرف على مفهوم برمجة الويب من طرف الخادم وأطر الويب من خلال الاطلاع على مقال مدخل إلى برمجة مواقع الويب من طرف الخادم. يوصَى بشدة بمعرفة مفاهيم البرمجة ولغة جافا سكريبت، ولكنها ليست ضرورية لفهم المفاهيم الأساسية.
ملاحظة: اطلع على مقال أساسيات لغة جافا سكريبت في سياق التطوير من طرف العميل، إذ تعَد لغة ومفاهيم جافا سكريبت الأساسية هي نفسها بالنسبة للتطوير من طرف الخادم في بيئة Node.js. تقدم Node.js واجهات برمجة تطبيقات إضافية لدعم الوظائف المفيدة في البيئات التي لا تستخدم المتصفحات (لإنشاء خوادم HTTP والوصول إلى نظام الملفات مثلًا)، ولكنها لا تدعم واجهات برمجة تطبيقات جافا سكريبت للعمل مع المتصفحات ونموذج DOM. ستوفر هذه السلسلة من المقالات بعض المعلومات حول العمل مع بيئة Node.js وإطار عمل Express.
تتألف هذه السلسلة من المقالات التالية:
- مدخل إلى إطار عمل الويب Express: نجيب في هذا المقال على أسئلة "ما هي بيئة Node؟" و"ما هو إطار عمل Express؟"، وسنأخذ فكرةً عامة على ما يجعل إطار عمل Express مميزًا، وسنحدد الميزات الرئيسية وسنعرض بعض البنى الأساسية لتطبيق Express، بالرغم من أنه لن يكون لديك بعد بيئة تطوير لاختبارها في هذه المرحلة.
- إعداد بيئة تطوير Node (في إطار عمل Express) (هذا المقال): سنوضح في هذا المقال كيفية إعداد واختبار بيئة تطوير Node/Express على أنظمة تشغيل ويندوز ولينكس (أوبنتو) وماك أو اس macOS. يجب أن يوفر لك هذا المقال ما تحتاجه لتتمكن من بدء تطوير تطبيقات Express مهما كان نظام التشغيل الذي تستخدمه.
- تطبيق عملي لتعلم Express - الجزء الأول: إنشاء موقع ويب هيكلي لمكتبة محلية: يشرح المقال الأول من التطبيق العملي لتعلم إطار عمل Express ما ستتعلمه، ويقدم نظرة عامة على مثال موقع "المكتبة المحلية" الذي سنعمل عليه ونطوره في المقالات اللاحقة. يوضح هذا المقال كيفية إنشاء مشروع موقع ويب هيكلي، والذي يمكنك ملؤه لاحقًا بالوجهات Routes والقوالب أو العروض وقواعد البيانات الخاصة بالموقع.
- تطبيق عملي لتعلم Express - الجزء الثاني: استخدام قاعدة البيانات (باستخدام مكتبة Mongoose): يوضح هذا المقال بإيجاز قواعد بيانات Node/Express، ثم يوضح كيفية استخدام مكتبة Mongoose لتوفير الوصول إلى قاعدة البيانات لموقع المكتبة المحلية LocalLibrary، ويشرح كيفية التصريح عن مخطط Schema الكائنات والنماذج وأنواع الحقول الرئيسية والتحقق من صحة البيانات الأساسي، ويعرض بإيجاز بعض الوجهات الرئيسية التي يمكنك من خلالها الوصول إلى بيانات النموذج.
- تطبيق عملي لتعلم Express - الجزء الثالث: الوجهات Routes والمتحكمات Controllers: سنُعِد في هذا المقال الوجهات (شيفرة معالجة عناوين URL) باستخدام دوال معالجة "وهمية dummy" لجميع النقاط النهائية للموارد التي سنحتاجها في موقع LocalLibrary. سيكون لدينا في النهاية بنية معيارية لشيفرة معالجة الوجهات، والتي يمكننا توسيعها باستخدام دوال معالجة حقيقية في المقالات اللاحقة. سنوضح أيضًا كيفية إنشاء وجهات معيارية باستخدام إطار عمل Express.
- تطبيق عملي لتعلم Express - الجزء الرابع: عرض بيانات المكتبة والعمل مع الاستمارات: سنضيف في هذا المقال الصفحات التي تعرض كتب موقع LocalLibrary وبيانات أخرى، حيث ستتضمن الصفحات صفحة رئيسية توضح عدد السجلات لكل نوع نموذج وصفحات القائمة والصفحات التفصيلية لجميع نماذجنا، وسنكتسب في هذا المقال خبرة عملية في الحصول على السجلات من قاعدة البيانات واستخدام القوالب. سنشرح أيضًا كيفية العمل مع استمارات HTML في إطار عمل Express باستخدام مكتبة Pug، وخاصة كيفية كتابة الاستمارات لإنشاء المستندات وتحديثها وحذفها من قاعدة البيانات.
- تطبيق عملي لتعلم Express - الجزء الخامس: النشر في بيئة الإنتاج: أنشأت موقع المكتبة المحلية LocalLibrary، ولكنك تريد تثبيته على خادم ويب عام حتى يصل إليه موظفو وأعضاء المكتبة عبر الإنترنت، لذا يقدّم هذا المقال نظرة عامة حول كيفية البحث عن مضيف لنشر موقعك، وما عليك فعله لتجهيز موقعك لمرحلة الإنتاج.
هذه هي جميع المواضيع التي يجب تعلّمها، ولكن إذا أردتَ معرفة المزيد وتوسيع هذه السلسلة من المقالات، فاطلع على بعض الموضوعات الأخرى التي يجب تغطيتها وهي:
- استخدام الجلسات.
- استيثاق المستخدمين.
- الترخيص للمستخدمين وأذوناتهم.
- اختبار تطبيق Express.
- أمان الويب لتطبيقات Express.
كما سيكون وجود تقييم نهائي إضافةً رائعة.
سنجيب في هذا المقال على الأسئلة: "ما هي بيئة Node؟" و"ما هو إطار عمل Express؟"، ويقدّم نظرة عامة على ما يجعل إطار عمل Express مميزًا. سنحدد الميزات الرئيسية، وسنعرض بعض البنى الأساسية لتطبيق Express، بالرغم من أنه لن يكون لديك بعد بيئة تطوير لاختبارها في هذه المرحلة.
-
المتطلبات الأساسية: المهارات الحاسوبية الأساسية، وفهم عام لبرمجة مواقع الويب من طرف الخادم وخاصةً تفاعلات الخادم مع العميل في مواقع الويب.
-
الهدف: التعرف على إطار عمل Express وكيفية ملاءمته مع بيئة Node، والوظائف التي يوفرها، والمكونات الأساسية لتطبيق Express.
مدخل إلى بيئة Node
تُعَد Node -أو Node.js رسميًا- بيئة تشغيل مفتوحة المصدر ومتوافقة مع منصات مختلفة وتتيح للمطورين إنشاء جميع أنواع الأدوات والتطبيقات من طرف الخادم في شيفرة جافا سكريبت. يُعَد وقت التشغيل مخصَّصًا للاستخدام خارج سياق المتصفح، أي التشغيل مباشرةً على حاسوب أو نظام تشغيل خادم، وبالتالي تحذف البيئة واجهات برمجة تطبيقات جافا سكريبت الخاصة بالمتصفح وتضيف دعمًا لواجهات برمجة تطبيقات نظام التشغيل التقليدية بما في ذلك مكتبات HTTP ومكتبات نظام الملفات.
تتمتع بيئة Node بعدد من الفوائد من منظور تطوير خادم الويب، وهي:
- أداء رائع: صُمِّمت بيئة Node لتحسين الإنتاجية وقابلية التوسع في تطبيقات الويب، وتُعَد حلًا جيدًا للعديد من مشاكل تطوير الويب الشائعة، مثل تطبيقات الويب في الوقت الفعلي.
- تُكتَب الشيفرة البرمجية باستخدام لغة جافا سكريبت قديمة وبسيطة، مما يقلل الوقت في التعامل مع "تغيير السياق" بين اللغات عند كتابة الشيفرة البرمجية من طرف العميل والخادم.
- تُعَد لغة جافا سكريبت لغة برمجة جديدة نسبيًا وتستفيد من التحسينات في تصميم اللغة عند مقارنتها مع لغات خادم الويب التقليدية الأخرى، مثل بايثون و PHP وغير ذلك. تُصرَّف أو تُحوَّل العديد من اللغات الجديدة والشائعة إلى لغة جافا سكريبت حتى يمكنك استخدام لغات TypeScript و CoffeeScript و ClojureScript و Scala و LiveScript وغيرها.
- يوفر مدير حزم Node -أو اختصارًا npm- الوصول إلى مئات الألوف من الحزم القابلة لإعادة الاستخدام، ولديه التحليل Resolution الأفضل للاعتماديات Dependency Resolution، ويمكن استخدامه لأتمتة معظم سلسلة أدوات البناء.
- تُعَد بيئة Node.js قابلة للنقل، فهي متوفرة على أنظمة تشغيل مايكروسوفت ويندوز وماك أو اس macOS ولينكس و Solaris و FreeBSD و OpenBSD و WebOS و NonStop OS، ويدعمها العديد من مزودي خدمات استضافة الويب الذين يوفرون في أغلب الأحيان بنية تحتية وتوثيقًا محددًا لاستضافة مواقع Node.
- تمتلك نظام بيئي ومجتمع مطورين خارجي نشط جدًا بوجود الكثير من الأشخاص المستعدين للمساعدة.
يمكنك استخدام بيئة ب Node.js لإنشاء خادم ويب بسيط باستخدام حزمة Node HTTP.
مرحبا Node.js
ينشئ المثال التالي خادم ويب يستمع إلى أيّ نوع من طلبات HTTP على عنوان URL هو http://127.0.0.1:8000/
، إذ سيستجيب السكريبت بالسلسلة النصية "Hello World" عند تلقي طلبٍ ما. إذا كانت بيئة Node مثبَّتةً لديك مسبقًا، فيمكنك اتباع الخطوات التالية لتجربة مثالنا:
أولًا، افتح الطرفية، أو افتح الأداة المساعدة لسطر الأوامر على نظام ويندوز.
ثانيًا، أنشئ المجلد الذي تريد حفظ البرنامج فيه مثل المجلد "test-node"، ثم انتقل إليه من خلال إدخال الأمر التالي في الطرفية:
cd test-node
ثالثًا، أنشئ ملفًا بالاسم "hello.js" وضع فيه الشيفرة البرمجية التالية باستخدام محرر النصوص المفضل لديك:
// حمّل وحدة HTTP const http = require("http"); const hostname = "127.0.0.1"; const port = 8000; // أنشئ خادم HTTP const server = http.createServer(function (req, res) { // اضبط ترويسة استجابة HTTP بحالة HTTP ونوع المحتوى res.writeHead(200, { "Content-Type": "text/plain" }); // أرسل متن الاستجابة "Hello World" res.end("Hello World\n"); }); // اطبع سجلًا بمجرد أن يبدأ الخادم بالاستماع server.listen(port, hostname, function () { console.log(`Server running at http://${hostname}:${port}/`); });
رابعًا، احفظ الملف في المجلد الذي أنشأته سابقًا.
خامسًا، ارجع إلى الطرفية واكتب الأمر التالي:
node hello.js
أخيرًا، انتقل إلى العنوان http://localhost:8000
في متصفح الويب، إذ يجب أن ترى النص "Hello World" في الجزء العلوي الأيسر من صفحة ويب أخرى فارغة.
أطر عمل الويب
لا تدعم بيئة Node مهام تطوير الويب الشائعة الأخرى مباشرةً بنفسها، فإذا أدرتَ إضافة معالجة محددة لأفعال HTTP المختلفة، مثل GET
و POST
و DELETE
وغير ذلك، فعالج الطلبات بصورة منفصلة في مسارات URL المختلفة ("الوجهات Routes")، أو خدّم الملفات الثابتة، أو استخدم القوالب Templates لإنشاء الاستجابة ديناميكيًا، إذ لن تكون بيئة Node ذات فائدة كبيرة من تلقاء نفسها. يجب عليك إما كتابة الشيفرة البرمجية بنفسك، أو يمكنك تجنب إعادة اختراع العجلة واستخدام إطار عمل ويب.
مقدمة إلى إطار عمل Express
يُعَد Express إطار عمل الويب الخاص ببيئة Node الأكثر شيوعًا، وهو المكتبة الأساسية لعدد من أطر عمل الويب الخاصة ببيئة Node الشائعة الأخرى، ويوفر آليات بهدف:
- كتابة معالجات للطلبات ذات أفعال HTTP مختلفة في وجهات URL المختلفة.
- التكامل مع محرّكات تقديم "العرض View" لتوليد استجابات من خلال إدخال بيانات في القوالب.
- ضبط إعدادات تطبيقات الويب الشائعة مثل المنفذ المُستخدَم للاتصال وموقع القوالب المستخدمة لتقديم الاستجابة.
- إضافة برمجيات وسيطة middleware لمعالجة الطلبات الإضافية في أيّ وقت ضمن خط معالجة الطلبات.
يُعَد إطار عمل Express بسيط إلى حدٍ ما، ولكن أنشأ المطورون حزمًا وسيطة متوافقة لمعالجة أيّ مشكلة في تطوير الويب تقريبًا، وتوجد مكتبات للعمل مع ملفات تعريف الارتباط والجلسات وتسجيل دخول المستخدمين ومعاملات عنوان URL وبيانات POST
وترويسات الأمان وغير ذلك الكثير. اطلع على قائمة الحزم الوسيطة التي يهتم بها فريق Express مع قائمة ببعض الحزم الخارجية الشائعة.
ملاحظة: تُعَد هذه المرونة سيفًا ذا حدين، إذ توجد حزم وسيطة لمعالجة أيّ مشكلة أو متطلب تقريبًا، ولكن يمكن أن يكون العمل على استخدام الحزم المناسبة تحديًا في بعض الأحيان. كما لا توجد طريقة صحيحة لبناء تطبيق، إذا لا تُعَد العديد من الأمثلة التي تجدها على الإنترنت مثالية، أو يمكن أن تعرض فقط جزءًا صغيرًا مما تحتاج إلى فعله لتطوير تطبيق ويب.
تاريخ بيئة Node وإطار عمل Express
أُصدِرت بيئة Node في البداية لنظام لينكس فقط في عام 2009، وأُصدِر مدير الحزم npm في عام 2010، وأُضيف دعم ويندوز الأصلي في عام 2012.
أُصدِر إطار عمل Express في البداية في الشهر 11 من عام 2010 وهو حاليًا في الإصدار الرئيسي الرابع من واجهة برمجة التطبيقات API، ويمكنك التحقق من سجل التغييرات للحصول على معلومات حول التغييرات في الإصدار الحالي و GitHub للحصول على ملاحظات الإصدارات التاريخية الأكثر تفصيلًا.
ما مدى شعبية Node و Express؟
تُعد شعبية إطار عمل الويب أمرًا مهمًا لأنه مؤشر على استمرار صيانته والموارد التي يُحتمَل أن تكون متاحة من حيث التوثيق والمكتبات الإضافية والدعم الفني.
لا يوجد أيّ مقياس نهائي ومتوفر حاليًا لشعبية أطر العمل من طرف الخادم، بالرغم من أنه يمكنك تقدير الشعبية باستخدام آليات مثل حساب عدد مشاريع غيت هَب GitHub وأسئلة موقع StackOverflow لكل منصة. لذا يُفضَّل أن تسأل ما إذا كان Node و Express يتمتعان بشعبية كافية لتجنب مشاكل المنصات التي لا تحظى بشعبية، وما إذا كانا مستمرين في التطور ويمكنك الحصول على المساعدة إن احتجت إليها، وإذا كان هناك فرصة لك للحصول على عمل مدفوع الأجر إذا تعلمت Express. يمكن القول أن Express إطار عمل شائع الاستخدام استنادًا إلى عدد الشركات البارزة التي تستخدمه وعدد الأشخاص المساهمين في الشيفرة البرمجية الأساسية وعدد الأشخاص الذين يقدمون الدعم المجاني والمدفوع.
هل Express إطار عمل قائم على رأيه Opinionated؟
تشير أطر عمل الويب إلى نفسها في أغلب الأحيان على أنها "قائمة على رأيها Opinionated" أو أنها "غير قائمة على رأيها Unopinionated"، فأطر العمل القائمة على رأيها هي أطر العمل التي لديها آراء حول الطريقة الصحيحة للتعامل مع أيّ مهمة معينة، وتدعم التطور السريع في نطاق محدد أو حل مشاكل من نوع معين لأن الطريقة الصحيحة لفعل أي شيء تكون عادةً مفهومة ومُوثَّقة جيدًا. لكن يمكن أن تكون أقل مرونة في حل المشاكل خارج مجالها الرئيسي، وتميل إلى تقديم خيارات أقل للمكونات والأساليب التي يمكن أن تستخدمها.
يكون لأطر العمل غير القائمة على رأيها قيود أقل بكثير على أفضل طريقة لربط المكونات مع بعضها بعضًا لتحقيق هدفٍ ما أو حتى لتحديد المكونات التي يجب استخدامها، وتسهّل على المطورين استخدام أنسب الأدوات لإكمال مهمة معينة وإن كان ذلك على حساب الجهد التي تحتاجه للعثور على تلك المكونات بنفسك.
يُعَد إطار عمل Express غير قائم على رأيه، إذ يمكنك إدخال أيّ برمجية وسيطة متوافقة تريدها تقريبًا إلى سلسلة معالجة الطلبات وبأيّ ترتيب تريده تقريبًا، ويمكنك بناء التطبيق في ملف واحد أو في ملفات متعددة وباستخدام أي بنية مجلدات، إذ ستشعر أحيانًا أن لديك الكثير من الخيارات.
كيف تبدو شيفرة Express البرمجية؟
ينتظر تطبيق الويب طلبات HTTP من متصفح الويب (أو عميل آخر) في موقع ويب تقليدي مُوجَّه بالبيانات. يعمل التطبيق عند تلقي طلب على تحديد الإجراء المطلوب بناءً على نمط عنوان URL وربما على المعلومات المرتبطة به الواردة في بيانات POST
أو بيانات GET
، ثم يمكنه بعد ذلك -اعتمادًا على ما هو مطلوب- قراءة المعلومات أو كتابتها في قاعدة بيانات أو أداء مهام أخرى مطلوبة لتلبية الطلب، ثم سيعيد التطبيق استجابةً إلى متصفح الويب، وينشئ غالبًا صفحة HTML ديناميكيًا ليعرضها المتصفح من خلال إدخال البيانات المُسترجَعة ضمن عناصر بديلة في قالب HTML.
يوفر Express توابعًا لتحديد الدالة المستدعاة لفعل HTTP معين، مثل GET
و POST
و SET
وغير ذلك ونمط عنوان URL ("الوِجهة Route")، وتوابعًا لتحديد محرّك القالب ("العرض View") المستخدَم، ومكان وجود ملفات القالب، والقالب الذي يجب استخدامه لتقديم الاستجابة. يمكنك استخدام برمجيات Express الوسيطة لإضافة الدعم لملفات تعريف الارتباط والجلسات والمستخدمين والحصول على معاملات POST
/ GET
وغير ذلك، ويمكنك استخدام أيّ آلية قاعدة بيانات تدعمها بيئة Node، إذ لا يحدد إطار عمل Express أيّ سلوك متعلق بقاعدة البيانات.
سنشرح في الأقسام التالية بعض الأشياء التي ستراها عند العمل باستخدام شيفرة Express و Node.
مثال لطباعة مرحبا بالعالم Helloworld باستخدام إطار عمل Express
أولًا، ضع في بالك مثال مرحبًا بالعالم المعياري، إذ سنناقش كل جزء منه فيما يلي وفي الأقسام التالية.
ملاحظة: إذا كان لديك بيئة Node وإطار عمل Express مُثبَّتين مسبقًا (أو إذا ثبّتهما كما هو موضح في المقال التالي)، فيمكنك حفظ الشيفرة البرمجية التالية في ملف نصي بالاسم "app.js" وتشغيله في موجه أوامر باش Bash من خلال استدعاء الأمر: node ./app.js
.
const express = require("express"); const app = express(); const port = 3000; app.get("/", function (req, res) { res.send("Hello World!"); }); app.listen(port, function () { console.log(`Example app listening on port ${port}!`); });
يطلب السطران الأوليان باستخدام require()
الوحدةَ express وينشئان تطبيق Express. يحتوي هذا الكائن- الذي يُطلَق عليه تقليديًا الاسم app
- على توابع لتوجيه طلبات HTTP وضبط البرمجيات الوسيطة وتصيير عروض HTML وتسجيل محرك القوالب وتعديل إعدادات التطبيق التي تتحكم في سلوك التطبيق، مثل وضع البيئة وما إذا كانت تعريفات الوجهة حساسة لحالة الأحرف وغير ذلك.
يعرض الجزء الأوسط من الشيفرة (الأسطر الثلاثة التي تبدأ بالتابع app.get
) تعريف الوجهة، إذ يحدّد التابع app.get()
دالة رد النداء callback المُستدعاة عندما يكون هناك طلب HTTP من النوع GET
له المسار (/
) المتعلق بجذر الموقع. تأخذ دالة رد النداء طلبًا وكائن استجابة بوصفهما وسيطين، وتستدعي التابع send()
للاستجابة لإعادة السلسلة النصية "Hello World!".
تشغّل الكتلة النهائية الخادم على منفذ محدد ("3000") وتطبع تعليق سجل في الطرفية، ويمكنك مع تشغيل الخادم الانتقال إلى localhost:3000
في متصفحك لترى مثال الاستجابة المُعادة.
استيراد وإنشاء الوحدات
الوحدة Module هي مكتبة أو ملف جافا سكريبت يمكنك استيراده في شيفرة برمجية أخرى باستخدام دالة require()
الخاصة ببيئة Node، إذ يُعَد إطار عمل Express بحد ذاته وحدةً مثل مكتبات البرمجيات الوسيطة وقواعد البيانات التي نستخدمها في تطبيقات Express.
توضح الشيفرة البرمجية التالية كيفية استيراد وحدة باسمها باستخدام إطار عمل Express، إذ نستدعي أولًا الدالة require()
من خلال تحديد اسم الوحدة بوصفها سلسلة نصية ('express'
)، ثم نستدعي الكائن المُعاد لإنشاء تطبيق Express، ويمكننا بعد ذلك الوصول إلى خاصيات ودوال كائن التطبيق.
const express = require("express"); const app = express();
يمكنك أيضًا إنشاء وحداتك الخاصة التي يمكن استيرادها بالطريقة نفسها.
ملاحظة: من فوائد إنشاء وحداتك الخاصة أنها تسمح لك بتنظيم شيفرتك البرمجية إلى أجزاء يمكن إدارتها، إذ يصعب فهم وصيانة تطبيق مؤلف من ملف واحد، ويساعدك استخدام الوحدات في إدارة فضاء أسمائك، إذ تُستورَد المتغيرات التي تصدّرها بصورة صريحة فقط عند استخدام إحدى الوحدات.
يمكن جعل الكائنات متاحةً خارج الوحدة من خلال جعلها خاصيات إضافية في الكائن exports
، فمثلًا تُعَد الوحدة square.js التالية ملفًا يصدّر التوابع area()
و perimeter()
:
exports.area = function (width) { return width * width; }; exports.perimeter = function (width) { return 4 * width; };
يمكننا استيراد هذه الوحدة باستخدام الدالة require()
، ثم استدعاء التابع (أو التوابع) المُصدَّرة كما يلي:
const square = require("./square"); // نطلب هنا باستخدام الدالة require() اسم الملف بدون لاحقة الملف .js (الاختيارية) console.log(`The area of a square with a width of 4 is ${square.area(4)}`);
ملاحظة: يمكنك أيضًا تحديد مسار مطلق، (أو اسم ما كما فعلنا في البداية) للوحدة.
إذا أردتَ تصدير كائن كامل في مهمة واحدة بدلًا من بناء خاصيةً واحدة في كل مرة، فأسنده إلى module.exports
كما يلي، ويمكنك إجراء ذلك لجعل جذر كائن exports بانيًا Constructor أو دالة أخرى:
module.exports = { area(width) { return width * width; }, perimeter(width) { return 4 * width; }, };
ملاحظة: يمكنك عَدّ exports
بوصفه اختصارًا للمصطلح module.exports
ضمن وحدة معينة، ولكن يُعَد exports
مجرد متغير مهيَّأ بقيمة module.exports
قبل تقييم الوحدة، وهذه القيمة هي مرجع إلى كائن (كائن فارغ في هذه الحالة)، وهذا يعني أن exports
يحتوي على مرجع للكائن نفسه الذي يشير إليه module.exports
، مما يعني أيضًا أن exports
لم يعد مرتبطًا بالكائن module.exports
من خلال إسناد قيمة أخرى إلى exports
.
اطلع على مقال تعرف على وحدات Node.js الأساسية وإنشاء وحدات برمجية Modules في Node.js والوحدات Modules في توثيق Node لمزيد من المعلومات.
استخدام واجهات برمجة التطبيقات غير المتزامنة
تستخدم شيفرة جافا سكريبت في أغلب الأحيان واجهات برمجة تطبيقات غير متزامنة بدلًا من واجهات برمجة تطبيقات متزامنة للعمليات التي يمكن أن تستغرق بعض الوقت لتكتمل، حيث تُعَد واجهة برمجة التطبيقات المتزامنة واجهة يجب أن تكتمل فيها العملية قبل أن تبدأ العملية التي تليها مثل دوال log المتزامنة التالية التي ستطبع النص إلى الطرفية بالترتيب (First, Second):
console.log("First"); console.log("Second");
بينما تبدأ واجهة برمجة التطبيقات غير المتزامنة عمليةً وتُعاد مباشرةً قبل اكتمال العملية، وستستخدم واجهة برمجة التطبيقات API بعض الآليات لإجراء عمليات إضافية بمجرد انتهاء العملية، فمثلًا ستطبع الشيفرة التالية "Second, First" لأنه العملية لا تكتمل إلا بعد عدة ثوانٍ بالرغم من استدعاء التابع setTimeout()
أولًا والإعادة مباشرةً.
setTimeout(function () { console.log("First"); }, 3000); console.log("Second");
يُعَد استخدام واجهات برمجة التطبيقات غير المتزامنة وغير المُعطِّلة في Node أكثر أهمية من استخدامها في المتصفح لأن بيئة Node هي بيئة تنفيذ ذات خيط برمجي Thread واحد وتقاد بالأحداث، فاعتمادها على خيط واحد يعني تشغيل جميع الطلبات الواردة إلى الخادم على خيط واحد (بدلًا من توليدها في عمليات منفصلة). يُعَد هذا النموذج فعالًا جدًا من حيث السرعة وموارد الخادم، ولكنه يعني أنه إذا استدعَت أيٌّ من دوالك توابعًا متزامنة تستغرق وقتًا طويلًا حتى تكتمل، فلن تعطِّل الطلب الحالي فحسب، بل ستعطِّل كل طلب آخر يعالجه تطبيقك.
هناك عدد من الطرق لتُعلِم واجهةُ برمجة التطبيقات غير المتزامنة تطبيقَك بأنه اكتمل، ولكن الطريقة الأكثر شيوعًا هي تسجيل دالة رد النداء عند استدعاء واجهة برمجة التطبيقات غير المتزامنة، والتي ستُستدعَى مرة أخرى عند اكتمال العملية، وهذا هو الأسلوب الذي استخدمناه مسبقًا.
ملاحظة: يمكن أن يكون استخدام دوال رد النداء "فوضويًا" إذا كان لديك سلسلة من العمليات غير المتزامنة الاعتمادية التي يجب إجراؤها بالترتيب لأن ذلك ينتج عنه مستويات متعددة من دوال رد النداء المتداخلة. تُعرف هذه المشكلة عمومًا باسم "جحيم دوال رد النداء Callback Hell"، ويمكن تقليل هذه المشكلة من خلال ممارسات كتابة الشيفرة البرمجية الجيدة باستخدام وحدةٍ مثل الوحدة async، أو إعادة إنتاج الشيفرة البرمجية إلى ميزات جافا سكريبت أصيلة مثل الوعود Promises واللاتزامن والانتظار async/await، حيث توفّر بيئة Node الدالة utils.promisify
لتحويل دالة رد النداء إلى وعدٍ بطريقة مريحة.
ملاحظة: من الأمور الشائعة لكلٍّ من Node و Express استخدام دوال رد النداء ذات قيمة الخطأ أولًا، حيث تكون القيمة الأولى في دوال رد النداء قيمة خطأ، بينما تحتوي الوسائط اللاحقة على بيانات نجاح.
إنشاء معالجات الوجهة Route Handlers
عرّفنا في مثال مرحبًا بالعالم باستخدام إطار عمل Express السابق دالة معالجة الوجهة (دالة رد لنداء) لطلبات HTTP من النوع GET
إلى جذر الموقع ('/').
app.get("/", function (req, res) { res.send("Hello World!"); });
تأخذ دالة رد النداء كائني طلب واستجابة بوصفهما وسطاء، وتستدعي التابع send()
في هذه الحالة للاستجابة لإعادة السلسلة النصية "Hello World!". هناك عدد من توابع الاستجابة الأخرى لإنهاء دورة الطلب/الاستجابة، فمثلًا يمكنك استدعاء التابع res.json()
لإرسال استجابة بتنسيق JSON أو التابع res.sendFile()
لإرسال ملف.
ملاحظة: يمكنك استخدام أيّ أسماء وسطاء تريدها في دوال رد النداء، إذ سيكون المتغير الأول هو الطلب وسيظل الوسيط الثاني هو الاستجابة دائمًا عند استدعاء دالة رد النداء، ولكن من المنطقي تسميتها بحيث يمكنك تحديد الكائن الذي تعمل به في متن دالة رد النداء.
يوفّر كائن تطبيق Express توابعًا لتعريف معالجات الوجهة لجميع أفعال HTTP الأخرى، والتي تُستخدَم غالبًا بالطريقة نفسها تمامًا وهي:
checkout(), copy(), delete(), get(), head(), lock(), merge(), mkactivity(), mkcol(), move(), m-search(), notify(), options(), patch(), post(), purge(), put(), report(), search(), subscribe(), trace(), unlock(), unsubscribe()
يوجد تابع توجيه خاص هو app.all()
يُستدعَى في استجابة أيّ تابع HTTP، ويُستخدَم هذا التابع لتحميل دوال وسيطة في مسار معين لجميع توابع الطلب. يوضّح المثال التالي (من توثيق Express) معالجًا يُنفَّذ لطلبات القسم /secret
بغض النظر عن فعل HTTP المُستخدَم (بشرط أن تدعمه وحدة http).
app.all("/secret", function (req, res, next) { console.log("Accessing the secret section…"); next(); // تمرير التحكم إلى المعالج التالي });
تسمح الوجهات Routes بمطابقة أنماط معينة من المحارف في عنوان URL واستخراج بعض القيم من عنوان URL وتمريرها بوصفها معاملات إلى معالج الوجهة، مثل سمات كائن الطلب المُمرَّر بوصفه معاملًا.
من المفيد تجميع معالجات الوجهة لجزء معين من الموقع مع بعضها بعضًا والوصول إليها باستخدام بادئة وجهة مشتركة، فمثلًا يحتوي موقعٌ له خاصية ويكي Wiki على جميع الوجهات المتعلقة بها في ملف واحد ويمكن الوصول إليها باستخدام بادئة وجهة هي "/wiki/"، ويمكن تحقيق ذلك في إطار عمل Express باستخدام كائن express.Router
. يمكننا مثلًا إنشاء وجهة wiki في وحدة بالاسم wiki.js ثم تصدير كائن Router
كما يلي:
// wiki.js - وحدة وجهة Wiki const express = require("express"); const router = express.Router(); // وجهة الصفحة الرئيسية router.get("/", function (req, res) { res.send("Wiki home page"); }); // وجهة الصفحة About router.get("/about", function (req, res) { res.send("About this wiki"); }); module.exports = router;
ملاحظة: تشبه إضافة الوجهات إلى الكائن Router
إضافةَ الوجهات إلى الكائن app
كما هو موضح سابقًا.
سنطلب باستخدام الدالة require()
وحدة الوجهة (wiki.js) لاستخدام الموجّه في ملف تطبيقنا الرئيسي، ثم نستدعي التابع use()
في تطبيق Express لإضافة الكائن Router إلى مسار المعالجة الوسيطة، ويمكن بعد ذلك الوصول إلى الوجهتين من /wiki/
و /wiki/about/
.
const wiki = require("./wiki.js"); // … app.use("/wiki", wiki);
اطلع على مقال الموجّهات والمتحكمات لمزيد من التفاصيل حول العمل مع الوجهات وخاصة استخدام الكائن Router
.
استخدام البرمجيات الوسيطة
تُستخدَم البرمجيات الوسيطة على نطاق واسع في تطبيقات Express -للمهام بدءًا تخديم الملفات الثابتة إلى معالجة الأخطاء- لضغط استجابات HTTP. تنهي دوال الوجهات دورة طلب واستجابة HTTP من خلال إعادة الاستجابة لعميل HTTP، ولكن تجري الدوال الوسيطة بعض العمليات على الطلب أو الاستجابة ثم تستدعي الدالة التالية في المكدس stack، والتي يمكن أن تكون برمجية وسيطة أو معالج وجهة، ويعود ترتيب استدعاء البرمجيات الوسيطة إلى مطور التطبيق.
ملاحظة: يمكن للبرمجيات الوسيطة إجراء أيّ عملية وتنفيذ أيّ شيفرة برمجية وإجراء تغييرات على كائن الطلب والاستجابة، ويمكنها إنهاء دورة الطلب والاستجابة. إذا لم تنتهِ الدورة، فيجب استدعاء التابع next()
لتمرير التحكم إلى الدالة الوسيطة التالية، أو سيُترَك الطلب مُعلَّقًا.
تستخدم معظم التطبيقات برمجيات وسيطة خارجية لتبسيط مهام تطوير الويب الشائعة، مثل العمل مع ملفات تعريف الارتباط cookies والجلسات واستيثاق المستخدمين والوصول إلى طلب POST
وبيانات جسون JSON والتسجيل وغير ذلك. يمكنك العثور على قائمة بحزم البرمجيات الوسيطة التي يهتم بها فريق Express، والتي تتضمن أيضًا الحزم الخارجية الشائعة الأخرى، وتتوفر حزم Express الأخرى في مدير الحزم npm.
يجب تثبيت برمجية وسيطة خارجية في تطبيقك باستخدام مدير الحزم npm لاستخدامها، فمثلًا يمكن تثبيت البرمجية الوسيطة morgan لتسجيل طلبات HTTP باستخدام الأمر التالي:
npm install morgan
يمكنك بعد ذلك استدعاء التابع use()
في كائن تطبيق Express لإضافة البرمجيات الوسيطة إلى المكدس:
const express = require("express"); const logger = require("morgan"); const app = express(); app.use(logger("dev")); // …
ملاحظة: تُستدعَى دوال التوجيه والبرمجيات الوسيطة بالترتيب نفسه للتصريح عنها، إذ يكون الترتيب مهمًا بالنسبة لبعض البرمجيات الوسيطة، فمثلًا إذا اعتمدت البرمجيات الوسيطة للجلسة على البرمجيات الوسيطة لملفات تعريف الارتباط، فيجب إضافة معالج ملفات تعريف الارتباط أولًا. تُستدعَى البرمجيات الوسيطة قبل إعداد الوجهات غالبًا، أو لن تتمكن معالجات الوجهات من الوصول إلى الوظائف التي تضيفها برمجيتك الوسيطة.
يمكنك كتابة دوالك الوسيطة، إذ يُحتمَل أن تضطر إلى ذلك إذا أردتَ إنشاء شيفرة معالجة الأخطاء فقط. يتمثل الاختلاف الوحيد بين دالة البرمجية الوسيطة ودالة رد استدعاء معالج الوجهة في أن دوال البرمجيات الوسيطة لها وسيط ثالث هو next
الذي يُتوقَّع أن تستدعيه دوال البرمجيات الوسيطة إن لم تكن الدالة التي تكمل دورة الطلب، إذ تحتوي الدالة الوسيطة على الدالة next
التي يجب استدعاؤها عند استدعاء هذه الدالة الوسيطة.
يمكنك إضافة دالة وسيطة إلى سلسلة المعالجة لجميع الاستجابات باستخدام التابع app.use()
، أو لفعل HTTP محدَّد باستخدام التابع المرتبط به مثل: app.get()
و app.post()
وغير ذلك، وتُحدَّد الوجهة بالطريقة نفسها لكلتا الحالتين، بالرغم من أن الوجهة اختيارية عند استدعاء التابع app.use()
.
يوضح المثال التالي كيفية إضافة الدالة الوسيطة باستخدام كلا الأسلوبين مع أو بدون وجهة:
const express = require("express"); const app = express(); // مثال على دالة وسيطة const a_middleware_function = function (req, res, next) { // إجراء بعض العمليات next(); // استدعاء next() ليستدعي Express الدالة الوسيطة التالية في السلسلة }; // الدالة المُضافة باستخدام التابع use() لجميع الوجهات والأفعال app.use(a_middleware_function); // الدالة المُضافة باستخدام التابع use() لوجهة محددة app.use("/someroute", a_middleware_function); // الدالة الوسيطة المُضافة لفعل HTTP ووجهة محددة app.get("/", a_middleware_function); app.listen(3000);
ملاحظة: صرّحنا عن دالة وسيطة بصورة منفصلة ثم ضبطناها بوصفها دالة رد نداء، وصرّحنا عن دالة رد النداء عند استخدامها في دالة معالج الوجهة السابقة، ويُعَد هذان الأسلوبان صالحين في جافا سكريبت.
تخديم الملفات الثابتة
يمكنك استخدام البرمجية الوسيطة express.static لتخديم الملفات الثابتة بما في ذلك الصور وملفات CSS وجافا سكريبت، وتُعَد static()
الدالة الوسيطة الوحيدة التي هي جزء من إطار عمل Express. يمكنك مثلًا استخدام السطر التالي لتخديم الصور وملفات CSS وجافا سكريبت من مجلد بالاسم public في المستوى نفسه الذي تستدعيه فيه Node:
app.use(express.static("public"));
تُخدَّم الملفات في المجلد public من خلال إضافة اسم هذه الملفات (بالنسبة إلى المجلد "public" الأساسي) إلى عنوان URL الأساسي كما في الأمثلة التالية:
http://localhost:3000/images/dog.jpg http://localhost:3000/css/style.css http://localhost:3000/js/app.js http://localhost:3000/about.html
يمكنك استدعاء الدالة static()
عدة مرات لتخديم مجلدات متعددة، بحيث إذا لم تعثر دالة وسيطة على ملفٍ ما، فسيُمرَّر إلى البرمجية الوسيطة التالية، إذ يعتمد ترتيب استدعاء البرمجيات الوسيطة على ترتيب تصريحها.
app.use(express.static("public")); app.use(express.static("media"));
يمكنك أيضًا إنشاء بادئة افتراضية لعناوين URL الثابتة الخاصة بك بدلًا من إضافة الملفات إلى عنوان URL الأساسي، فمثلًا نحدد فيما يلي مسار ربط Mount Path بحيث تُحمَّل الملفات باستخدام البادئة "/media":
app.use("/media", express.static("public"));
يمكنك الآن تحميل الملفات الموجودة في المجلد public من بادئة المسار /media
:
http://localhost:3000/media/images/dog.jpg http://localhost:3000/media/video/cat.mp4 http://localhost:3000/media/cry.mp3
معالجة الأخطاء
تُعالَج الأخطاء باستخدام دالة وسيطة خاصة واحدة أو أكثر لها أربعة وسائط بدلًا من الوسائط الثلاثة المعتادة: (err, req, res, next)
كما يلي:
app.use(function (err, req, res, next) { console.error(err.stack); res.status(500).send("Something broke!"); });
تعيد الشيفرة السابقة أيّ محتوًى مطلوب، ولكن يجب استدعاؤها بعد جميع توابع app.use()
واستدعاءات الوجهات الأخرى، بحيث تكون آخر برمجية وسيطة في عملية معالجة الطلب.
يحتوي إطار عمل Express على معالج أخطاء مبني مسبقًا، والذي يهتم بأيّ أخطاء متبقية يمكن أن تواجهها في التطبيق، وتُضاف دالة معالجة الأخطاء الافتراضية هذه في نهاية مكدس الدوال الوسيطة. إذا مرّرتَ خطأً إلى الدالة next()
ولم تعالجه في معالج الأخطاء، فسيعالجه معالج الأخطاء المبني مسبقًا، إذ سيُكتَب الخطأ إلى العميل باستخدام متعقّب المكدس stack trace.
لا يُضمَّن متعقّب المكدس في بيئة الإنتاج، لذا يجب ضبط متغير البيئة NODE_ENV على القيمة 'production'
لتشغيله في وضع الإنتاج.
ملاحظة: لا يجري التعامل مع HTTP404 ورموز حالة الخطأ الأخرى بوصفها أخطاء، فإذا أردتَ معالجتها، فيمكنك إضافة دالة وسيطة لذلك.
استخدام قواعد البيانات
يمكن لتطبيقات Express استخدام أيّ آلية قاعدة بيانات تدعمها بيئة Node، ولا يعرّف Express أيّ سلوك أو متطلبات إضافية محددة لإدارة قاعدة البيانات، إذ هناك العديد من الخيارات لاستخدامها مثل PostgreSQL و MySQL و Redis و SQLite و MongoDB وغير ذلك.
يجب أولًا تثبيت مشغّل قاعدة البيانات باستخدام مدير الحزم npm، فمثلًا يمكنك استخدام الأمر التالي لتثبيت مشغّل قاعدة بيانات NoSQL MongoDB:
npm install mongodb
يمكنك تثبيت قاعدة البيانات محليًا أو على خادم سحابي، إذ ستطلب المشغّل في شيفرة Express، ثم تتصل بقاعدة البيانات، ثم تجري عمليات الإنشاء والقراءة والتحديث والحذف -أو اختصارًا CRUD. يوضح المثال التالي (من توثيق Express) كيفية العثور على سجلات "mammal" باستخدام قاعدة بيانات MongoDB، وتعمل الشيفرة التالية مع الإصدارات الأقدم من إصدار mongodb الذي هو 2.2.33:
const MongoClient = require("mongodb").MongoClient; MongoClient.connect("mongodb://localhost:27017/animals", (err, db) => { if (err) throw err; db.collection("mammals") .find() .toArray((err, result) => { if (err) throw err; console.log(result); }); });
يمكنك استخدام الشيفرة التالية بالنسبة للإصدار رقم 3.0 من mongodb والإصدارات الأحدث:
const MongoClient = require("mongodb").MongoClient; MongoClient.connect("mongodb://localhost:27017/animals", (err, client) => { if (err) throw err; const db = client.db("animals"); db.collection("mammals") .find() .toArray((err, result) => { if (err) throw err; console.log(result); client.close(); }); });
هناك طريقة شائعة أخرى وهي الوصول إلى قاعدة بياناتك بطريقة غير مباشرة باستخدام رابط الكائنات العلائقي Object Relational Mapper -أو ORM اختصارًا، إذ تعرّف في هذه الطريقة بياناتك بوصفها كائنات أو نماذج ويربط رابط ORM هذه البيانات بتنسيق قاعدة البيانات الأساسية. تتمتع هذه الطريقة بفائدة أنه يمكنك -بصفتك مطورًا- الاستمرار في التفكير وفق مصطلحات كائنات جافا سكريبت بدلًا من التفكير وفق دلالات قاعدة البيانات، وأن هناك مكانًا واضحًا لإجراء التحقق من صحة البيانات الواردة وفحصها (سنتحدث أكثر عن قواعد البيانات في مقال لاحق (استخدام قاعدة البيانات).
تصيير البيانات- العروض Views
تسمح محركات القوالب (يُشار إليها أيضًا باسم "محركات العروض" في توثيق Express) بتحديد بنية مستندات الخرج في قالبٍ ما باستخدام العناصر البديلة للبيانات التي ستُملَأ عند توليد الصفحة، إذ تُستخدَم القوالب لإنشاء صفحات HTML، ولكن يمكنها أيضًا إنشاء أنواع أخرى من المستندات، ويدعم Express عددًا من محركات القوالب.
تضبط في شيفرة إعدادات تطبيقك محركَ القوالب لاستخدامه وتضبط الموقع الذي يجب أن يبحث فيه Express عن القوالب باستخدام إعدادات 'views'
و 'view engine'
كما يلي، ويجب أيضًا تثبيت الحزمة التي تحتوي على مكتبة قوالبك:
const express = require("express"); const path = require("path"); const app = express(); // اضبط المجلد ليتضمن القالب ('views') app.set("views", path.join(__dirname, "views")); // اضبط محرك القوالب للاستخدام، ويكون في هذه الحالة 'some_template_engine_name' app.set("view engine", "some_template_engine_name");
يعتمد مظهر القالب على المحرك الذي تستخدمه. بافتراض أن لديك ملف قالب بالاسم "index.Response.render()
في دالة معالج الوجهة لإنشاء استجابة HTML وإرسالها كما يلي:
app.get("/", function (req, res) { res.render("index", { title: "About dogs", message: "Dogs rock!" }); });
بنية الملفات
لا يضع إطار عمل Express أيّ افتراضات للبنية أو المكونات التي تستخدمها، إذ يمكن أن توضَع الوجهات والعروض والملفات الثابتة والشيفرة الأخرى الخاصة بالتطبيق في أيّ عدد من الملفات وبأيّ بنية مجلدات. يمكن أن يكون كامل تطبيق Express في ملف واحد، ولكن من المنطقي تقسيم تطبيقك إلى ملفات بناءً على الوظيفة، مثل إدارة الحسابات والمدونات ولوحات المناقشة، ونطاق المشكلات المعمارية، مثل النموذج، أو العرض، أو المتحكم إذا استخدمتَ معمارية MVC وهي اختصار لثلاث كلمات هي:
- النموذج Model الذي يعني بيانات التطبيق فهو يتفاعل مباشرة مع قاعدة البيانات الخاصة بك ويسترد المعلومات منها.
- العرض View الذي يعني واجهة التطبيق فهو يعرض الصفحات التي يتفاعل معها المستخدم مباشرة.
- المتحكم Controller وهو صلة الوصل بين العرض والنموذج فهو يستقبل طلبات المستخدمين ويسترد البيانات المطلوبة من النموذج ويعالجها ويرسلها إلى صفحات العرض.
سنستخدم في مقال لاحق مولّد تطبيقات Express أو Express Application Generator الذي يُنشِئ تطبيقًا هيكليًا معياريًا يمكننا توسيعه بسهولة لإنشاء تطبيقات الويب.
الخلاصة
أكملنا الخطوة الأولى في رحلة تعلم Express/Node، إذ يجب أن تفهم الآن فوائد Express و Node الرئيسية، وما تبدو عليه الأجزاء الرئيسية لتطبيق Express (الوجهات والبرمجيات الوسيطة ومعالجة الأخطاء وشيفرة القوالب)، ويجب أن تفهم أيضًا أن الطريقة التي تجمع بها هذه الأجزاء مع بعضها بعضًا والمكتبات التي تستخدمها أمرٌ متروكٌ لك بسبب كون Express إطار عمل غير قائم على رأيه.
يُعَد Express إطار عمل تطبيق ويب خفيف الوزن، إذ تأتي الكثير من فوائده وإمكاناته من مكتبات وميزات خارجية، إذ سنلقي نظرةً عليها بمزيد من التفصيل في المقالات التالية، وسنتعرّف في المقال التالي على إعداد بيئة تطوير Node، بحيث يمكنك البدء في رؤية بعض شيفرات Express.
ترجمة -وبتصرُّف- للمقالين Express web framework (Node.js/JavaScript) و Express/Node introduction.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.