<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: nodejs</title><link>https://academy.hsoub.com/programming/javascript/nodejs/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: nodejs</description><language>ar</language><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x648;&#x627;&#x62C;&#x647;&#x629; &#x628;&#x631;&#x645;&#x62C;&#x629; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; REST API &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-rest-api-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-nodejs-r2548/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_04/002RESTAPINode.js.png.b69ca2070e214e410a18d312f7117276.png" /></p>
<p>
	تُستخدم واجهات برمجة التطبيقات APIs على نطاق واسع في شتى المجالات، إذ تتيح للبرمجيات إمكانية التواصل والتكامل مع أنظمة وبرمجيات أخرى، سواء كانت داخلية ضمن نفس النظام أو خارجية. وتكمن إحدى أبرز فوائدها في تسهيل إعادة استخدام المكونات البرمجية، وتوفّر معظم الخدمات الإلكترونية عبر الإنترنت واجهات برمجة تطبيقات تمكّن المطورين من دمج ميزات متنوعة، مثل تسجيل الدخول واستخدام حسابات التواصل الاجتماعي، والدفع بواسطة بطاقات الائتمان، وتتبع سلوك المستخدمين بكل سهولة. ويُعد معيار نقل الحالة التمثيلية Representational State Transfer أو  REST اختصارًا المعيار الأكثر شيوعًا في تصميم هذه الواجهات.
</p>

<p>
	سنشرح في هذا المقال طريقة إنشاء REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> خاصة ببيئة Node.js فلغة <a href="https://academy.hsoub.com/javascript/" rel="">جافا سكريبت JavaScript</a> من أكثر لغات البرمجة شيوعًا بين المطورين المحترفين. لذلك سنركز في هذا المقال على إنشاء REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> بسيطة وآمنة، ويمكن بالطبع استخدام أي من المنصات واللغات البرمجية الأخرى لتحقيقها، مثل إطار عمل <a href="https://academy.hsoub.com/programming/c-sharp/dotnet/aspnet/core/" rel="">ASP.NET Core</a>، أو إطار <a href="https://academy.hsoub.com/programming/php/laravel/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%B4%D9%87%D9%8A%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%8A%D9%84-laravel-r2093/" rel="">لارافيل Laravel</a> للغة PHP، أو إطار <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D9%85%D8%B5%D8%BA%D8%B1-bottle-%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-python-%D9%84%D9%84%D9%88%D9%8A%D8%A8-r93/" rel="">Bottle</a> للغة Python لإنشاء واجهة REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<h2>
	متطلبات العمل
</h2>

<p>
	يحتاج فهم وتطبيق هذا المقال لامتلاك الأمور التالية:
</p>

<ul>
	<li>
		خبرة مسبقة في التعامل مع إطار Node.js
	</li>
	<li>
		معرفة بإطار Express.js لأنه معيار أساسي في بناء الواجهة الخلفية للواجهة البرمجية REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
	</li>
	<li>
		معرفة بمكتبة Mongoose لربط الواجهة الخلفية بقاعدة بيانات MongoDB
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: لن نتناول في هذا المقال شرح أكواد الواجهة الأمامية، وسنركز على الواجهة الخلفية فقط. ولكن بما أننا سنستخدم لغة JavaScript في تطوير الواجهة الخلفية، فإن هذا يتيح لنا إمكانية مشاركة بعض الأجزاء البرمجية مثل نماذج الكائنات Object Models بين الواجهة الأمامية والخلفية، مما يسهم في توحيد الكود وتقليل التكرار داخل المشروع.
</p>

<h2 id="restapi">
	هيكلية REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</h2>

<p>
	تستخدم واجهات REST APIs من أجل الوصول للبيانات والتحكم بها، وذلك باستخدام مجموعة من العمليات عديمة الحالة stateless بمعنى أن الخادم ينفذ العملية المطلوبة منه ثم ينسى كل شيء عنها بعد الانتهاء منها. وهذه العمليات جزء لا يتجزأ من <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">بروتوكول HTTP</a> المستخدم في نقل البيانات على الويب، وهي تستخدم في تنفيذ الوظائف الأساسية كإنشاء البيانات وقراءتها وتحديثها وحذفها أو ما يعرف باسم عمليات CRUD، على الرغم من عدم تطابقها بدقة تامة معها. وهذه العمليات هي كالتالي:
</p>

<ul>
	<li>
		<code>POST</code> لإنشاء مورد جديد أو توفير البيانات بشكل عام
	</li>
	<li>
		<code>GET</code> للحصول على قائمة موارد أو مورد محدد
	</li>
	<li>
		<code>PUT</code> لإنشاء أو استبدال مورد
	</li>
	<li>
		<code>PATCH</code> لتحديث أو تعديل مورد
	</li>
	<li>
		<code>DELETE</code> لحذف المورد
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: ما نعنيه بالمورد resource هنا البيانات التي نتعامل معها عبر واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> ويمكننا الوصول إليها عبر عنوان URL لنقطة الوصول endpoint.
</p>

<p>
	يتيح لنا بناء واجهة REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> باستخدام Node.js إنشاء نقاط وصول Endpoints لكل عملية من عمليات التحكم بالبيانات، وذلك من خلال استخدام بروتوكول HTTP مع تحديد اسم المورد ضمن عنوان URL. عند اتباع هذا الأسلوب، سنحصل على هيكل واضح ومستقر للواجهة البرمجية يسهل فهمه وتطويره وصيانته بمرونة وسرعة. كما أن هذا النمط يُعد شائعًا بين معظم الخدمات الخارجية التي تعتمد بدورها على REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، مما يُسهّل عملية التكامل معها لاحقًا، سواء لإضافة ميزات مثل تسجيل الدخول أو الدفع الإلكتروني.
</p>

<p>
	والآن، دعونا نبدأ بشرح خطوات بناء REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> آمنة باستخدام Node.js خطوة بخطوة.
</p>

<h2>
	بناء REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> آمنة لإدارة المستخدمين باستخدام Node.js
</h2>

<p>
	سنبني في هذا المقال REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> آمنة تتميز بكونها شائعة وعملية لإدارة المستخدمين <code>users</code>. سيملك المورد الذي سننشئه هيكلا بسيطا كالآتي:
</p>

<ul>
	<li>
		<code>id</code>: معرف مستخدم مميز UUID منشأ تلقائيًا
	</li>
	<li>
		<span><span>:</span></span><code>firstName</code><span>: </span>الاسم الأول للمستخدم
	</li>
	<li>
		<code>lastName</code>: الاسم الأخير للمستخدم
	</li>
	<li>
		<code>email</code>: البريد الإلكتروني للمستخدم
	</li>
	<li>
		<code>password</code>: كلمة المرور
	</li>
	<li>
		<code>permissionLevel</code>: أذونات المستخدم كأن يكون مستخدم عادي أو مشرف أو لديه أذونات خاصة
	</li>
</ul>

<p>
	ثم سننشئ عمليات لهذا المورد كالآتي:
</p>

<ul>
	<li>
		<code>POST</code> عند نقطة الوصول <code>‎/users</code> لإنشاء مستخدم جديد
	</li>
	<li>
		<code>GET</code> عند نقطة الوصول <code>‎/users</code> للحصول على قائمة بكافة المستخدمين
	</li>
	<li>
		<code>GET</code> عند نقطة الوصول <code>‎/users/:userId</code> للحصول على بيانات مستخدم معين
	</li>
	<li>
		<code>PATCH</code> عند نقطة الوصول <code>‎/users/:userId</code> لتحديث بيانات مستخدم معين
	</li>
	<li>
		<code>DELETE</code> عند نقطة الوصول <code>‎/users/:userId</code> لحذف مستخدم معين
	</li>
</ul>

<p>
	سنستخدم رموز JSON Web Tokens -أو JWTs اختصارًا- كوسلية للتحقق من هوية المستخدمين. عند تسجيل الدخول، سيُطلب من المستخدم إدخال بريده الإلكتروني وكلمة مروره، وسيتم إرسال هذه البيانات إلى مورد خاص بالمصادقة يحمل الاسم <code>auth</code>، بعد التحقق من صحة البيانات، يصدر هذا المورد رمز JWT ويُستخدم لاحقًا للوصول إلى بعض العمليات المحمية داخل الواجهة البرمجية. لمعرفة المزيد عن هذا النوع من الرموز، يمكن الرجوع إلى مقال <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%8A%D8%AB%D8%A7%D9%82-%D8%B9%D8%A8%D8%B1-%D9%85%D9%81%D8%AA%D8%A7%D8%AD-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D9%85%D8%B4%D9%81%D8%B1-token-authentication-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-nodejs-%D9%88-react-r1135/" rel="">الاستيثاق عبر مفتاح المستخدم المشفر token authentication في تطبيق Node.js و React</a>
</p>

<h2 id="restapinodejs-1">
	إعداد بيئة العمل
</h2>

<p>
	بداية، علينا التأكد من تثبيت آخر إصدار من Node.js على جهازنا، يستخدم في هذا المقال الإصدار 14.9.0 لكن يمكنك استخدام إصدار أحدث إذا كان متوفرًا. بعد ذلك يجب التأكد من أن MongoDB مثبت أيضَا على جهازنا، لن نخوض في تفاصيل استخدام Mongoose أو هيكلية MongoDB، لكننا سنعتمد على تشغيل الأساسيات فقط.
</p>

<p>
	لتبسيط العمل، يمكن تشغيل خادم <a href="https://academy.hsoub.com/devops/servers/databases/mongodb/" rel="">MongoDB</a> في الوضع التفاعلي، أي باستخدام سطر الأوامر مباشرة بدلًا من تشغيله كخدمة تعمل في الخلفية بكتابة الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_615_10" style=""><span class="pln">mongo</span></pre>

<p>
	سنحتاج لاحقًا في هذا المقال إلى التفاعل المباشر مع MongoDB من خلال هذا الوضع -دون استخدام كود Node.js- لفهم كيفية التعامل مع البيانات.
</p>

<p>
	<strong>ملاحظة</strong>: عند استخدام MongoDB، لا نحتاج لإنشاء قاعدة بيانات محددة كما هو الحال في بعض أنظمة إدارة قواعد البيانات العلاقية RDBMS فسيؤدي أول استدعاء لها من كود Node.js إلى إنشائها تلقائيًا إذا لم تكن موجودة، مما يسهل العمل ويسرع عملية التطوير.
</p>

<p>
	لا يحتوي هذا المقال على جميع الأكواد اللازمة لاكتمال عمل المشروع، ويمكنك الحصول على الكود كاملًا من <a href="https://github.com/makinhs/rest-api-tutorial" rel="external nofollow">هذا المستودع</a> ومتابعة النقاط المهمة أثناء القراءة، كما يمكن نسخ ملفات ومقاطع محددة من المستودع. بعد ذلك سننتقل إلى ملف <code>rest-api-tutorial/</code> الناتج في الطرفية، نلاحظ أن المشروع يحتوي على ثلاثة مجلدات <a href="https://www.google.com/url?q=https://academy.hsoub.com/programming/javascript/%25D9%2585%25D9%2582%25D8%25AF%25D9%2585%25D8%25A9-%25D8%25A5%25D9%2584%25D9%2589-%25D8%25A7%25D9%2584%25D9%2588%25D8%25AD%25D8%25AF%25D8%25A7%25D8%25AA-modules-%25D9%2581%25D9%258A-%25D8%25AC%25D8%25A7%25D9%2581%25D8%25A7%25D8%25B3%25D9%2583%25D8%25B1%25D8%25A8%25D8%25AA-r926/&amp;sa=D&amp;source=docs&amp;ust=1733749715363166&amp;usg=AOvVaw0cbeyS_pvy6mM4Zp4Nbjto" rel="external nofollow">لوحدات Modules</a> تنظم مختلف أجزاء التطبيق وهي كالتالي:
</p>

<ul>
	<li>
		<code>common</code> لمعالجة جميع الخدمات المشتركة والمعلومات المتبادلة بين المستخدمين
	</li>
	<li>
		<code>users</code> يتضمن كل ما يتعلق بإدارة المستخدمين
	</li>
	<li>
		<code>auth</code> لمعالجة رموز JWT وعمليات تسجيل الدخول
	</li>
</ul>

<p>
	بعد ذلك، سنشغل الأمر <code>npm install</code> أو الأمر <code>yarn</code> لتنصيب التبعيات المطلوبة. بمجرد اكتمال عملية التنصيب، سنكون قد أعددنا كل الأمور والتبعيات اللازمة لتشغيل الواجهة الخلفية للواجهة البرمجية REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> باستخدام Node.js.
</p>

<h2 id="">
	إنشاء وحدة المستخدم User Module
</h2>

<p>
	سنستخدم <a href="https://mongoosejs.com/" rel="external nofollow">مكتبة Mongoose</a> مع MongoDB ، وهي مكتبة تساعدنا في التعامل مع بيانات MongoDB بطريقة مبسطة، وذلك من خلال إنشاء مخطط schema يحدد شكل بيانات المستخدم، ثم استخدامه لبناء نموذج model يسمح لنا بإضافة المستخدمين وتعديلهم وحذفهم من قاعدة البيانات.
</p>

<p>
	أولًا، نحتاج لإنشاء مخطط المستخدم User Schema في الملف <code>/users/models/users.model.js</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_7" style=""><span class="kwd">const</span><span class="pln"> userSchema </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Schema</span><span class="pun">({</span><span class="pln">
firstName</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
lastName</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
email</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
password</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
permissionLevel</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Number</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بعد تعريف المخطط يمكننا ربط المخطط بنموذج المستخدم بسهولة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_9" style=""><span class="kwd">const</span><span class="pln"> userModel </span><span class="pun">=</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="pln">model</span><span class="pun">(</span><span class="str">'Users'</span><span class="pun">,</span><span class="pln"> userSchema</span><span class="pun">);</span></pre>

<p>
	يمكننا بعدها استخدام هذا النموذج لإضافة كل عمليات CRUD التي نحتاجها في نقاط وصول Express.js الخاصة بنا.
</p>

<p>
	لنبدأ بإنشاء مستخدم جديد باستخدام العملية <code>create user</code> عن طريق تعريف <span ipsnoautolink="true">وجهة Route</span>‏ في الملف <code>users/routes.config.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_11" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/users'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">insert
</span><span class="pun">]);</span></pre>

<p>
	نقوم بإدراج هذا الجزء من الكود داخل تطبيقنا باستخدام Express.js من خلال الملف الرئيسي <code>index.js</code>، أما كائن <code>UsersController</code>، فنستورده من ملف المتحكم controller المسؤول عن معالجة المستخدمين.
</p>

<p>
	وسنشفر كلمة المرور بالشكل المناسب في الملف <code>/users/controllers/user.controller.js</code> لضمان حفظها بأمان في قاعدة البيانات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_13" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">insert </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> salt </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">randomBytes</span><span class="pun">(</span><span class="lit">16</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">(</span><span class="str">'base64'</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> hash </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">createHmac</span><span class="pun">(</span><span class="str">'sha512'</span><span class="pun">,</span><span class="pln">salt</span><span class="pun">)</span><span class="pln">
                                    </span><span class="pun">.</span><span class="pln">update</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">password</span><span class="pun">)</span><span class="pln">
                                    </span><span class="pun">.</span><span class="pln">digest</span><span class="pun">(</span><span class="str">"base64"</span><span class="pun">);</span><span class="pln">
   req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">password </span><span class="pun">=</span><span class="pln"> salt </span><span class="pun">+</span><span class="pln"> </span><span class="str">"$"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> hash</span><span class="pun">;</span><span class="pln">
   req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">permissionLevel </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">UserModel</span><span class="pun">.</span><span class="pln">createUser</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">)</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">201</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">_id</span><span class="pun">});</span><span class="pln">
       </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	عند الوصول لهذه النقطة يمكننا اختبار نموذجنا Mongoose عن طريق تشغيل خادم Node.js <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> باستخدام الأمر <code>npm start</code> وإرسال طلب <code>POST</code> يحتوي على بعض بيانات <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> إلى نقطة الوصول <code>users/</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_17" style=""><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="str">"firstName"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"ِِAbd"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"lastName"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hamza"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"email"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"ِِabd.hamza@example.com"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"password"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"s3cr3tp4sswo4rd"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هنالك العديد من الأدوات التي يمكننا استخدامها لاختبار واجهتنا البرمجية، سنعتمد على الأداة <a href="https://insomnia.rest/" rel="external nofollow">Insomnia</a>، و يمكن أيضًا استخدام <a href="https://www.postman.com/" rel="external nofollow">Postman</a> أو أي بدائل مفتوحة المصدر مثل أداة cURL أو <a href="https://www.usebruno.com/" rel="external nofollow">Bruno</a> كما يمكننا الاكتفاء بكتابة أكواد جافاسكريبت مباشرة، ككتابة الكود التالي في التبويب Console ضمن أدوات المطور Developer Tools للمتصفح<span>:</span>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_10" style=""><span class="pln">fetch</span><span class="pun">(</span><span class="str">'http://localhost:3600/users'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
        headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"Content-type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
        body</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">
            </span><span class="str">"firstName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Abd"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"lastName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hamza"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"email"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"abd.hamza@example.com"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"password"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"s3cr3tp4sswo4rd"</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Request succeeded with JSON response'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Request failed'</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span></pre>

<p>
	ستحتوي نتيجة طلب <code>POST</code> الناجحة على المعرف <code>id </code>للمستخدم الذي أنشأناه حديثًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_21" style=""><span class="pun">{</span><span class="pln"> </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"5b02c5c84817bf28049e58a3"</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سنحتاج أيضا لإضافة التابع <code>createUser</code> إلى النموذج في الملف  <code>users/models/users.model.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_23" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">createUser </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">userData</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">(</span><span class="pln">userData</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">save</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	الآن علينا التأكد فيما إذا كان المستخدم موجودًا في قاعدة البيانات، لذلك سنضيف ميزة الحصول على اسم المستخدم من خلال المعرّف  <code>id</code> إلى نقطة الوصول <code>users/:userId</code>.
</p>

<p>
	سننشئ أولًا <span ipsnoautolink="true">وجهة Route </span> للتطبيق في الملف <code>/users/routes/config.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_27" style=""><span class="pln">app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'/users/:userId'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">getById
</span><span class="pun">]);</span></pre>

<p>
	ثم سننشئ وحدة تحكم في الملف <code>/users/controllers/users.controller.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_29" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">getById </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">UserModel</span><span class="pun">.</span><span class="pln">findById</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">params</span><span class="pun">.</span><span class="pln">userId</span><span class="pun">).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">200</span><span class="pun">).</span><span class="pln">send</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وأخيرا نضيف التابع <code>findById</code> إلى النموذج في الملف <code>/users/models/users.model.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3384_31" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">findById </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">User</span><span class="pun">.</span><span class="pln">findById</span><span class="pun">(</span><span class="pln">id</span><span class="pun">).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        result </span><span class="pun">=</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">toJSON</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">delete</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">_id</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">delete</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">__v</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يجب أن نحصل على استجابة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_12" style=""><span class="pun">{</span><span class="pln">
   </span><span class="str">"firstName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Abd"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"lastName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hamza"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"email"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"abd.hamza@example.com"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"password"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ=="</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"permissionLevel"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"5b02c5c84817bf28049e58a3"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أن بإمكاننا رؤية كلمة المرور المشفرة هنا، ولكن يفضل عدم عرض كلمة المرور مطلقًا حتى ولو شفرناها. كما نلاحظ وجود الحقل <code>permissionLevel</code> وسنستخدمه لاحقًا لتحديد ومعالجة أذونات المستخدم.
</p>

<p>
	يمكننا الآن إضافة الوظيفة اللازمة لتحديث بيانات المستخدم مثل الاسم أو البريد الإلكتروني أو كلمة المرور بنفس الطريقة التي استخدمناها سابقًا. سنستخدم هنا عملية <code>PATCH</code> لأنها تسمح لنا بإرسال الحقول المرغوب بتعديلها فقط، ولذلك ستكون ال<span style="display: none;"> </span><span ipsnoautolink="true">وجهة</span> Route هو العملية <code>PATCH</code> لنقطة الوصول<br>
	<code>/users/:userid</code> وسنرسل أي حقول نريد تعديلها في جسم الطلب.
</p>

<p>
	سنحتاج أيضًا لإضافة بعض التحققات الإضافية لأن التعديل يجب أن يقتصر على المستخدم المسؤول admin، ولا يجب أن يتمكن أحد غير المسؤول من التعديل على الحقل <code>permisionLevel</code>. ولكننا سنتخطى هذا الأمر حاليًا ونعود له عندما نضيف وحدة المصادقة <code>auth</code>. يجب أن يبدو <span ipsnoautolink="true">المتحكم controller</span> الخاص بنا الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_16" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">patchById </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">password</span><span class="pun">){</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> salt </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">randomBytes</span><span class="pun">(</span><span class="lit">16</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">(</span><span class="str">'base64'</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> hash </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">createHmac</span><span class="pun">(</span><span class="str">'sha512'</span><span class="pun">,</span><span class="pln"> salt</span><span class="pun">).</span><span class="pln">update</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">password</span><span class="pun">).</span><span class="pln">digest</span><span class="pun">(</span><span class="str">"base64"</span><span class="pun">);</span><span class="pln">
       req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">password </span><span class="pun">=</span><span class="pln"> salt </span><span class="pun">+</span><span class="pln"> </span><span class="str">"$"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> hash</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="typ">UserModel</span><span class="pun">.</span><span class="pln">patchUser</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">params</span><span class="pun">.</span><span class="pln">userId</span><span class="pun">,</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">204</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({});</span><span class="pln">
   </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ترسل هذه الخطوة استجابة برمز الحالة HTTP 204 الذي يشير لنجاح تنفيذ الطلب، ولكن دون إرجاع أي بيانات أو محتوى في جسم الاستجابة.
</p>

<p>
	سنحتاج أيضا لإضافة التابع <code>patchUser</code> للنموذج model لتحديث بيانات المستخدم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_18" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">patchUser </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> userData</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">User</span><span class="pun">.</span><span class="pln">findOneAndUpdate</span><span class="pun">({</span><span class="pln">
        _id</span><span class="pun">:</span><span class="pln"> id
    </span><span class="pun">},</span><span class="pln"> userData</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يحقق <span ipsnoautolink="true">المتحكم controller</span> التالي عملية <code>GET</code> لعرض قائمة المستخدمين عند نقطة الوصول<code>/users/</code> مع تطبيق آلية التصفية باستخدام الصفحات والحد الأقصى للعناصر المعروضة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_21" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">list </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> limit </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">limit </span><span class="pun">&amp;&amp;</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">limit </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> parseInt</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">limit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> page </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">page</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">page </span><span class="pun">=</span><span class="pln"> parseInt</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">page</span><span class="pun">);</span><span class="pln">
           page </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">.</span><span class="pln">isInteger</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">page</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">query</span><span class="pun">.</span><span class="pln">page </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="typ">UserModel</span><span class="pun">.</span><span class="pln">list</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">,</span><span class="pln"> page</span><span class="pun">).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">200</span><span class="pun">).</span><span class="pln">send</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">})</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	سيكون تابع النموذج المقابل كالآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_26" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">list </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">perPage</span><span class="pun">,</span><span class="pln"> page</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">User</span><span class="pun">.</span><span class="pln">find</span><span class="pun">()</span><span class="pln">
            </span><span class="pun">.</span><span class="pln">limit</span><span class="pun">(</span><span class="pln">perPage</span><span class="pun">)</span><span class="pln">
            </span><span class="pun">.</span><span class="pln">skip</span><span class="pun">(</span><span class="pln">perPage </span><span class="pun">*</span><span class="pln"> page</span><span class="pun">)</span><span class="pln">
            </span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> users</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    reject</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    resolve</span><span class="pun">(</span><span class="pln">users</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وستكون للاستجابة الناتجة البنية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_28" style=""><span class="pun">[</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       </span><span class="str">"firstName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Maher"</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"lastName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Saleh"</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"email"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"maher.saleh@example.com"</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"password"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA=="</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"permissionLevel"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"5b02c5c84817bf28049e58a3"</span><span class="pln">
   </span><span class="pun">},</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       </span><span class="str">"firstName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Abd"</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"lastName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hamza"</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"email"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"abd.hamza@example.com"</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"password"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw=="</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"permissionLevel"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
       </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"5b02d038b653603d1ca69729"</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	وأخيرا سنضيف عملية <code>DELETE</code> لحذف المستخدم بناء على المعرف الخاص به عند نقطة الوصول <code>/users/:userId.</code>
</p>

<p>
	سننشئ <span ipsnoautolink="true">المتحكم controller</span> المسؤول عن الحذف كالآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_31" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">removeById </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">UserModel</span><span class="pun">.</span><span class="pln">removeById</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">params</span><span class="pun">.</span><span class="pln">userId</span><span class="pun">)</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">result</span><span class="pun">)=&gt;{</span><span class="pln">
           res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">204</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({});</span><span class="pln">
       </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	كما في السابق، سيرسل المتحكم استجابة بالحالة HTTP 204 الذي يشير لنجاح الطلب. ويكون تابع النموذج model method المقابل كالآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_33" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">removeById </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">userId</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">User</span><span class="pun">.</span><span class="pln">deleteMany</span><span class="pun">({</span><span class="pln">_id</span><span class="pun">:</span><span class="pln"> userId</span><span class="pun">},</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                reject</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                resolve</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أصبحنا الآن نملك جميع العمليات اللازمة للتحكم ببيانات المستخدم.
</p>

<h2 id="auth">
	إنشاء وحدة المصادقة Auth
</h2>

<p>
	قبل أن نتمكن من تأمين وحدة المستخدمين <code>users</code> عن طريق إضافة الأذونات والبرامج الوسيطة للتحقق، نحتاج أولًا إلى طريقة تمكننا من توليد رمز مميز JWT صالح للمستخدم الحالي. سيُستخدم هذا الرمز لإثبات هوية المستخدم في الطلبات المستقبلية دون الحاجة لإجراء التحقق في كل مرة. يملك هذا الرمز عادة وقت انتهاء صلاحية ويُنشَأ رمز جديد كل عدة دقائق للحفاظ على تواصل آمن، ولكن في هذا المقال سنتخطى إعادة إنشاء الرمز كل مرة ونكتفي برمز واحد كل تسجيل دخول لتبسيط العمل.
</p>

<p>
	سننشئ بداية نقطة وصول تستقبل طلبات <code>POST</code> إلى المورد  <code>auth/</code>. يجب أن يحتوي جسم الطلب على البريد الإلكتروني وكلمة المرور الخاصة بالمستخدم على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_36" style=""><span class="pun">{</span><span class="pln">
   </span><span class="str">"email"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"abd.hamza@example.com"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"password"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"s3cr3tp4sswo4rd2"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	قبل أن نسمح للطلب بالوصول ل<span ipsnoautolink="true">لمتحكم </span>والتفاعل معه يجب التحقق من أن المستخدم موجود وكلمة مروره صحيحة عن طريق كود يكتب في الملف<br>
	<code>/authorization/middlewares/verify.user.middleware.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_39" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">isPasswordAndUserMatch </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">UserModel</span><span class="pun">.</span><span class="pln">findByEmail</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">email</span><span class="pun">)</span><span class="pln">
       </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">user</span><span class="pun">)=&gt;{</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(!</span><span class="pln">user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]){</span><span class="pln">
               res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">404</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({});</span><span class="pln">
           </span><span class="pun">}</span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
               </span><span class="kwd">let</span><span class="pln"> passwordFields </span><span class="pun">=</span><span class="pln"> user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">password</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'$'</span><span class="pun">);</span><span class="pln">
               </span><span class="kwd">let</span><span class="pln"> salt </span><span class="pun">=</span><span class="pln"> passwordFields</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
               </span><span class="kwd">let</span><span class="pln"> hash </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">createHmac</span><span class="pun">(</span><span class="str">'sha512'</span><span class="pun">,</span><span class="pln"> salt</span><span class="pun">)</span><span class="pln">
                                </span><span class="pun">.</span><span class="pln">update</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">password</span><span class="pun">)</span><span class="pln">
                                </span><span class="pun">.</span><span class="pln">digest</span><span class="pun">(</span><span class="str">"base64"</span><span class="pun">);</span><span class="pln">
               </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">hash </span><span class="pun">===</span><span class="pln"> passwordFields</span><span class="pun">[</span><span class="lit">1</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                   req</span><span class="pun">.</span><span class="pln">body </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                       userId</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">_id</span><span class="pun">,</span><span class="pln">
                       email</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">email</span><span class="pun">,</span><span class="pln">
                       permissionLevel</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">permissionLevel</span><span class="pun">,</span><span class="pln">
                       provider</span><span class="pun">:</span><span class="pln"> </span><span class="str">'email'</span><span class="pun">,</span><span class="pln">
                       name</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">firstName </span><span class="pun">+</span><span class="pln"> </span><span class="str">' '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> user</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">lastName</span><span class="pun">,</span><span class="pln">
                   </span><span class="pun">};</span><span class="pln">
                   </span><span class="kwd">return</span><span class="pln"> next</span><span class="pun">();</span><span class="pln">
               </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                   </span><span class="kwd">return</span><span class="pln"> res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">400</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({</span><span class="pln">errors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'Invalid email or password'</span><span class="pun">]});</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	بعد القيام بذلك، يمكننا الانتقال إلى <span ipsnoautolink="true">المتحكم controller</span> وتوليد رمز JWT:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_42" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">login </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> refreshId </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">userId </span><span class="pun">+</span><span class="pln"> jwtSecret</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> salt </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">randomBytes</span><span class="pun">(</span><span class="lit">16</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">(</span><span class="str">'base64'</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> hash </span><span class="pun">=</span><span class="pln"> crypto</span><span class="pun">.</span><span class="pln">createHmac</span><span class="pun">(</span><span class="str">'sha512'</span><span class="pun">,</span><span class="pln"> salt</span><span class="pun">).</span><span class="pln">update</span><span class="pun">(</span><span class="pln">refreshId</span><span class="pun">).</span><span class="pln">digest</span><span class="pun">(</span><span class="str">"base64"</span><span class="pun">);</span><span class="pln">
       req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">refreshKey </span><span class="pun">=</span><span class="pln"> salt</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> jwt</span><span class="pun">.</span><span class="pln">sign</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">body</span><span class="pun">,</span><span class="pln"> jwtSecret</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> b </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">hash</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> refresh_token </span><span class="pun">=</span><span class="pln"> b</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">(</span><span class="str">'base64'</span><span class="pun">);</span><span class="pln">
       res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">201</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({</span><span class="pln">accessToken</span><span class="pun">:</span><span class="pln"> token</span><span class="pun">,</span><span class="pln"> refreshToken</span><span class="pun">:</span><span class="pln"> refresh_token</span><span class="pun">});</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">500</span><span class="pun">).</span><span class="pln">send</span><span class="pun">({</span><span class="pln">errors</span><span class="pun">:</span><span class="pln"> err</span><span class="pun">});</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	رغم أننا في هذا المقال لن نعيد إنشاء الرمز المميز JWT بشكل متكرر، إلا أن المتحكم Controller مُصمم ليكون قادرًا على توليد هذه الرموز عند الحاجة. يُعد JWT جزءًا أساسيًا من عملية التحقق من هوية المستخدم، لذلك تم تضمين وظيفة توليد الرمز في المتحكم لتسهيل استخدامها مستقبلًا، سواء في هذا المشروع أو في مشاريع قادمة.
</p>

<p>
	كل ما نحتاجه هو إنشاء <span ipsnoautolink="true">وجهة</span> واستدعاء البرمجية الوسيطة Middleware المناسبة في الملف <code>/authorization/routes.config.js</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6366_11" style=""><span class="pln">  app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/auth'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="typ">VerifyUserMiddleware</span><span class="pun">.</span><span class="pln">hasAuthValidFields</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">VerifyUserMiddleware</span><span class="pun">.</span><span class="pln">isPasswordAndUserMatch</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">AuthorizationController</span><span class="pun">.</span><span class="pln">login
    </span><span class="pun">]);</span></pre>

<p>
	ستحتوي الاستجابة الناتجة على رمز JWT المولد في الحقل <code>accessToken</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9503_48" style=""><span class="pun">{</span><span class="pln">
   </span><span class="str">"accessToken"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY"</span><span class="pun">,</span><span class="pln">
   </span><span class="str">"refreshToken"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ=="</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بعد إنشاء الرمز، يمكننا استخدامه داخل ترويسة <code>Authorization</code> باستخدام صيغة <code>Bearer ACCESS_TOKEN</code>
</p>

<h2 id="-1">
	إنشاء برمجيات وسيطة لعمليات التحقق Validations والأذونات Permissions
</h2>

<p>
	أول ما يجب علينا فعله هو تحديد من يستطيع استخدام المورد <code>users</code>. وفيما يلي السيناريوهات التي علينا التعامل معها:
</p>

<ul>
	<li>
		التسجيل أو إنشاء مستخدم جديد: متاح للجميع، ولا يتطلب استخدام رموز JWT
	</li>
	<li>
		تحديث بيانات مستخدم: مسموح فقط للمستخدم الذي قام بتسجيل الدخول أو للمسؤولين
	</li>
	<li>
		حذف حساب مستخدم: مخصص للمسؤولين فقط
	</li>
</ul>

<p>
	بعد تحديد السيناريوهات المحتملة لكيفية تصميم مستويات مختلفة من الوصول بناءً على الأدوار ومتطلبات الأمان في التطبيق، سنحتاج أولًا إلى برمجية وسيطة Middleware تتحقق فيما إذا كان المستخدم يملك رمز JWT صالح. قد تكون عبارة عن برمجية وسيطة بسيطة ضمن الملف <br>
	<code>/common/middlewares/auth.validation.middleware.js</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6366_13" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">validJWTNeeded </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">[</span><span class="str">'authorization'</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">let</span><span class="pln"> authorization </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">[</span><span class="str">'authorization'</span><span class="pun">].</span><span class="pln">split</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">authorization</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!==</span><span class="pln"> </span><span class="str">'Bearer'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">401</span><span class="pun">).</span><span class="pln">send</span><span class="pun">();</span><span class="pln">
            </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                req</span><span class="pun">.</span><span class="pln">jwt </span><span class="pun">=</span><span class="pln"> jwt</span><span class="pun">.</span><span class="pln">verify</span><span class="pun">(</span><span class="pln">authorization</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> secret</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> next</span><span class="pun">();</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">403</span><span class="pun">).</span><span class="pln">send</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">401</span><span class="pun">).</span><span class="pln">send</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln"> </span></pre>

<p>
	سنستخدم رموز الحالة الخاصة ببروتوكول HTTP للتعامل مع الأخطاء التي قد تحدث أثناء معالجة الطلبات، وذلك على النحو التالي:
</p>

<ul>
	<li>
		رمز 401 Unauthorized: يُستخدم عندما يكون الطلب غير مصحوب برمز JWT صالح، أو عندما لا يتم إرسال أي رمز على الإطلاق
	</li>
	<li>
		رمز 403 Forbidden: يُستخدم عندما يكون الرمز المرسل صالحًا، لكن المستخدم لا يملك الصلاحيات الكافية لتنفيذ هذا الطلب
	</li>
</ul>

<p>
	يمكننا استخدام العملية المنطقية AND أو ما يُعرف بـ قناع البِت Bitmasking، حيث نعتبر كل بت في عدد مكوّن من 32 بت كإذن منفصل. وكل إذن يُمثَّل كقوة للعدد 2. على سبيل المثال، يمكن للمسؤول أن يمتلك جميع الأذونات عن طريق تعيين الرقم 2147483647، مما يعني أن جميع الأذونات مفعّلة. من جهة أخرى، يمكننا تعيين قيمة 7 للأذونات، مما يعني تفعيل الأذونات الموجودة في البتات التي تحمل القيم 1 و 2 و 4، أو ما يعادل القوى 0 و 1 و 2 للعدد 2.
</p>

<p>
	وفيما يلي مثال على كيفية استخدام ذلك في برمجية وسيطة Middleware:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6366_15" style=""><span class="pln">exports</span><span class="pun">.</span><span class="pln">minimumPermissionLevelRequired </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">required_permission_level</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> user_permission_level </span><span class="pun">=</span><span class="pln"> parseInt</span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">jwt</span><span class="pun">.</span><span class="pln">permission_level</span><span class="pun">);</span><span class="pln">
       </span><span class="kwd">let</span><span class="pln"> user_id </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">jwt</span><span class="pun">.</span><span class="pln">user_id</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">user_permission_level </span><span class="pun">&amp;</span><span class="pln"> required_permission_level</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> next</span><span class="pun">();</span><span class="pln">
       </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> res</span><span class="pun">.</span><span class="pln">status</span><span class="pun">(</span><span class="lit">403</span><span class="pun">).</span><span class="pln">send</span><span class="pun">();</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تتحقق هذه البرمجية الوسيطة مما إذا كان مستوى أذونات المستخدم يتطابق مع المستوى المطلوب باستخدام العملية AND. إذا كانت النتيجة أكبر من الصفر -أي أن المستخدم يملك الإذن المطلوب- يُسمَح للعملية بالاستمرار عبر الدالة<code> next </code>أما إذا كانت النتيجة صفرًا، فهذا يعني أن المستخدم ليس لديه الأذونات الكافية، وبالتالي ترجع الرمز HTTP 403.
</p>

<p>
	الآن، نحتاج إلى إضافة البرمجية الوسيطة الخاصة بالاستيثاق في وجهة وحدة المستخدم الموجودة في الملف<br>
	<code>/users/routes.config.js</code>، بحيث نضيف التحقق من صلاحيات المستخدم قبل السماح له بالوصول إلى العمليات المختلفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6366_17" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/users'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">insert
</span><span class="pun">]);</span><span class="pln">
app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'/users'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="typ">ValidationMiddleware</span><span class="pun">.</span><span class="pln">validJWTNeeded</span><span class="pun">,</span><span class="pln">
   </span><span class="typ">PermissionMiddleware</span><span class="pun">.</span><span class="pln">minimumPermissionLevelRequired</span><span class="pun">(</span><span class="pln">PAID</span><span class="pun">),</span><span class="pln">
   </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">list
</span><span class="pun">]);</span><span class="pln">
app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'/users/:userId'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="typ">ValidationMiddleware</span><span class="pun">.</span><span class="pln">validJWTNeeded</span><span class="pun">,</span><span class="pln">
   </span><span class="typ">PermissionMiddleware</span><span class="pun">.</span><span class="pln">minimumPermissionLevelRequired</span><span class="pun">(</span><span class="pln">FREE</span><span class="pun">),</span><span class="pln">
   </span><span class="typ">PermissionMiddleware</span><span class="pun">.</span><span class="pln">onlySameUserOrAdminCanDoThisAction</span><span class="pun">,</span><span class="pln">
   </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">getById
</span><span class="pun">]);</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">patch</span><span class="pun">(</span><span class="str">'/users/:userId'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="typ">ValidationMiddleware</span><span class="pun">.</span><span class="pln">validJWTNeeded</span><span class="pun">,</span><span class="pln">
   </span><span class="typ">PermissionMiddleware</span><span class="pun">.</span><span class="pln">minimumPermissionLevelRequired</span><span class="pun">(</span><span class="pln">FREE</span><span class="pun">),</span><span class="pln">
   </span><span class="typ">PermissionMiddleware</span><span class="pun">.</span><span class="pln">onlySameUserOrAdminCanDoThisAction</span><span class="pun">,</span><span class="pln">
   </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">patchById
</span><span class="pun">]);</span><span class="pln">
app</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">(</span><span class="str">'/users/:userId'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
   </span><span class="typ">ValidationMiddleware</span><span class="pun">.</span><span class="pln">validJWTNeeded</span><span class="pun">,</span><span class="pln">
   </span><span class="typ">PermissionMiddleware</span><span class="pun">.</span><span class="pln">minimumPermissionLevelRequired</span><span class="pun">(</span><span class="pln">ADMIN</span><span class="pun">),</span><span class="pln">
   </span><span class="typ">UsersController</span><span class="pun">.</span><span class="pln">removeById
</span><span class="pun">]);</span></pre>

<p>
	هكذا نكون أكملنا التطوير الأساسي لواجهة REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> باستخدام Node.js وكل ما يتبقى هو اختبارها للتأكد من خلوها من الأخطاء.
</p>

<h2 id="insomnia">
	تشغيل واختبار الواجهة البرمجية باستخدام Insomnia
</h2>

<p>
	سنختبر الواجهة البرمجية باستخدام <a href="https://insomnia.rest/" rel="external nofollow">Insomnia</a>، وهو برنامج عميل REST مناسب يحتوي على نسخة مجانية، ومن الأفضل بالتأكيد تضمين اختبارات الكود وإنشاء تقارير للأخطاء بشكل مناسب داخل المشروع، لكن Insomnia يعد خيارًا جيدًا حاليًا لمجرد اختبار الواجهة البرمجية.
</p>

<p>
	لنتمكن من إنشاء مستخدم، نحتاج فقط إلى إرسال طلب <code>POST</code> مع البيانات المطلوبة لنقطة الوصول المناسبة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170227" href="https://academy.hsoub.com/uploads/monthly_2025_04/_001.JPG.23f1f484e2efce8ed8b43ae31dc321ed.JPG" rel=""><img alt="001 اختبار الواجهة البرمجية طلب post" class="ipsImage ipsImage_thumbnailed" data-fileid="170227" data-ratio="31.29" data-unique="joyn0d77v" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_04/_001.thumb.JPG.9a8a49df9f7222aee804880c010f658f.JPG"></a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="" rel=""> </a>
</p>

<p>
	بعد نجاح الطلب، سنحفظ المعرّف الذي أنشأته لنا الواجهة البرمجية لهذا المستخدم، لاستخدامه لاحقًا في عمليات لاحقة مثل التحديث أو الحذف.
</p>

<p style="text-align: center;">
	<img alt="002 اختبار الواجهة البرمجية معرف المستخدم" class="ipsImage ipsImage_thumbnailed" data-fileid="170228" data-ratio="45.50" data-unique="cxioj5r2a" style="width: 600px; height: auto;" width="644" src="https://academy.hsoub.com/uploads/monthly_2025_04/_002.JPG.52cbcd27c34b84d621edd28f92ec397e.JPG">
</p>

<p>
	يمكننا الآن توليد رمز JWT باستخدام نقطة الوصول <code>/auth/</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170229" href="https://academy.hsoub.com/uploads/monthly_2025_04/_003.JPG.802b43b642d2cc7abc9c3fdfe6be6bcf.JPG" rel=""><img alt="003 اختبار الواجهة البرمجية رمز JWT " class="ipsImage ipsImage_thumbnailed" data-fileid="170229" data-ratio="32.00" data-unique="hpjzeapud" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_04/_003.thumb.JPG.788b6a6e68751b60555a01ae35cc6c04.JPG"></a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="" rel=""> </a>
</p>

<p>
	يجب أن نحصل بنتيجة الطلب على رمز وصول <code>Token</code> كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170164" href="https://academy.hsoub.com/uploads/monthly_2025_04/_004.JPG.a7f3b78d07b1e9eae416ba8e2e3dc6ac.JPG" rel=""><img alt="إختبار الواجهة004 رمز token" class="ipsImage ipsImage_thumbnailed" data-fileid="170164" data-unique="ab7gd0gqv" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_004.thumb.JPG.28c189c7a2abf482813db7fd246740a1.JPG"> </a>
</p>

<p>
	الآن، سننسخ هذا الرمز ونضيف قبله كلمة <code>Bearer</code>، مع التأكد من ترك فراغ بين الكلمتين. وسنضيف هذا الرمز لكافة ترويسات الطلب Request Headers ضمن الحقل <code>Authorization</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170163" href="https://academy.hsoub.com/uploads/monthly_2025_04/_005.JPG.92ca1eba850b768e78e79d858c161846.JPG" rel=""><img alt="إختبار الواجهة005" class="ipsImage ipsImage_thumbnailed" data-fileid="170163" data-unique="ybd9hd3so" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_005.thumb.JPG.d39b235277370fac165dd886bc476246.JPG"> </a>
</p>

<p>
	تعرض الصورة التالية استجابة الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> عند طلب بيانات مستخدم معين من نقطة الوصول <code>/users/:userId </code>بعد إضافة البرمجية الوسيطة للتحقق من الصلاحيات Permissions Middleware، مع وجود رمز مصادقة صالح Valid Token.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170162" href="https://academy.hsoub.com/uploads/monthly_2025_04/_006.JPG.fe0cbfac1763c2197a320904d8e4ef41.JPG" rel=""><img alt="إختبار الواجهة006" class="ipsImage ipsImage_thumbnailed" data-fileid="170162" data-unique="mxdm349x2" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_006.thumb.JPG.f49594d308de89340ba753d6cddc9614.JPG"> </a>
</p>

<p>
	كما ذكرنا سابقًا نعرض هنا كلمة المرور لتبسيط الشرح، ولكن لا ننسى أنه لا يجب نهائيًا عرض كلمة المرور في الاستجابات، سواءً كانت مشفرة أم لم تكن.
</p>

<p>
	لنحاول الآن الحصول على قائمة بجميع المستخدمين بإرسال الطلب <code>GET </code>على النحو التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170161" href="https://academy.hsoub.com/uploads/monthly_2025_04/_007.JPG.8ebafe8edc0e510df639b2fd297c965e.JPG" rel=""><img alt="إختبار الواجهة007" class="ipsImage ipsImage_thumbnailed" data-fileid="170161" data-unique="2ifz2wq7m" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_007.thumb.JPG.9e632f26ef63bccf69a9237c1200e56a.JPG"> </a>
</p>

<p>
	سنتفاجأ بالحصول على استجابة <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81-%D9%88%D8%A5%D8%B5%D9%84%D8%A7%D8%AD-%D8%B1%D9%85%D9%88%D8%B2-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-http-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-r116/" rel="">برمز الخطأ 403</a>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170160" href="https://academy.hsoub.com/uploads/monthly_2025_04/_008.JPG.afd73b108a0b7bdb34652a77097c2b3a.JPG" rel=""><img alt="إختبار الواجهة008" class="ipsImage ipsImage_thumbnailed" data-fileid="170160" data-unique="hgck8npdr" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_008.thumb.JPG.9b11ad9dab0c55b4b0162f4d524ace8e.JPG"> </a>
</p>

<p>
	السبب وراء عدم القدرة على الوصول إلى هذه النقطة هو أن مستوى الصلاحيات الحالي للمستخدم هو <code>permissionLevel=1</code> وهذا لا يمنحه الإذن الكافي للوصول إلى البيانات المطلوبة، لذلك يجب تحديث الصلاحيات <code>permissionLevel</code> لهذا المستخدم من القيمة 1 للقيمة 7، أو ربما للقيمة 5 فهي كافية للوصول إلى نقطة الوصول الحالية.
</p>

<p>
	يمكننا القيام بهذا التحديث عبر واجهة MongoDB التفاعلية يدويًا، مع تعديل المعرف ليتناسب مع المستخدم الحالي لدينا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_851_14" style=""><span class="pln">db</span><span class="pun">.</span><span class="pln">users</span><span class="pun">.</span><span class="pln">update</span><span class="pun">({</span><span class="str">"_id"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">ObjectId</span><span class="pun">(</span><span class="str">"5b02c5c84817bf28049e58a3"</span><span class="pun">)},{</span><span class="pln">$set</span><span class="pun">:{</span><span class="str">"permissionLevel"</span><span class="pun">:</span><span class="lit">5</span><span class="pun">}})</span></pre>

<p>
	سنحتاج بعد ذلك لتوليد رمز JWT جديد. وبعدها سنحصل على استجابة صحيحة للطلب <code>GET</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170159" href="https://academy.hsoub.com/uploads/monthly_2025_04/_009.JPG.199d72c92301cca3fd52776c98509dc3.JPG" rel=""><img alt="إختبار الواجهة009" class="ipsImage ipsImage_thumbnailed" data-fileid="170159" data-unique="908z08z9j" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_009.thumb.JPG.820b67482fde8717d39921e07aef17af.JPG"> </a>
</p>

<p>
	سنختبر الآن وظيفة تحديث بيانات المستخدم عبر إرسال طلب <code>POST</code> مع بعض الحقول إلى نقطة الوصول <code>/users/:userId</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170158" href="https://academy.hsoub.com/uploads/monthly_2025_04/_010.JPG.74b36dcafc30e2e97a84730252bf67e0.JPG" rel=""><img alt="إختبار الواجهة010" class="ipsImage ipsImage_thumbnailed" data-fileid="170158" data-unique="baikphmgz" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_010.thumb.JPG.c0eff43719c592cb03d37ad0681ab447.JPG"> </a>
</p>

<p>
	نتوقع الحصول على استجابة بالرمز 204 للتأكد بأن العملية نجحت، ولكن يمكننا التأكد عبر طلب معلومات المستخدم مجددًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170157" href="https://academy.hsoub.com/uploads/monthly_2025_04/_011.JPG.58c73e9e8df76e656d3e67cf7230a812.JPG" rel=""><img alt="إختبار الواجهة011" class="ipsImage ipsImage_thumbnailed" data-fileid="170157" data-unique="9p8qjykgi" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_011.thumb.JPG.d8ceec47cd192f99a49cddf259816b7e.JPG"> </a>
</p>

<p>
	وأخيرا سنحتاج لاختبار حذف المستخدم، للقيام بهذا يجب علينا إنشاء مستخدم جديد، مع عدم نسيان الاحتفاظ برمز المعرف الخاص بالمستخدم والتأكد من امتلاكنا لرمز JWT مناسب لمستخدم مسؤول. يجب علينا تعيين الرقم 2053 إلى <code>userPermission</code> الخاص بالمستخدم الجديد والذي يساوي الرقم 2048، أي <code>ADMIN</code>، بالإضافة إلى الرقم 5 الذي عيناه سابقًا ليتمكن من تنفيذ عملية حذف المستخدم بعد الانتهاء من ذلك وتوليد رمز JWT جديد، يجب علينا تحديث ترويسة طلب <code>Authorization</code> الذي أضفناه سابقًا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="170156" href="https://academy.hsoub.com/uploads/monthly_2025_04/_012.JPG.1af1d630dca855a77ee8b934594c88af.JPG" rel=""><img alt="إختبار الواجهة012" class="ipsImage ipsImage_thumbnailed" data-fileid="170156" data-unique="8xergvbjp" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/_012.thumb.JPG.bf0cb3c99c144b49dbfe39ed1b078db5.JPG"> </a>
</p>

<p>
	بعد إرسال طلب <code>DELETE</code> لنقطة الوصول <code>/users/:userId</code> يفترض أن نحصل على استجابة برمز الحالة 204 لتأكيد نجاح العملية. ويمكننا التأكد مجددا عبر إرسال طلب <code>/users/</code> لجميع المستخدمين الموجودين من خادم Node <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاص بنا.
</p>

<h2 id="-2">
	الخاتمة
</h2>

<p>
	تعلمنا في هذا المقال أهم الخطوات اللازمة لإنشاء واجهة برمجة تطبيقات REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> بسيطة وآمنة باستخدام Node.js باستخدام مجموعة من الأدوات والتقنيات، ويجب الانتباه إلى أننا تخطينا هنا اتباع أفضل الممارسات للسهولة ويجب الانتباه للأمور التالية عند تطوير واجهة برمجية فعلية:
</p>

<ul>
	<li>
		إضافة تحققات صحيحة وشاملة، كالتأكد من كون البريد الإلكتروني فريد لكل مستخدم
	</li>
	<li>
		إضافة اختبار الوحدة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%87%D9%8A%D9%83%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-unit-tests-r1132/" rel="">unit testing</a> وتقارير للأخطاء
	</li>
	<li>
		منع المستخدمين من تغيير مستوى الصلاحيات الخاص بهم
	</li>
	<li>
		منع المسؤولين من حذف أنفسهم
	</li>
	<li>
		منع الكشف عن المعلومات الحساسة، ككلمات المرور المشفرة على سبيل المثال
	</li>
	<li>
		نقل رمز JWT من ملف <code>common/config/env.config.js</code> إلى نظام مخصص لتوزيع الرموز السرية خارج الكود البرمجي، بحيث لا يعتمد على البيئة المحلية
	</li>
</ul>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.toptal.com/nodejs/secure-rest-api-in-nodejs" rel="external nofollow">Creating a Secure REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> in Node.js</a> لصاحبه Marcos Henrique da Silva.
</p>

<h2 id="-3">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/" rel="">اللاتزامن والانتظار async/await في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">مدخل إلى الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%81%D9%84%D8%B3%D9%81%D8%A9-restful-%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-rest-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r635/" rel="">شرح فلسفة RESTful - تعلم كيف تبني واجهات REST البرمجية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/express/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-nodejs-%D9%88express-r1099/" rel="">مدخل إلى Node.js وExpress</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2548</guid><pubDate>Thu, 03 Apr 2025 15:00:00 +0000</pubDate></item><item><title>&#x625;&#x639;&#x62F;&#x627;&#x62F; &#x62E;&#x627;&#x62F;&#x645; GraphQL &#x641;&#x64A; &#x628;&#x64A;&#x626;&#x629; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-graphql-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-r2189/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_12/--GraphQL---Node.png.977e095be3958666e9e6a3e7bb584056.png" /></p>
<p>
	GraphQL لغة استعلام <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر</a> لواجهات برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> وبيئة تشغيل لتنفيذ الاستعلامات، وأُنشِئَت في الأساس لمعالجة مشكلات REST التقليدية وقد عرضنا بعضًا منها، وسنطّلع في هذا المقال على آلية تفاعل مكونات GraphQL مع بعضها، عبر بناء خادم GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> في بيئة <a href="https://nodejs.org/en" rel="external nofollow">Node.js</a>، وسيُستخدم خادم Express <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> بدلًا من <a href="https://new.apollographql.com/" rel="external nofollow">Apollo GraphQL</a> رغم أنه الأشهر بين تطبيقات GraphQL.
</p>

<p>
	ستتعلم خلال متابعتك لخطوات العمل إنشاء مخطط GraphQL متوافق مع نظام الأنواع بما فيه من عمليات، مثل الاستعلامات والطفرات ومحللات الاستجابة، وستتآلف مع <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/" rel="">بيئة التطوير المتكاملة</a> <a href="https://github.com/graphql/graphiql" rel="external nofollow">GraphiQL</a>، إذ سنستعملها لاستعراض المخطط وتصحيح أخطائه، ولطلب استعلامات العميل من واجهة GraphQL.
</p>

<h2 id="">
	متطلبات العمل
</h2>

<p>
	جهّز المتطلبات التالية لتتابع معنا سير العمل:
</p>

<ul>
	<li>
		بيئة Node.js جاهزة على خادمك المحلي، ويمكنك الاسترشاد بأحد المقالين <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-nodejs-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r419/" rel="">تثبيت Node.js على نظام أبونتو 18.04</a> أو <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-nodejs-%D8%B9%D9%84%D9%89-debian-8-r385/" rel="">كيفية تثبيت Node.js على Debian 8</a> لإعدادها.
	</li>
	<li>
		فهم <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-graphql-r2208/" rel="">أساسيات GraphQL</a>.
	</li>
	<li>
		الإلمام ببروتوكول HTTP، ننصحك بقراءة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-r73/" rel="">مدخل إلى HTTP</a>.
	</li>
	<li>
		معرفة أساسية بكل من <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">HTML</a> و <a href="https://academy.hsoub.com/files/33-%D8%AF%D9%84%D9%8A%D9%84-javascript-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84/" rel="">JavaScript</a>، تفيدك هذه المقالات <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-html-r1894/" rel="">أساسيات إنشاء موقع ويب باستخدام تعليمات HTML</a>، و<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">أساسيات لغة جافاسكربت</a>.
	</li>
</ul>

<h2 id="expresshttp">
	إعداد خادم Express HTTP
</h2>

<p>
	يبدأ إعداد خادم Express بتثبيت الحزمتين <code>express</code> و <code>cors</code>، باستخدام الأمر <code>npm install</code> ضمن مشروع جديد وفق التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_8" style=""><span class="pln">npm install express cors</span></pre>

<p>
	<a href="https://expressjs.com/" rel="external nofollow">Express</a> هو إطار لتطبيقات الويب المبنية على Node.js، وهو مصمم خصيصًا لبناء واجهات برمجة التطبيقات، وسيكون إطار العمل الخاص بخادمنا، أما الحزمة <a href="https://www.npmjs.com/package/cors" rel="external nofollow">CORS</a> وهي اختصار لمصطلح البرمجيات الوسيطة لمشاركة الموارد ذات الأصل المختلط Cross-Origin Resource Sharing، إذ إن الغاية منها هي السماح لمتصفحك بالوصول إلى موارد الخادم.
</p>

<p>
	ثبّت أيضًا الاعتمادية Nodemon مع الراية D بصفتها اعتمادية تطوير، لتستخدمها في مرحلة التطوير فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_12" style=""><span class="pln">npm install </span><span class="pun">-</span><span class="pln">D nodemon</span></pre>

<p>
	<a href="https://www.npmjs.com/package/nodemon" rel="external nofollow">Nodemon</a> هي أداة مساعدة في تطوير تطبيقات Node، تعيد تشغيل التطبيق في كل مرة يطرأ فيها تغيير على ملفات المشروع الموجودة في مجلد الجذر لتأخذ التعديلات مفعولها.
</p>

<p>
	سينشأ بتثبيت هذه الحزم المجلد <code>node_modules</code> والملف <code>package.json</code> مع اعتماديتي إنتاج واعتمادية تطوير.
</p>

<p>
	إذا فتحت الملف package.json بمحرر نصوص مثل <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/" rel="">نانو nano</a> ستجده يحتوي المعلومات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_14" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"cors"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^2.8.5"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"express"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^4.17.3"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"nodemon"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^2.0.15"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عدّل عليه ليصبح وفق التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_16" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"server.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"dev"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"nodemon server.js"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"cors"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^2.8.5"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"express"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^4.17.3"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"nodemon"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^2.0.15"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"module"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنشرح التعديلات السابقة، إذ وضعنا ملف الخادم "server.js" -الذي سننشئه بعد قليل- في الحقل <code>main</code>، وبهذا تتأكد من تشغيل <code>npm start</code> للخادم؛ أما الملف النصي المسمى "dev"، فيُسهل التطوير إذ يتضمن أمر تشغيل <code>nodemon server.js</code>.
</p>

<p>
	أضفنا في السطر الأخير القيمة <code>module</code> لحقل النوع <code>type</code> لنُفعّل إمكانية استخدام تعليمة الاستيراد <code>import</code> عوضًا عن <code>require</code> -تعليمة CommonJS الافتراضية- وفي هذا أيضًا تسهيلٌ للعمل.
</p>

<p>
	احفظ التغييرات، وأغلق الملف، لننتقل لإنشاء ملف الخادم.
</p>

<p>
	سنبني الآن خادم Express بسيط عبر إنشاء ملف نصي باسم "server.js"، يتضمن التعليمات المبينة أدناه، يعمل هذا الخادم على المنفذ <code>4000</code>، وتظهر في صفحته الأولى عبارة الترحيب <code>!Hello, GraphQL</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_18" style=""><span class="kwd">import</span><span class="pln"> express from </span><span class="str">'express'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> cors from </span><span class="str">'cors'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> app </span><span class="pun">=</span><span class="pln"> express</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4000</span><span class="pln">

app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">cors</span><span class="pun">())</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="pln">urlencoded</span><span class="pun">({</span><span class="pln"> extended</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}))</span><span class="pln">

app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">'Hello, GraphQL!'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

app</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Running</span><span class="pln"> a server at http</span><span class="pun">:</span><span class="com">//localhost:${port}`)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	لاحظ أن الملف يبدأ بتعليمة استيراد الدالة <a href="https://expressjs.com/en/4x/api.html#express" rel="external nofollow"><code>express</code></a>، ثم بعض الإعدادات الخاصة بكل من CORS و JSON، وبعدها استعملنا <code>('/')app.get</code> لتحديد ما ينبغي إرساله إلى الجذر <code>/</code> ضمن طلبات <code>GET</code>، وفي نهاية الملف استخدمنا <code>()app.listen</code> لتحديد منفذ خادم <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<p>
	احفظ الآن التغيرات، وأغلق الملف، ثم نفذ الأمر التالي لتشغيل خادم Node:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_20" style=""><span class="pln">npm run dev</span></pre>

<p>
	تأكد -قبل الانتقال للخطوات التالية- من سلامة تشغيل الخادم بظهور صفحة الترحيب <code>!Hello, GraphQL</code> عند فتح الرابط "http://localhost:4000" من المتصفح أو تشغيل الأمر:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2502_26" style=""><span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:4000</span></pre>

<h2 id="graphqlhttp">
	إعداد البرمجية الوسيطة لخادم GraphQL HTTP
</h2>

<p>
	يوجد ثلاث خطوات أساسية لتحقيق التكامل بين مخطط GraphQL مع خادم إكسبريس، وتتلخص في إعداد الاتصال مع مخزن البيانات، وتعريف المخطط، وتعريف محللات الاستجابة.
</p>

<p>
	ستحتاج في البداية لتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2502_24" style=""><span class="pln">npm install graphql@14 express</span><span class="pun">-</span><span class="pln">graphql </span><span class="lit">@graphql</span><span class="pun">-</span><span class="pln">tools</span><span class="pun">/</span><span class="pln">schema</span></pre>

<p>
	الذي يثبت الحزم الضرورية للعمل مع GraphQL، وهي:
</p>

<ul>
	<li>
		<code>graphql</code>: الحزمة المرجعية التي تستخدمها لغة جافا سكربت لجعل التعامل مع GraphQL متاحًا في اللغة.
	</li>
	<li>
		<code>express-graphql</code>: البرمجية الوسيطة لخادم HTTP للتعامل مع GraphQL.
	</li>
	<li>
		<code>graphql-tools/schema@</code>: مجموعة أدوات تُسرّع تطوير GraphQL.
	</li>
</ul>

<p>
	ضِف تعليمات استيراد هذه الحزم إلى الملف "server.js"، لتصبح بدايته على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_28" style=""><span class="kwd">import</span><span class="pln"> express from </span><span class="str">'express'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> cors from </span><span class="str">'cors'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> graphqlHTTP </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'express-graphql'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> makeExecutableSchema </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@graphql-tools/schema'</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	لننجز الآن إعدادات الاتصال مع مخزن البيانات قبل إنشاء مخطط GraphQL القابل للتنفيذ.
</p>

<p>
	أنشئ الكائن <code>data</code> بإضافة التعليمات الخاصة به إلى الملف "server.js" مباشرةً بعد تعليمات الاستيراد كما هو مبين أدناه، إذ يمثل هذا الكائن مخزنًا للبيانات في الذاكرة in-memory store، ويحتوي مجموعة بيانات بسيطة لنستعلم عنها من خادم GraphQL. نخفف بهذه الطريقة عبء إنشاء قاعدة بيانات حقيقية والاتصال معها كوننا نجهز بيئة تعليمة بسيطة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_30" style=""><span class="kwd">import</span><span class="pln"> express from </span><span class="str">'express'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> cors from </span><span class="str">'cors'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> graphqlHTTP </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'express-graphql'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> makeExecutableSchema </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@graphql-tools/schema'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'001'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jaime'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'002'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jorah'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">…</span></pre>

<p>
	تمثّل هيكلة البيانات جدولًا بسيطًا يدعى <code>warriors</code>، الذي يحتوي سطرين <code>'Jaime'</code> و <code>'Jorah'</code> لبيانات اثنين من محاربي لعبة افتراضية سنستخدمها في مثالنا.
</p>

<p>
	<strong>ملاحظة:</strong> لا يغطي هذا المقال حالة اتصال خادم GraphQL بقاعدة بيانات حقيقية، وعادةً ما يستخدم خادم GraphQL <a href="https://academy.hsoub.com/programming/javascript/react/%D9%81%D9%87%D9%85-%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B2%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-reducers-%D9%81%D9%8A-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-redux-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-r1163/" rel="">دوال الاختزال reducers</a> للوصول إلى قواعد البيانات والتعديل عليها، ويعتمد تقنياتٍ مثل <a href="https://www.prisma.io/graphql" rel="external nofollow">Prisma</a>، وهي أداة <a href="https://academy.hsoub.com/programming/php/laravel/%D8%A7%D9%84%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D9%91%D8%A9-%D9%84%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-eloquent-orm-r410/" rel="">ربط العلاقات بالكائنات ORM</a>، أما إعدادات الاتصال مع قاعدة البيانات فيحملها المعامل <code>context</code> أحد معاملات <a href="https://graphql.org/learn/execution/#asynchronous-resolvers" rel="external nofollow">محللات الاستجابة غير المتزامنة</a>. سيمثل المتغير <code>data</code> إعدادات اتصالنا مع مخزن البيانات حتى نهاية هذا المقال.
</p>

<p>
	الآن، بعد تثبيت الحزم المطلوبة، وتجهيز بعض البيانات للاستعلام، يمكننا إنشاء المخطط الذي يُعرّف واجهة <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، ويصف ماهية البيانات الممكن الاستعلام عنها.
</p>

<h3 id="graphql">
	مخطط GraphQL
</h3>

<p>
	سننشئ مخططًا أوليًّا <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">لواجهة <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> يتضمن الحد الأدنى من التعليمات البرمجية التي تسمح لنا بالاتصال مع نقطة الوصول GraphQL Endpoint. سنفترض في هذا المثال أننا نستعلم عن بيانات لعبة مغامرات فيها أدوار لشخصيات مختلفة، مثل المحاربين والسحرة والمعالجين وغيرهم، لذا سنحرص عند إنشاء المخطط أن يكون مرنًا وقابلًا لإعادة الاستخدام، لتستطيع تكراره مع أي نوع من شخصيات اللعبة وتوسعته ليشمل متغيرات أخرى تخصهم مثل الأسلحة والتعويذات.
</p>

<p>
	تستند مخططات GraphQL إلى <a href="https://graphql.org/learn/schema/" rel="external nofollow">نظام الأنواع</a>، ويمكنك دائمًا إنشاء أنواعك الخاصة إذا لم تلبِ <a href="https://academy.hsoub.com/programming/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r1726/" rel="">الأنواع</a> الافتراضية المُضمّنة في النظام حاجتك، وهذا ما سنفعله هنا إذ سننشئ نوعًا <code>type</code> يدعى المحارب <code>warrior</code> له حقلين <code>id</code> و <code>name</code>.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2502_33" style=""><span class="pln">type </span><span class="typ">Warrior</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الحقل <code>id</code> نوعه <code>ID</code> رقم تعريفي، والحقل <code>name</code> نوعه سلسلة نصية <code>String</code>، وكلاهما من الأنواع الأولية المُضمّنة في نظام الأنواع، والأنواع الأولية تستخدم لإنشاء أنواع أعقد مثل الكائنات، أما إشارة التعجب <code>!</code> الواردة في التعليمات أعلاه فتشير لكون الحقل بجانبها لا يقبل قيمة فارغة "Null"، فلا يجب أن يكون فارغًا بأية حال.
</p>

<p>
	الآن بعد إنشاء نوع المحارب، سننشئ نوع الاستعلام <code>Query</code>، وهو نقطة الدخول entry point إلى استعلام GraphQL. ألقِ نظرةً على التعليمات المبينة أدناه، إذ يُعرّف هذا النوع المحاربين <code>warriors</code> على أنهم مصفوفة من البيانات التابعة لنوع المحارب <code>Warrior</code>.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2502_35" style=""><span class="pln">type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Warrior</span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أصبح مخططنا جاهزًا للاستخدام مع البرمجية الوسيطة GraphQL HTTP، لذا سنمرره للدالة <code>makeExecutableSchema</code>، إحدى أدوات <code>graphql-tools</code>، بصفته <code>typeDefs</code> ليصبح قابلًا للتنفيذ. للدالة <code>makeExecutableSchema</code> معاملان أحدهما للمخطط والآخر لمحللات الاستجابة، وينبغي تعريفها قبل تنفيذ الدالة:
</p>

<ul>
	<li>
		<code>typeDefs</code>: لغة مخطط GraphQL، وهي سلسلة نصية.
	</li>
	<li>
		<code>resolvers</code>: محللات الاستجابة، وهي الدوال التي تُستدعى لتنفيذ حقل معين وإرجاع قيمة.
	</li>
</ul>

<p>
	اكتب تعليمات تعريف <code>typeDefs</code> ضمن الملف "server.js" تمامًا بعد تعليمات استيراد الاعتماديات، وأسند له قيمة نصية هي مخطط GraphQL، وفق التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_37" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'001'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jaime'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'002'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jorah'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> typeDefs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">
type </span><span class="typ">Warrior</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Warrior</span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	إذًا، فقد جهزّنا إلى الآن البيانات التي سنستعلم عنها <code>data</code>، وعرّفنا المخطط ضمن <code>typeDefs</code>، وسنعرّف في الخطوة التالية محللات الاستجابة، التي ستحدد طريقة استجابة خادمنا للطلبات الواردة.
</p>

<h3 id="-1">
	محللات الاستجابة
</h3>

<p>
	<a href="https://graphql.org/learn/execution/#root-fields-resolvers" rel="external nofollow">محللات الاستجابة Resolvers</a> هي مجموعة من الدوال البرمجية التي توّلد استجابة خادم GraphQL، ولكل محلل استجابة أربعة معاملات:
</p>

<ul>
	<li>
		<code>obj</code>: الكائن الأب، غير ضروري في حالتنا لأن الكائن المستخدم لدينا وحيد وهو الأب أو الجذر.
	</li>
	<li>
		<code>args</code>: تمرر هنا أي متغيرات وسيطة يحتاجها GraphQL.
	</li>
	<li>
		<code>context</code>: حقل مشترك بين جميع محللات الاستجابة، ويُخصص غالبًا لمعلومات الاتصال مع قاعدة البيانات.
	</li>
	<li>
		<code>info</code>: <a href="https://github.com/graphql/graphql-js/blob/main/src/type/definition.ts#L977-L986" rel="external nofollow">معلومات إضافية</a>.
	</li>
</ul>

<p>
	سننشئ الآن محلل استجابة لاستعلام الجذر <code>Query</code>، يُرجع لطالب الاستعلام معلوماتٍ عن المحاربين <code>warriors</code>، وسنمرّر له مخزن البيانات في الذاكرة الذي عرّفناه سابقًا.
</p>

<p>
	ضِف ما يلي إلى الملف "server.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_39" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> typeDefs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">
type </span><span class="typ">Warrior</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Warrior</span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">obj</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</span><span class="pun">,</span><span class="pln"> info</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">warriors</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	أنشأنا بهذه التعليمات دالة محلل استجابة تدعى <code>warriors</code>، ترتبط بالاستعلام <code>Query</code> وهو نقطة دخولنا إلى خادم GraphQL، ومهمة هذه الدالة هي إرجاع بيانات المحاربين من جدول المحاربين <code>warriors</code> الموجود في قاعدة البيانات التي يشير إليها المعامل <code>context</code>، وهي في حالتنا مخزن البيانات البسيط <code>data</code>.
</p>

<p>
	إذًا، فلكل دالة محلل استجابة أربعة معاملات: <code>obj</code> و <code>args</code> و <code>info</code>، إضافةً إلى <code>context</code>، الذي كان أكثرها ارتباطًا بمخططنا، وهذا المعامل مشترك بين جميع محللات الاستجابة، ويشير غالبًا إلى إعدادات الاتصال بين خادم GraphQL و<a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">قاعدة البيانات</a>.
</p>

<p>
	الآن، بعد أن عرّفنا <code>typeDefs</code> و <code>resolvers</code> معاملات الدالة <code>makeExecutableSchema</code>، يمكننا تحويل المخطط إلى مخطط قابل للتنفيذ بكتابة الأوامر التالية في الملف "server.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_42" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">obj</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</span><span class="pun">,</span><span class="pln"> info</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">warriors</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> executableSchema </span><span class="pun">=</span><span class="pln"> makeExecutableSchema</span><span class="pun">({</span><span class="pln">
  typeDefs</span><span class="pun">,</span><span class="pln">
  resolvers</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	تنشئ الدالة <a href="https://the-guild.dev/graphql/tools/docs/generate-schema#makeexecutableschemaoptions" rel="external nofollow">makeExecutableSchema</a> مخططًا كاملًا يمكنك تمريره إلى نقطة وصل GraphQL.
</p>

<p>
	لنستبدل نقطة الوصل الحالية أي عبارة الترحيب <code>!Hello, GraphQL</code> بنقطة الوصول <code>graphql/</code> عبر كتابة التعليمات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_44" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> executableSchema </span><span class="pun">=</span><span class="pln"> makeExecutableSchema</span><span class="pun">({</span><span class="pln">
  typeDefs</span><span class="pun">,</span><span class="pln">
  resolvers</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">
  </span><span class="str">'/graphql'</span><span class="pun">,</span><span class="pln">
  graphqlHTTP</span><span class="pun">({</span><span class="pln">
    schema</span><span class="pun">:</span><span class="pln"> executableSchema</span><span class="pun">,</span><span class="pln">
    context</span><span class="pun">:</span><span class="pln"> data</span><span class="pun">,</span><span class="pln">
    graphiql</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">)</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	يتطلب استخدام البرمجية الوسيطة <code>graphqlHTTP</code> مخططًا أنشأناه وسياقًا يمثّل معلومات الاتصال مع مخزن البيانات، وينص الاتفاق على استخدام خادم GraphQL نقطة الوصول <code>graphql/</code>.
</p>

<p>
	إليك المحتوى النهائي للملف "server.js" بعد جميع التعديلات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2502_46" style=""><span class="kwd">import</span><span class="pln"> express from </span><span class="str">'express'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> cors from </span><span class="str">'cors'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> graphqlHTTP </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'express-graphql'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> makeExecutableSchema </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@graphql-tools/schema'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> app </span><span class="pun">=</span><span class="pln"> express</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4000</span><span class="pln">

</span><span class="com">// مخزن البيانات في الذاكرة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'001'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jaime'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'002'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jorah'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// المخطط</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> typeDefs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">
type </span><span class="typ">Warrior</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Warrior</span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="com">// محلل الاستجابة للمحاربين</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    warriors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">obj</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">warriors</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> executableSchema </span><span class="pun">=</span><span class="pln"> makeExecutableSchema</span><span class="pun">({</span><span class="pln">
  typeDefs</span><span class="pun">,</span><span class="pln">
  resolvers</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">cors</span><span class="pun">())</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="pln">urlencoded</span><span class="pun">({</span><span class="pln"> extended</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}))</span><span class="pln">

</span><span class="com">// نقطة الدخول</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">
  </span><span class="str">'/graphql'</span><span class="pun">,</span><span class="pln">
  graphqlHTTP</span><span class="pun">({</span><span class="pln">
    schema</span><span class="pun">:</span><span class="pln"> executableSchema</span><span class="pun">,</span><span class="pln">
    context</span><span class="pun">:</span><span class="pln"> data</span><span class="pun">,</span><span class="pln">
    graphiql</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">)</span><span class="pln">

app</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Running</span><span class="pln"> a server at http</span><span class="pun">:</span><span class="com">//localhost:${port}`)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	احفظ التغييرات على الملف وأغلقه.
</p>

<p>
	تكتمل بذلك واجهة برمجة التطبيقات، يمكنك الآن فتح الرابط "http://localhost:4000/graphql"، واستعراض مخطط GraphQL وتصحيح أخطائه باستخدام بيئة التطوير المتكاملة <a href="https://github.com/graphql/graphiql" rel="external nofollow">GraphiQL IDE</a>، وهو موضوع القسم التالي من المقال.
</p>

<h2 id="graphiqlide">
	استخدام GraphiQL IDE
</h2>

<p>
	الخطوة الأولى لاستخدام بيئة التطوير GraphiQL IDE هي تفعيلها بإسناد القيمة <code>true</code> للخيار <code>graphiql</code> في كتلة التعليمات الخاصة بالبرمجية الوسيطة ضمن ملف الخادم، ولو رجعت للتعليمات السابقة ستجدها مُفعلة.
</p>

<p>
	تعمل GraphiQL ضمن المتصفح، وتفيدك في كتابة استعلامات GraphQL، والتحقق من صحتها، واختبارها لضمان خرج صحيح من الخادم.
</p>

<p>
	تنقسم شاشة GraphiQL إلى قسمين، القسم الأيسر لكتابة التعليمات والأيمن لعرض النتائج. سنجري الآن استعلامًا عن المحاربين <code>warriors</code>، لذلك اطلب المُعرّف <code>id</code> والاسم <code>name</code> لكل محارب بكتابة التعليمات التالية في القسم الأيسر من الشاشة:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2502_48" style=""><span class="pun">{</span><span class="pln">
  warriors </span><span class="pun">{</span><span class="pln">
    id
    name
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	اضغط بعدها على الزر التشغيل أعلى يسار الشاشة، وستظهر نتيجة استعلامك في القسم الأيمن بصيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a>، وفق التالي:
</p>

<pre class="ipsCode" id="ips_uid_2502_58">Output{
  "data": {
    "warriors": [
      { "id": "001", "name": "Jaime" },
      { "id": "002", "name": "Jorah" }
    ]
  }
}</pre>

<p>
	حاول تعديل الاستعلام، استعلم مثلًا عن الاسم <code>name</code> فقط:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2502_52" style=""><span class="pun">{</span><span class="pln">
  warriors </span><span class="pun">{</span><span class="pln">
    name
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ولاحظ تغير النتيجة تبعًا لاستعلامك، فهي تشمل الآن أسماء المحاربين دون أرقامهم:
</p>

<pre class="ipsCode" id="ips_uid_2502_56">Output{
  "data": {
    "warriors": [{ "name": "Jaime" }, { "name": "Jorah" }]
  }
}</pre>

<p>
	تعكس هذه التجربة واحدةً من أقوى خصائص GraphQL، وهي أنها موجهة بطلبات العميل [Client-driven]()، إذ تسمح له بالاستعلام عن الحقول التي يحتاجها فقط، وتعرضها هي بالذات دون زيادة أو نقصان.
</p>

<p>
	تفيدك GraphiQL أيضًا باستعراض تفاصيل المخطط، فكل ما تحتاجه هو الضغط على زر التوثيق Docs في يمين الشاشة، وسيظهر لك شريط جانبي خاص بالتوثيقات.
</p>

<p>
	لننتقل إلى القسم الأخير من مقالنا، وفيه محاكاة لتقديم طلبات استعلام فعلية من العميل إلى واجهة GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<h2 id="graphqlapi">
	إرسال طلب استعلام لواجهة GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> من طرف العميل
</h2>

<p>
	يمكنك إرسال طلبات الاستعلام إلى واجهة GraphQL عبر بروتوكول HTTP بالطريقة نفسها المتبعة مع واجهات <a href="https://restfulapi.net/" rel="external nofollow">REST</a>، وتستطيع أيضًا استعمال واجهات APIs المُضمّنة في المتصفحات مع GraphQL، ومن أمثلتها <code>fetch</code> الخاصة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1297/" rel="">بجلب البيانات</a>.
</p>

<p>
	افتح الملف "index.html"، وأنشئ بداخله هيكل HTML مع <a href="https://wiki.hsoub.com/HTML/pre" rel="external">الوسم &lt;pre&gt;</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2502_60" style=""><span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1.0"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">

    </span><span class="tag">&lt;title&gt;</span><span class="pln">GraphQL Client</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">

  </span><span class="tag">&lt;pre&gt;</span><span class="com">&lt;!-- البيانات ستعرض هنا --&gt;</span><span class="tag">&lt;/pre&gt;</span><span class="pln">

  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;script&gt;</span><span class="pln">
      </span><span class="com">// Add query here</span><span class="pln">
    </span><span class="tag">&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	ثم أنشئ دالة غير متزامنة تحت الوسم <code>script</code>، ترسل طلب <code>POST</code> لواجهة GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2502_63" style=""><span class="pln">...
</span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;script&gt;</span><span class="pln">
      </span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> queryGraphQLServer</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> fetch</span><span class="pun">(</span><span class="str">'http://localhost:4000/graphql'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
          headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          body</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">
            query</span><span class="pun">:</span><span class="pln"> </span><span class="str">'{ warriors { name } }'</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">}),</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">()</span><span class="pln">

        </span><span class="com">// إضافة البيانات إلى الوسم</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> pre </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'pre'</span><span class="pun">)</span><span class="pln">
        pre</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="com">// JSON بصيغة</span><span class="pln">

      </span><span class="pun">}</span><span class="pln">

      queryGraphQLServer</span><span class="pun">()</span><span class="pln">
    </span><span class="tag">&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
...</span></pre>

<p>
	أسند القيمة <code>application/json</code> للحقل <code>Content-Type</code> في ترويسة المقطع <code>script</code>، واكتب الاستعلام الذي تطلبه بصيغة سلسلة نصية ضمن المتن، فيكون تسلسل الإجراءات كما يلي: يستدعي <code>script</code> الدالة غير المتزامنة، فترسل طلبًا للخادم، وعند ورود الاستجابة توضع تحت الوسم <code>pre</code>.
</p>

<p>
	إليك المحتوى النهائي للملف "index.html":
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2502_65" style=""><span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1.0"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">

    </span><span class="tag">&lt;title&gt;</span><span class="pln">GraphQL</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">

  </span><span class="tag">&lt;pre&gt;&lt;/pre&gt;</span><span class="pln">

  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;script&gt;</span><span class="pln">
      </span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> queryGraphQLServer</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> fetch</span><span class="pun">(</span><span class="str">'http://localhost:4000/graphql'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
          headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          body</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">
            query</span><span class="pun">:</span><span class="pln"> </span><span class="str">'{ warriors { name } }'</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">}),</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">()</span><span class="pln">

        </span><span class="kwd">const</span><span class="pln"> pre </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'pre'</span><span class="pun">)</span><span class="pln">
        pre</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="com">// Pretty-print the JSON</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      queryGraphQLServer</span><span class="pun">()</span><span class="pln">
    </span><span class="tag">&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	احفظ التغييرات، وأغلق الملف "index.html"، ثم استعرضه بواسطة المتصفح، وافتح نافذة الشبكة Network ضمن أدوات المطور لترى الطلب المرسل عبر الشبكة من المتصفح إلى نقطة وصول GraphQL ذات العنوان "http://localhost:4000/graphql"، وافحص بعدها استجابة الخادم؛ فإذا تضمنت البيانات التي استعلمت عنها مع رمز الاستجابة <code>200</code>، فاستعلامك صحيح وقد نجحت بإنشاء خادم GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<h2 id="-2">
	الخاتمة
</h2>

<p>
	تناول هذا المقال إعداد خادم GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> في بيئة Node.js باستخدام إطار العمل Express بطريقة مبسطة تعينك على فهم أساسيات GraphQL، وأبرزها أن الخادم يتكون من نقطة وصول وحيدة <code>graphql/</code> تستقبل جميع طلبات الاستعلام، وواجهة برمجة التطبيقات تحتوي دومًا مخطط ومحلل استجابة. يتضمن المخطط نوعين، نوعًا أساسيًا للاستعلام <code>Query</code>، ونوعًا مخصصًا تُعرّفه حسب حالتك، وفي مثالنا كان نوع المحارب <code>Warrior</code>، ومهمة محلل الاستجابة جلب البيانات المناسبة لهذه الأنواع من مخزن البيانات.
</p>

<p>
	نأمل أننا ساعدناك في خطوتك الأولى مع GraphQL، وننصحك بتوسيع بحثك عنها لتتعرف على خياراتها المتقدمة ومكملاتها، مثل أدوات المصادقة والأمان والتخبئة caching، لتزيد فعاليتها في تطبيقاتك.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-graphql-api-server-in-node-js" rel="external nofollow">How To Set Up a GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> Server in Node.js</a> لصاحبته Tania Rascia.
</p>

<h2 id="-3">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-graphql-r2208/" rel="">مقدمة إلى GraphQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%88%D9%84-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D9%88%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87-r1711/" rel="">كتابة أول برنامج في بيئة Node.js وتنفيذه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-graphql-%D9%88%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r1208/" rel="">مدخل إلى المكتبة GraphQL واستعمالاتها في بناء تطبيقات الويب الحديثة</a>
	</li>
	<li>
		النسخة الكاملة لكتاب <a href="https://academy.hsoub.com/files/32-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-nodejs/" rel="">البرمجة باستخدام Node.js </a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2189</guid><pubDate>Wed, 06 Dec 2023 16:01:00 +0000</pubDate></item><item><title>&#x628;&#x631;&#x645;&#x62C;&#x629; &#x62A;&#x637;&#x628;&#x64A;&#x642; '&#x623;&#x644;&#x647;&#x645;&#x646;&#x64A;' &#x644;&#x639;&#x631;&#x636; &#x627;&#x644;&#x646;&#x635;&#x627;&#x626;&#x62D; &#x648;&#x627;&#x644;&#x62D;&#x643;&#x645; &#x627;&#x644;&#x645;&#x641;&#x64A;&#x62F;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; ChatGPT &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A3%D9%84%D9%87%D9%85%D9%86%D9%8A-%D9%84%D8%B9%D8%B1%D8%B6-%D8%A7%D9%84%D9%86%D8%B5%D8%A7%D8%A6%D8%AD-%D9%88%D8%A7%D9%84%D8%AD%D9%83%D9%85-%D8%A7%D9%84%D9%85%D9%81%D9%8A%D8%AF%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%81%D9%8A-nodejs-r2025/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/--------ChatGPT--.png.4a41789c3b3fc5c711c7b9a54762d98d.png" /></p>
<p>
	نماذج اللغة الكبيرة (أو Large Language Models) هي نوع من <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/" rel="">نماذج الذكاء الاصطناعي</a> التي تُدرّب على كمية كبيرة من النصوص لتستطيع بذلك توليد نصوص مشابهة لها، وهنا يأتي <a href="https://academy.hsoub.com/apps/web/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D9%88%D8%AA-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%AF%D8%AB%D8%A9-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D8%B4%D8%A7%D8%AA-%D8%AC%D9%8A-%D8%A8%D9%8A-%D8%AA%D9%8A-chatgpt-r863/" rel="">ChatGPT</a> وهو نموذج لغة كبير دُرّب على محتوى كبير جدًا من الانترنت، بحيث أصبح لديه اطلاع على الكثير من المعلومات واللغات وأساليب الكتابة، وطوّر باستخدام بعض التقنيات أهمها تقنية التعلم الموجه من قبل البشر RLHF (أو Reinforcement learning from human feedback) ليتّبع في توليده للنصوص اسلوب الحوار بين المستخدم والنموذج، مما يبسط طريقة التعامل معه وتوجيهه لتوليد النصوص التي نحتاحها، وفي هذا المقال سنتعامل مع ذلك النموذج لتوليد نصائح وحكم مفيدة للمستخدم بحسب ما يطلب أو يشعر، وذلك عبر واجهة سطر الأوامر نطورها ضمن بيئة نود (Node.js) حيث يُدخل المستخدم موضوع النصيحة أو الهدف منها ويولد ChatGPT ذلك، وستحتاج لاتباع المقال معرفة بلغة جافاسكربت وبيئة نود ومدير الحزم npm.
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" src="https://academy.hsoub.com/files/32-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-nodejs/?do=embed" style="margin: auto;"></iframe>

<h2>
	إنشاء برنامج سطر أوامر باستخدام npm
</h2>

<p>
	كي نتمكن من تنفيذ التطبيق بسهولة من سطر الأوامر كأي برنامج آخر مثبت على الجهاز وبدلًا من تشغيله كل مرة باستخدام <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">نود Node.js</a> والإشارة إلى مسار ملف جافاسكربت الرئيسي للبرنامج يمكننا الاستفادة من ميزة في npm تمكننا من تحديد ملف جافاسكربت من حزمة ما كالمشروع الذي سنطوره ونشره ضمن الجهاز كبرنامج ضمن سطر الأوامر، حيث يُستخدم مدير الحزم npm عادة لتعريف البرامج في نود كحزم يمكن استخدامها ضمن المشاريع الأخرى، ولنبدأ أولًا بإنشاء مجلد جديد لملفات المشروع وليكن بالاسم <code>inspire-cli</code> ونعرفه كحزمة نود بتنفيذ الأمر التالي ضمن المجلد:
</p>

<pre class="ipsCode">npm init -y
</pre>

<p>
	سينشئ عن تنفيذ ذلك الأمر ملف تعريف الحزمة <code>package.json</code> ضمن المجلد، وباستخدام الخيار <code>y-</code> ستُملئ القيم الافتراضية ضمنه دون الحاجة لإدخالها يدويًا، والآن نُنشئ ملف البرنامج الرئيسي <code>index.js</code> والذي سيحتوي شيفرة البرنامج، ولتعريفه كبرنامج <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر أوامر</a> يمكن بداخل ملف تعريف الحزمة <code>package.json</code> وضمن المفتاح <code>bin</code> تعريف كائن مفاتيحه هي أسماء برامج سطر الأوامر التي توفرها هذه الحزمة، وقيمها هي مسار الملفات التي ستُنفذ باستخدام نود عند تنفيذ البرنامج، لتطبيقنا سنعرف برنامج بالاسم <code>ألهمني</code> ومسار ملف البرنامج له هو الملف الرئيسي <code>index.js</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_7" style=""><span class="str">"bin"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"ألهمني"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وضمن ملف <a href="https://academy.hsoub.com/javascript/" rel="">جافاسكربت</a> الرئيسي لذلك البرنامج index.js نضيف السطر التالي لاختبار نجاح التنفيذ، ونلاحظ سطر البداية وهو سطر يدل <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%88-yarn-r1597/" rel="">مدير الحزم npm</a> على الطريقة الصحيحة لإنشاء الملف التنفيذي لبرنامج سطر الأوامر المقابل لهذا الملف لتحديد بيئة تشغيله وهي نود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_9" style=""><span class="pun">#!</span><span class="str">/usr/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">env node

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'مرحبًا'</span><span class="pun">)</span></pre>

<p>
	وبذلك يمكن لأي مستخدم بعد تثبيت هذا المشروع كحزمة على جهازه تنفيذ الأمر باستخدام اسم البرنامج المُعرف ضمن الكائن <code>bin</code> ليُنفذ البرنامج index.js في بيئة نود ويظهر له النتيجة، ولاختباره أثناء التطوير يمكن الاستفادة من أمر الربط <code>link</code> من مدير الحزم npm والذي يربط الحزمة ضمن البيئة العامة للجهاز لنتمكن من استخدامها من أي مكان، لذا ننفذ الأمر التالي ضمن مجلد المشروع:
</p>

<pre class="ipsCode">npm link .
</pre>

<p>
	تدل النقطة على المشروع الذي نرغب بربطه وتعني المشروع في المسار الحالي، وبعد تنفيذ هذا الأمر أصبح الوصول لبرنامج سطر الأوامر ممكنًا من أي مكان لنختبر ذلك بتنفيذ الأمر التالي ضمن أي <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B7%D8%B1%D9%81%D9%8A%D9%91%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-terminal-r18/" rel="">طرفية terminal</a> لنلاحظ خرج البرنامج كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130439" href="https://academy.hsoub.com/uploads/monthly_2023_07/cli-bin.png.c9698399e171da64203f5bb7865ca43e.png" rel=""><img alt="cli-bin.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130439" data-unique="x8ijn76s8" src="https://academy.hsoub.com/uploads/monthly_2023_07/cli-bin.png.c9698399e171da64203f5bb7865ca43e.png"> </a>
</p>

<p>
	أصبح برنامج سطر الأوامر جاهزًا وسنبدأ في الفقرة التالية بتطويره ليستقبل الدخل من المستخدم عبر معاملات سطر الأوامر المُمررة له.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة JavaScript
		</p>

		<p class="banner-subtitle">
			تعلم البرمجة بلغة جافا سكريبت انطلاقًا من أبسط المفاهيم وحتى بناء تطبيقات حقيقية.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/javascript-application-development/" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة JavaScript" src="https://academy.hsoub.com/learn/assets/images/courses/javascript-application-development.png">
	</div>
</div>

<h2>
	استقبال الدخل من المستخدم
</h2>

<p>
	نحتاج أن يدخل المستخدم موضوع النصيحة أو الحكمة التي يرغب بها، ويمكن استقبال ذلك عبر معاملات سطر الأوامر التي يمررها المستخدم عند تنفيذ البرنامج، ويمكن الوصول إليها في نود عبر المتغير العام <code>process.argv</code> وهو مصفوفة من القيم أول قيمة منه هي مسار البرنامج التنفيذي لبيئة نود التي ستُنفذ البرنامج، والقيمة الثانية هي مسار ملف البرنامج الحالي الذي سيُنفذ، والقيم الباقية هي ما تُهمنا وهي جميع المعاملات المُمررة للبرنامج من قبل المستخدم عند تنفيذه، حيث سنجمعها معًا لنكون منها الجملة التي أدخلها المستخدم لتحضيرها قبل إرسالها، لذا نعدل ملف البرنامج <code>index.js</code> ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_11" style=""><span class="pun">#!</span><span class="str">/usr/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">env node

</span><span class="kwd">const</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> input </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">' '</span><span class="pun">)</span></pre>

<p>
	حيث باستخدام التابع <code>slice</code> على مصفوفة المعاملات المُمررة للبرنامج يمكننا تجاهل أول عنصرين منها واستخراج المعاملات المُمررة فقط، وباستخدام التابع <code>join</code> نجمع تلك الكلمات ونفصل بينها بفراغات لتكون قيمة الثابت <code>input</code> هي جملة الدخل من المستخدم، وفي الفقرة التالية سنُرسل تلك الجملة إلى نموذج ChatGPT لتوليد النصيحة المطلوبة.
</p>

<h2>
	إرسال الدخل إلى ChatGPT وتوليد النصيحة
</h2>

<p>
	لنتمكن من التعامل مع نموذج ChatGPT عبر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> له يجب إنشاء حساب جديد ضمن المنصة المالكة له OpenAI وإعداد الحساب كحساب مدفوع وإدخال وسيلة الدفع، بعد ذلك يمكن توليد مفتاح للتعامل مع الواجهة البرمجية نتأكد من نسخه والاحتفاظ به، وننفذ الأمر التالي مع تبديل قيمة المفتاح لتخزينه ضمن متغيرات البيئة لجلبه ضمن البرنامج لاحقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_13" style=""><span class="kwd">set</span><span class="pln"> OPENAI_API_KEY</span><span class="pun">=&lt;قيمة</span><span class="pln"> </span><span class="pun">المفتاح&gt;</span></pre>

<p>
	وضمن نظام ماك أو لينكس يمكن تنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_15" style=""><span class="kwd">export</span><span class="pln"> OPENAI_API_KEY</span><span class="pun">=&lt;قيمة</span><span class="pln"> </span><span class="pun">المفتاح&gt;</span></pre>

<p>
	وبذلك عند تنفيذ البرنامج من نفس هذه الطرفية سيتمكن البرنامج من الوصول لقيمة ذلك المفتاح دون الحاجة لكتابته ضمن الشيفرة المصدرية، والآن لنرسل دخل المستخدم ونطلب من نموذج ChatGPT الرد المناسب ويمكن ذلك بحسب <a href="https://platform.openai.com/docs/api-reference/chat" rel="external nofollow">التوثيق الرسمي</a> لواجهة النموذج البرمجية بإرسال طلب HTTP من نوع POST إلى مسار الواجهة البرمجية <a href="https://api.openai.com/v1/chat/completions" ipsnoembed="false" rel="external nofollow">https://api.openai.com/v1/chat/completions</a> ونضع مفتاح الواجهة ضمن الترويسة Authorization وقيمتها الكلمة Bearer ثم مسافة ثم قيمة المفتاح، وضمن جسم الطلب نحدد كقيمة للمفتاح <code>model</code> اسم النموذج الذي نرغب بالتعامل معه وهو gpt-3.5-turbo وهو الاسم التقني لنموذج ChatGPT بالإصدار الثالث، ونضيف كقيمة للمفتاح <code>messages</code> مصفوفة من الكائنات المُعبرة عن الرسائل المُرسلة للنموذج نرسل ضمنها رسالة المستخدم وهو الدخل المُمرر للبرنامج، ويمكننا توجيه النموذج لشكل الخرج الذي نريده عبر تشكيل رسالة المستخدم لتصبح بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_17" style=""><span class="kwd">const</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`ألهمني</span><span class="pln"> </span><span class="pun">بعبارة</span><span class="pln"> </span><span class="pun">مختصرة</span><span class="pln"> </span><span class="pun">عن</span><span class="pln"> </span><span class="pun">الموضوع</span><span class="pln"> </span><span class="pun">التالي:</span><span class="pln">  $</span><span class="pun">{</span><span class="pln">input</span><span class="pun">}`</span></pre>

<p>
	وبذلك نرشد النموذج داخل البرنامج لطبيعة الرد الذي نريده منه دون أن يحتاج المستخدم سوى لإدخال الموضوع الذي يريده للبرنامج، وباستخدام <code>fetch</code> يمكننا إرسال طلب HTTP للواجهة البرمجية للنموذج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_19" style=""><span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://api.openai.com/v1/chat/completions'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
    headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
        </span><span class="str">'Authorization'</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Bearer</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">proccess</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">OPENAI_API_KEY</span><span class="pun">}`,</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    body</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">
        </span><span class="str">"model"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"gpt-3.5-turbo"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"messages"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[{</span><span class="pln"> </span><span class="str">"role"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"user"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"content"</span><span class="pun">:</span><span class="pln"> message </span><span class="pun">}]</span><span class="pln">
    </span><span class="pun">}),</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	حيث يعيد التابع <code>fetch</code> وعدًا (أو Promise) يمكننا استخدام التابع <code>then</code> للحصول على الرد، ونحول الرد القادم إلى صيغة json ثم نستخرج منها محتوى جواب النموذج على الرسالة المرسلة من قبل المستخدم وهي ضمن القيمة <code>json.choices[0].message.content</code> من رد json المرسل ونعرضها له على الشاشة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8195_21" style=""><span class="pln">fetch</span><span class="pun">(...)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">r </span><span class="pun">=&gt;</span><span class="pln"> r</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">json </span><span class="pun">=&gt;</span><span class="pln"> json</span><span class="pun">.</span><span class="pln">choices</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">.</span><span class="pln">content</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">gpt_response </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">gpt_response</span><span class="pun">))</span></pre>

<p>
	وبذلك يصبح البرنامج جاهزًا لنختبره في الفقرة التالية لتوليد بعض النصائح المفيدة.
</p>

<h2>
	اختبار البرنامج
</h2>

<p>
	ضمن سطر الأوامر وبما أننا عرفنا اسم البرنامج بالاسم <code>ألهمني</code> يصبح تنفيذ البرنامج أشبه بكتابة جملة معبرة باللغة العربية، وفي كل مرة سيُولد النموذج نصيحة مختلفة بحسب الموضوع المحدد، فمثلًا لنرى ما لديه من نصائح حول النجاح بتنفيذ الأمر <code>ألهمني عن النجاح</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130438" href="https://academy.hsoub.com/uploads/monthly_2023_07/result-success.png.daad1a456e48f484ead5ce34e297dbec.png" rel=""><img alt="result-success.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130438" data-unique="45ad61lm8" src="https://academy.hsoub.com/uploads/monthly_2023_07/result-success.png.daad1a456e48f484ead5ce34e297dbec.png"> </a>
</p>

<p>
	النجاح هو النتيجة الإيجابية للجهود المستمرة والتفاني في العمل لتحقيق الأهداف.
</p>

<p>
	لنرى ما سينصح به شخص يشعر بالتعب بتنفيذ الأمر <code>ألهمني أشعر بالتعب</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130437" href="https://academy.hsoub.com/uploads/monthly_2023_07/result-tired.png.af9698a820fa67f983886df9bbaadcbb.png" rel=""><img alt="result-tired.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130437" data-unique="tysnmbhh2" src="https://academy.hsoub.com/uploads/monthly_2023_07/result-tired.png.af9698a820fa67f983886df9bbaadcbb.png"> </a>
</p>

<p>
	استرح قليلاً، واشرب ماء بكميات كافية، ومارس التمارين الرياضية بانتظام.
</p>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130436" href="https://academy.hsoub.com/uploads/monthly_2023_07/result-tired2.png.21ad1c379878cb34ee8d685de578ae88.png" rel=""><img alt="result-tired2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130436" data-unique="ch1a3d0lj" src="https://academy.hsoub.com/uploads/monthly_2023_07/result-tired2.png.21ad1c379878cb34ee8d685de578ae88.png"> </a>
</p>

<p>
	لا تستسلم للتعب، النجاح يتطلب الجهد والإصرار.
</p>

<p>
	لاحظ سهولة التعامل مع هذه النماذج باستخدام اللغة الطبيعية، مما يفتح الاحتمالات الواسعة لتطوير العديد من التطبيقات المفيدة والأفكار الرائعة التي تضيف للمستخدم قيمة مفيدة أو توفر الوقت والجهد في بعض الأحيان، حيث يمكن التطوير على التطبيق الذي طورناه في هذا المقال بالتعديل على التعليمة المرسلة للنموذج لتغيير النتيجة التي يولدها أو اللهجة التي يتكلم بها أو حتى أن يرد بلغة مختلفة أو أي أفكار أخرى تخطر في ذهنك يمكنك مشاركتها في التعليقات ومشاركة النتائج التي حصلت عليها بعد تجربتك تطبيقها.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة الذكاء الاصطناعي
		</p>

		<p class="banner-subtitle">
			احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/artificial-intelligence" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<a href="https://academy.hsoub.com/learn/artificial-intelligence" rel=""><img alt="دورة الذكاء الاصطناعي AI" src="https://academy.hsoub.com/learn/assets/images/courses/artificial-intelligence.png"></a>
	</div>
</div>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%84%D8%AE%D8%B5%D9%84%D9%8A-%D9%84%D8%AA%D9%84%D8%AE%D9%8A%D8%B5-%D8%A7%D9%84%D9%85%D9%82%D8%A7%D9%84%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%88%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%84-r2008/" rel="">برمجة تطبيق 'لخصلي' لتلخيص المقالات باستخدام ChatGPT ولارافل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D8%B5%D9%81%D8%A9-%D9%84%D8%A7%D9%82%D8%AA%D8%B1%D8%A7%D8%AD-%D8%A7%D9%84%D9%88%D8%AC%D8%A8%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%88-dall-e-%D9%81%D9%8A-php-r2005/" rel="">تطوير تطبيق 'وصفة' لاقتراح الوجبات باستخدام ChatGPT و DALL-E في PHP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D8%AE%D8%AA%D8%A8%D8%B1%D9%86%D9%8A-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%88%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%85%D8%B9-nodejs-r2018/" rel="">تطوير تطبيق 'اختبرني' باستخدام ChatGPT ولغة جافاسكربت مع Node.js</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2025</guid><pubDate>Thu, 13 Jul 2023 13:06:00 +0000</pubDate></item><item><title>&#x62A;&#x637;&#x648;&#x64A;&#x631; &#x62A;&#x637;&#x628;&#x64A;&#x642; '&#x627;&#x62E;&#x62A;&#x628;&#x631;&#x646;&#x64A;' &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; ChatGPT &#x648;&#x644;&#x63A;&#x629; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x645;&#x639; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D8%AE%D8%AA%D8%A8%D8%B1%D9%86%D9%8A-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%88%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%85%D8%B9-nodejs-r2018/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/------ChatGPT--.png.dae2987dde122398d34e64fa1321e4d7.png" /></p>
<p>
	تتميز نماذج اللغة الكبيرة أو Large Language Models بقدرتها على فهم اللغة الطبيعية وتوليد النصوص الصحيحة إملائيًا والصحيحة بمعناها أيضًا، مما يسمح بالتعامل مع نماذج الذكاء الاصناعي تلك بسهولة وبساطة من خلال اللغة الطبيعية، ومن تلك النماذج نموذج ChatGPT الشهير من شركة OpenAI والذي سنستفيد من مزاياه تلك والمعلومات الكامنة ضمنه والتي اكتسبها أثناء مرحلة تدريبه على البيانات النصية المتوفرة على الإنترنت في هذا المقال لتوليد أسئلة للمستخدم عن موضوع معين يطلبه المستخدم، ثم عرض الأجوبة التي يُدخلها المستخدم على النموذج لتحديد مستوى فهمه لذلك الموضوع وحتى تصحيح أجوبته إن كانت خاطئة، سنلاحظ أيضًا الطيف الواسع من الاحتمالات الممكنة عند التعامل مع ذلك النموذج، من توليد المحتوى إلى طلب البيانات بشكل وتنسيق محدد يسهل تكامله مع التطبيق.
</p>

<h2>
	تجهيز المشروع وإعداد الصفحات
</h2>

<p>
	نبدأ بإنشاء مجلد جديد للمشروع وننفذ الأمر التالي بداخله والذي سيُعرّف المشروع كحزمة وتوليد ملف توصيف المشروع package.json والذي يمكن تعريف بعض الأوامر الخاصة بالمشروع ضمنه وتحديد الاعتماديات التي يريدها:
</p>

<pre class="ipsCode">npm init -y
</pre>

<p>
	نثبت الاعتماديات التي نحتاجها لبناء المشروع وهي مكتبة express لإنشاء خادم الويب للتطبيق والمكتبة ejs لبناء قوالب HTML لصفحات المشروع، ومكتبة openai للتعامل مع الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لنموذج <a href="https://academy.hsoub.com/apps/web/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D9%88%D8%AA-%D8%A7%D9%84%D9%85%D8%AD%D8%A7%D8%AF%D8%AB%D8%A9-%D8%A7%D9%84%D8%B0%D9%83%D9%8A-%D8%B4%D8%A7%D8%AA-%D8%AC%D9%8A-%D8%A8%D9%8A-%D8%AA%D9%8A-chatgpt-r863/" rel="">ChatGPT</a> ومكتبة dotenv والتي سنحتاجها لتحميل متغيرات البيئة من ملف env. للمشروع، وذلك بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode">npm install express ejs openai dotenv
</pre>

<p>
	لنبدأ ببناء <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم الويب</a> وتجهيز الصفحات الأساسية للتطبيق وهي ثلاثة صفحات صفحة يدخل فيها المستخدم موضوع الاختبار، وصفحة لعرض الاختبار وإدخال الأجوبة، وصفحة عرض النتيجة، ونولد أولًا تطبيق جديد باستدعاء تابع مكتبة <a href="https://academy.hsoub.com/programming/javascript/nodejs/express/" rel="">Express</a> ونضبط إعداداته لاستخدام <a href="https://academy.hsoub.com/questions/16497-%D8%A5%D8%B8%D9%87%D8%A7%D8%B1-%D8%B5%D9%81%D8%AD%D8%A9-html-%D8%B6%D9%85%D9%86-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-expressjs-%D9%81%D9%8A-nodejs/" rel="">محرك القوالب ejs</a> كمحرك توليد ملفات العرض وتخديم الملفات الثابتة العامة من المجلد public ضمن المشروع، واستقبال الدخل المرسل من نموذج HTML والتعامل معه، وذلك ضمن الملف bin/www ونضيف له ترويسة تحديد بيئة التشغيل لذلك الملف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_6" style=""><span class="pun">#!</span><span class="str">/usr/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">env node
</span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"path"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> express </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"express"</span><span class="pun">);</span><span class="pln">

</span><span class="com">// إنشاء تطبيق جديد</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> app </span><span class="pun">=</span><span class="pln"> express</span><span class="pun">();</span><span class="pln">
</span><span class="com">// ضبط محرك توليد ملفات العرض</span><span class="pln">
app</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'view engine'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ejs'</span><span class="pun">);</span><span class="pln">
</span><span class="com">//إعداد تخديم الملفات العامة</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="kwd">static</span><span class="pun">(</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'../public'</span><span class="pun">)));</span><span class="pln">
</span><span class="com">// ضبط استقبال المدخلات</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="pln">urlencoded</span><span class="pun">({</span><span class="pln"> extended</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}))</span></pre>

<p>
	نُعرّف ضمن متغير التطبيق <code>app</code> ثلاث مسارات وهي المسار الرئيسي <code>/</code> بالطريقة GET لعرض الصفحة الرئيسية، والمسار <code>test/</code> لعرض الاختبار، والمسار <code>result/</code> لعرض نتيجة الاختبار، وضمن كل منها نٌصيّر ونعيد صفحة القالب المقابلة لذلك المسار باستخدام التابع <code>render</code> من كائن الطلب <code>req</code> المُمرر لتابع معالجة الطلب لكل مسار، ليكون تعريف المسارات كالتالي مع تمرير بعض البيانات الوهمية لاختبار الصفحات والتي سنستبدلها لاحقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_8" style=""><span class="com">// الصفحة الرئيسية</span><span class="pln">
app</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="str">'index'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="com">// صفحة الاختبار</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/test'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
        questions</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="str">'السؤال الأول'</span><span class="pun">,</span><span class="pln">
            </span><span class="str">'السؤال الثاني'</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">]</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="com">// صفحة عرض النتيجة</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/result'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="str">'result'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        results</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                question</span><span class="pun">:</span><span class="pln"> </span><span class="str">'السؤال الأول'</span><span class="pun">,</span><span class="pln">
                answer</span><span class="pun">:</span><span class="pln"> </span><span class="str">'جواب السؤال الأول'</span><span class="pun">,</span><span class="pln">
                correct</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
                note</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
            </span><span class="pun">{</span><span class="pln">
                question</span><span class="pun">:</span><span class="pln"> </span><span class="str">'السؤال الثاني'</span><span class="pun">,</span><span class="pln">
                answer</span><span class="pun">:</span><span class="pln"> </span><span class="str">'جواب السؤال الثاني'</span><span class="pun">,</span><span class="pln">
                correct</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
                note</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ملاحظة حول الإجابة الخاطئة'</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">],</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	ملفات قوالب صفحات العرض مكانها الافتراضي ضمن المجلد views، وبما أننا نستخدم محرك ejs يجب أن تنتهي جميع تلك الملفات باللاحقة ejs.، ونربط ضمنها ملف التنسيقات style.css والذي يمكننا إنشاءه في المسار public\style.css، والصفحات هي الصفحة الرئيسية views\index.ejs وتحوي حقل لإدخال موضوع الاختبار:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6348_10" style=""><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"ar"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"style.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">اختبرني</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln"><span class="ipsEmoji">?</span></span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    </span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"test"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"post"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"subject"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"أدخل موضوع الاختبار"</span><span class="tag">/&gt;</span><span class="pln">
      </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="tag">&gt;</span><span class="pln">اختبرني</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;/form&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	والصفحة الثانية لعرض الأسئلة المقترحة، حيث نمرر لقالب العرض القيمة <code>questions</code> والتي تحوي الأسئلة كمصفوفة سلاسل نصية، نمر عليها ضمن القالب ونعرض لكل منها نص السؤال ثم حقل مخفي يحوي نص السؤال لإرساله مع حقل الجواب المُدخل من المستخدم، والبيانات ضمن النموذج المُرسل ستحوي على القيمة answers وهي مصفوفة من الكائنات الذي يُمثل كل منها السؤال question مع الجواب answer المُدخل من قبل المستخدم لتكون من الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_12" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"answers"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> </span><span class="str">"question"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"..."</span><span class="pun">,</span><span class="pln"> </span><span class="str">"answer"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">},</span><span class="pln">
    </span><span class="pun">…</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستعمل صيغة الأقواس المربعة <code>‎[question][index]answers</code> و <code>‎[answer][index]answers</code> كأسماء لتلك الحقول ليتولى express ترجمتها إلى الكائن <code>answers</code> وقيمته مصفوفة من كائنات يحوي كل منها الحقل <code>question</code> لنص السؤال والحقل <code>answer</code> للجواب المقابل لذلك السؤال، ليكون ملف قالب صفحة الأسئلة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6348_14" style=""><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"ar"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"style.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">اختبرني | الاختبار</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"result"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"post"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="pun">&lt;%</span><span class="pln"> questions</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">question</span><span class="pun">,</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> %&gt;
      </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">dir</span><span class="pun">=</span><span class="atv">"auto"</span><span class="tag">&gt;</span><span class="pun">&lt;%-</span><span class="pln"> question %&gt;</span><span class="tag">&lt;/p&gt;</span><span class="pln">
      &lt;input type="hidden" name="answers[</span><span class="pun">&lt;%=</span><span class="pln"> index %&gt;][question]" value="</span><span class="pun">&lt;%-</span><span class="pln"> question %&gt;"
      /&gt;
      &lt;input type="text" name="answers[</span><span class="pun">&lt;%=</span><span class="pln"> index %&gt;][answer]" /&gt;
      </span><span class="pun">&lt;%</span><span class="pln"> </span><span class="pun">})</span><span class="pln"> %&gt;
      </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="tag">&gt;</span><span class="pln">عرض النتيجة</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;/form&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	والصفحة الأخيرة لعرض النتيجة، حيث سنمرر لقالب العرض القيمة <code>results</code> وهي مصفوفة من كائنات نتائج الأسئلة، يحوي كل منها على الحقل <code>question</code> لنص السؤال والحقل <code>answer</code> للجواب المدخل من قبل المستخدم سنضعه كقيمة لحقل <code>input</code> مع الخاصية <code>readonly</code> لمنع تعديله، والحقل المنطقي <code>correct</code> يعبر عما إذا كان الجواب صحيحًا أم لا، وأخيرًا الحقل <code>note</code> والذي سنعرضه في حال كان الجواب خاطئًا ويحوي على ملاحظة من قبل ChatGPT عن الإجابة الصحيحة، وأخيرًا رابط للصفحة الرئيسية لطلب اختبار جديد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6348_16" style=""><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"ar"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"style.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">اختبرني | النتيجة</span><span class="tag">&lt;/title&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="pun">&lt;%</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> %&gt;
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"result"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">dir</span><span class="pun">=</span><span class="atv">"auto"</span><span class="tag">&gt;</span><span class="pun">&lt;%-</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">question %&gt;</span><span class="tag">&lt;/p&gt;</span><span class="pln">
      &lt;input type="text" value="</span><span class="pun">&lt;%=</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">answer %&gt;" readonly class="</span><span class="pun">&lt;%</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">correct</span><span class="pun">){</span><span class="pln"> %&gt; correct </span><span class="pun">&lt;%</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> %&gt; wrong </span><span class="pun">&lt;%</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> %&gt;"/&gt;
      </span><span class="pun">&lt;%</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(!</span><span class="pln">result</span><span class="pun">.</span><span class="pln">correct</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> %&gt;
        </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"wrong"</span><span class="tag">&gt;</span><span class="pun">&lt;%=</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">note %&gt;</span><span class="tag">&lt;/div&gt;</span><span class="pln">
      </span><span class="pun">&lt;%</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> %&gt;
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
    </span><span class="pun">&lt;%</span><span class="pln"> </span><span class="pun">})</span><span class="pln"> %&gt;

    </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"/"</span><span class="tag">&gt;</span><span class="pln">اختبار جديد</span><span class="tag">&lt;/a&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وقبل أن نعاين الصفحات يجب أن نضيف تعليمة تشغيل خادم الويب بنهاية الملف bin/www على منفذ محدد نستخرجه من متغيرات البيئة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_18" style=""><span class="com">// رقم المنفذ من متغيرات البيئة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">PORT</span><span class="pun">;</span><span class="pln">

</span><span class="com">// تشغيل خادم الويب</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Listening</span><span class="pln"> on http</span><span class="pun">:</span><span class="com">//localhost:${port}`))</span></pre>

<p>
	ولتحميل متغيرات البيئة من ملف env. نضعه ضمن مجلد المشروع مباشرةً نستخدم المكتبة dotenv بإضافة التعليمة التالية في بداية نفس الملف لتحميل القيم من env. وتعيينها كمتغيرات بيئة مباشرةً كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_20" style=""><span class="pun">#!</span><span class="str">/usr/</span><span class="pln">bin</span><span class="pun">/</span><span class="pln">env node
</span><span class="com">// تحميل متغيرات البيئة</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'dotenv'</span><span class="pun">).</span><span class="pln">config</span><span class="pun">()</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	وننشئ الملف env. ونعرف ضمنه القيمة PORT بأي رقم منفذ نريده وليكن 3000:
</p>

<pre class="ipsCode">PORT=3000
</pre>

<p>
	ونُعرّف ضمن ملف تعريف الحزمة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">package.json</a> النص البرمجي <code>start</code> لتشغيل الخادم ضمن القيمة <code>scripts</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_22" style=""><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"start"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"node bin/www"</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	لتنسيق جميع الصفحات يمكن التعديل على الملف public/style.css ولن نعرض محتواه اختصارًا، وبعد التعديل عليه لتنسيق الصفحات يمكن معاينتها بتشغيل الخادم بتنفيذ التعليمة التالية بطرفية ضمن مجلد المشروع:
</p>

<pre class="ipsCode">npm start
</pre>

<p>
	نزور الصفحة الرئيسية على الرابط المعروض بعد تشغيل الخادم:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130149" href="https://academy.hsoub.com/uploads/monthly_2023_07/index.png.c89644d6f63e040631b6841a7e9bce4e.png" rel=""><img alt="index.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130149" data-unique="ywmeq1q6v" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_07/index.thumb.png.30ea3783b849427fac6b225f886e8e6b.png"> </a>
</p>

<p>
	ندخل أي قيمة ضمن الصفحة السابقة لنعاين صفحة عرض الأسئلة لنشاهد التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130148" href="https://academy.hsoub.com/uploads/monthly_2023_07/questions.png.e3e94eae0d09edbc0eb6b21de00e4bba.png" rel=""><img alt="questions.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130148" data-unique="0eh2mc2yy" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_07/questions.thumb.png.92960eaa75fd585d25c7f3572e38cb64.png"> </a>
</p>

<p>
	ونضغط عرض النتيجة لنعاين صفحة عرض النتيجة النهائية كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130147" href="https://academy.hsoub.com/uploads/monthly_2023_07/result.png.29afaafe7f88452f7f5821659470ecdd.png" rel=""><img alt="result.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130147" data-unique="82f4o9vdo" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_07/result.thumb.png.729008e9f0539acafc7ec1073d67b9e8.png"> </a>
</p>

<p>
	بعد أن جهزنا ملفات المشروع بالكامل والصفحات المطلوبة أصبح جاهزًا الآن لربطه مع ChatGPT لتوليد المحتوى المطلوب وعرضه وهو ما سنبدأ به في الفقرات التالية.
</p>

<h2>
	تحضير الربط مع ChatGPT
</h2>

<p>
	قبل أن نتمكن من التعامل مع الواجهة البرمجية لنموذج ChatGPT علينا التحضير بعدة خطوات، أولًا عبر تسجيل حساب مدفوع ضمن موقع شركة OpenAI، ومنه توليد مفتاح واجهة برمجية جديد ثم نسخه إلى ملف env. ضمن المشروع وتحديده كقيمة للمفتاح <code>OPENAI_API_KEY</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_24" style=""><span class="pun">...</span><span class="pln">
OPENAI_API_KEY</span><span class="pun">=&lt;قيمة</span><span class="pln"> </span><span class="pun">المفتاح&gt;</span></pre>

<p>
	وضمن الملف نستخدم المكتبة openai التي ثبتناها في بداية المقال ونُنشئ كائن جديد من الصنف الذي توفره <code>OpenAIApi</code> ونمرر له الإعدادات المناسبة مع قيمة المفتاح السابق من متغيرات البيئة في بداية الملف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_26" style=""><span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">OpenAIApi</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Configuration</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"openai"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> openai </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">OpenAIApi</span><span class="pun">(</span><span class="pln">
    </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Configuration</span><span class="pun">({</span><span class="pln"> apiKey</span><span class="pun">:</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">OPENAI_API_KEY </span><span class="pun">})</span><span class="pln">
</span><span class="pun">)</span></pre>

<p>
	يمكننا الآن استخدام الثابت <code>openai</code> للتعامل مع الواجهات البرمجية التي توفرها OpenAI وتحديدًا نموذج ChatGPT وهو ما سنبدأ بالتعامل معه في الفقرة التالية.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		ChatGPT المُطلق للعموم والمجاني محصور بواجهة الويب التي توفرها الشركة وهو مجاني أصلًا كي يُدربه المستخدمون، ولا يملك واجهة برمجية سوى المدفوعة حاليًا.
	</p>
</blockquote>

<h2>
	توليد أسئلة الاختبار
</h2>

<p>
	كل ما علينا الآن هو تعديل توابع معالجة الطلبات المرسلة والطلب من نموذج ChatGPT توليد بعض الأسئلة عن الموضوع الذي أرسله المستخدم من الصفحة الرئيسية إلى المسار <code>test/</code> باستخراج قيمة موضوع الاختبار ضمن تابع معالجة الطلب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_28" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/test'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// استخراج موضوع الاختبار</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> subject </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">body
    </span><span class="pun">...</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	نحدد التعليمة التي سنرسلها إلى النموذج، وهي أهم خطوة في تلك العملية، فالتعليمة الواضحة والجيدة ينتج عنها النتيجة التي نريدها وفي حالتنا سنخبر النموذج بتوليد بعض الأسئلة عن الموضوع السابق وشكل التعليمة سيكون كالتالي:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		أكتب أربعة أسئلة يمكن الاجابة عليها بجواب قصير لاختبار مستوايي في &lt;الموضوع&gt;، الأسئلة كل سؤال بسطر:
	</p>
</blockquote>

<p>
	نلاحظ كيف حددنا شكل الخرج الذي نريده وهو أن يكتب كل سؤال ضمن سطر لنتمكن من استخراجه، ويمكننا تحديد شكل الخرج بطريقة متقدمة سنتعرف عليها لاحقًا أما حاليًا يمكننا فصل الجواب الناتج من النموذج إلى الأسطر المكونة له واعتبار كل سطر هو سؤال، ونبني التعليمة السابقة وندرج ضمنها قيمة الموضوع المرسل ونستخدم الثابت <code>openai</code> لإرسال رسالة إلى النموذج gpt-3.5-turbo وهو النسخة المتاحة دون اشتراك شهري وقت كتابة هذا المقال، حيث تحوي الرسالة على التعليمة السابقة، ثم نستخرج من الجواب قيمة الرسالة المُرسلة من النموذج ليصبح تابع معالجة طلب توليد أسئلة عن موضوع معين كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_31" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/test'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// استخراج موضوع الاختبار</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> subject </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">body

    </span><span class="com">// بناء التعليمة</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> prompt </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`أكتب</span><span class="pln"> </span><span class="pun">أربعة</span><span class="pln"> </span><span class="pun">أسئلة</span><span class="pln"> </span><span class="pun">يمكن</span><span class="pln"> </span><span class="pun">الاجابة</span><span class="pln"> </span><span class="pun">عليها</span><span class="pln"> </span><span class="pun">بجواب</span><span class="pln"> </span><span class="pun">قصير</span><span class="pln"> </span><span class="pun">لاختبار</span><span class="pln"> </span><span class="pun">مستوايي</span><span class="pln"> </span><span class="pun">في</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">subject</span><span class="pun">}،</span><span class="pln"> </span><span class="pun">الأسئلة</span><span class="pln"> </span><span class="pun">كل</span><span class="pln"> </span><span class="pun">سؤال</span><span class="pln"> </span><span class="pun">بسطر:`;</span><span class="pln">

    </span><span class="com">// إرسال طلب إلى النموذج</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> openai</span><span class="pun">.</span><span class="pln">createChatCompletion</span><span class="pun">({</span><span class="pln">
        model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'gpt-3.5-turbo'</span><span class="pun">,</span><span class="pln">
        messages</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="pun">{</span><span class="pln"> role</span><span class="pun">:</span><span class="pln"> </span><span class="str">'user'</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">:</span><span class="pln"> prompt </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">]</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">

    </span><span class="com">// استخراج الجواب</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">choices</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">.</span><span class="pln">content

    </span><span class="com">// استخراج الأسئلة المولدة من الجواب</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> questions </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'\n'</span><span class="pun">)</span><span class="pln">

    </span><span class="com">// تمرير الأسئلة المقترحة لصفحة الاختبار</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> questions </span><span class="pun">});</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	وفي الفقرة التالية سنستقبل الأجوبة من المستخدم ونُقيمها باستخدام ChatGPT مجددًا ونعرض النتيجة للمستخدم.
</p>

<h2>
	إرسال الإجابات للتقييم
</h2>

<p>
	بعد إدخال الإجابات في صفحة الاختبار وإرسالها إلى مسار عرض النتيجة <code>result/</code> يمكننا ضمن ذلك المسار استقبال الإجابات من الكائن <code>answers</code> من جسم الطلب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_34" style=""><span class="pln">app</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'/result'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// استخراج الإجابات</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> answers </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">body
    </span><span class="pun">...</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	قيمة ذلك المتغير ستكون مصفوفة من كائنات يحوي كل منها على نص السؤال ضمن الحقل <code>question</code> والجواب المُدخل من المستخدم <code>answer</code>، والتي سنضمنها ضمن التعليمة المُرسلة إلى ChatGPT لتقييمها، وبما أن التقييمات لكل سؤال نريدها أن تحوي معلومات عن صحة الجواب وملاحظة في حال كان خاطئًا، فيمكننا الطلب من ChatGPT إعادة الجواب بصيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> ليسهل علينا استخراج المعلومات منه وعرضها للمستخدم ضمن التطبيق، وذلك بإعطاء مثال عن شكل الخرج في نهاية التعليمة ليكون شكل التعليمة النهائي كالتالي:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		السؤال: … الجواب: …
	</p>

	<p>
		السؤال: … الجواب: …
	</p>
</blockquote>

<p>
	قيم الإجابات السابقة مع الشرح بصيغة JSON التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_36" style=""><span class="pun">[{</span><span class="pln">correct</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> note</span><span class="pun">:</span><span class="str">"تعليل أو ملاحظة حول الإجابة...."</span><span class="pun">}،</span><span class="pln"> </span><span class="pun">...]</span></pre>

<p>
	نبني تلك التعليمة من الأجوبة المرسلة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_38" style=""><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> answers </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">body

</span><span class="com">// الأسئلة مع الأجوبة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> questionsAndAnswers </span><span class="pun">=</span><span class="pln"> answers
        </span><span class="pun">.</span><span class="pln">map</span><span class="pun">(({</span><span class="pln"> question</span><span class="pun">,</span><span class="pln"> answer </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">`السؤال:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">question</span><span class="pun">}</span><span class="pln">\n</span><span class="pun">الجواب:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">answer</span><span class="pun">}`)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">'\n\n'</span><span class="pun">)</span><span class="pln">

</span><span class="com">// بناء التعليمة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> prompt </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">questionsAndAnswers</span><span class="pun">}</span><span class="pln">\n\n</span><span class="pun">قيم</span><span class="pln"> </span><span class="pun">الإجابات</span><span class="pln"> </span><span class="pun">السابقة</span><span class="pln"> </span><span class="pun">مع</span><span class="pln"> </span><span class="pun">الشرح</span><span class="pln"> </span><span class="pun">بصيغة</span><span class="pln"> JSON </span><span class="pun">التالية:</span><span class="pln">
    </span><span class="pun">[{</span><span class="pln">correct</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> note</span><span class="pun">:</span><span class="str">"تعليل أو ملاحظة حول الإجابة...."</span><span class="pun">}،</span><span class="pln"> </span><span class="pun">...]`;</span></pre>

<p>
	ثم نرسلها إلى ChatGPT كما فعلنا سابقًا والفرق هذه المرة في معالجة الجواب حيث سنترجمه من صيغة JSON إلى مصفوفة في <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">جافاسكريبت</a> باستخدام التابع <code>JSON.parse</code>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_40" style=""><span class="com">// إرسال التعليمة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> openai</span><span class="pun">.</span><span class="pln">createChatCompletion</span><span class="pun">({</span><span class="pln">
        model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'gpt-3.5-turbo'</span><span class="pun">,</span><span class="pln">
        messages</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
            </span><span class="pun">{</span><span class="pln"> role</span><span class="pun">:</span><span class="pln"> </span><span class="str">'user'</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">:</span><span class="pln"> prompt </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">]</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">

</span><span class="com">// استخراج الجواب</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> message </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">choices</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">?.</span><span class="pln">content

</span><span class="com">// معالجة الجواب</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> results </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span></pre>

<p>
	والآن نعرضها ضمن قالب العرض result.ejs باستخدام التابع <code>render</code>مع تمرير نص السؤال والجواب المدخل من المستخدم وتقييم الجواب والملاحظة حوله كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6348_42" style=""><span class="com">// عرض النتيجة</span><span class="pln">
res</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="str">'result'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    results</span><span class="pun">:</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> index</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
        question</span><span class="pun">:</span><span class="pln"> answers</span><span class="pun">[</span><span class="pln">index</span><span class="pun">].</span><span class="pln">question</span><span class="pun">,</span><span class="pln">
        answer</span><span class="pun">:</span><span class="pln"> answers</span><span class="pun">[</span><span class="pln">index</span><span class="pun">].</span><span class="pln">answer</span><span class="pun">,</span><span class="pln">
        note</span><span class="pun">:</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">note</span><span class="pun">,</span><span class="pln">
        correct</span><span class="pun">:</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">correct</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">})),</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	وبذلك يصبح التطبيق جاهزًا للاختبار سنستعرضه ضمن الفقرة التالية.
</p>

<h2>
	اختبار التطبيق
</h2>

<p>
	لنحاول الإجابة على بعض الأسئلة المتعلقة بالبرمجة في نود لاختبار المستوى:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130146" href="https://academy.hsoub.com/uploads/monthly_2023_07/test-index.png.02feebfa03080a929228ba1454680a52.png" rel=""><img alt="test-index.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130146" data-unique="l9orcks6h" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_07/test-index.thumb.png.9a9f355cb8c8f501e7a3a33d686e26f1.png"> </a>
</p>

<p>
	نلاحظ اقتراح أسئلة متنوعة عن ذلك الموضوع، لنحاول الإجابة عليها باللغة العربية وترك الجواب الأخير خاطئًا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130145" href="https://academy.hsoub.com/uploads/monthly_2023_07/test-questions.png.2a5beef22c90bb28a67d297b092c1290.png" rel=""><img alt="test-questions.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130145" data-unique="87mjowhb6" style="width: 428px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2023_07/test-questions.thumb.png.cc991fd9357a9a853855682529f789cf.png"> </a>
</p>

<p>
	عاين ChatGPT تلك الأجوبة وحدد الصحيح والخاطئ منها بفهم النص المكتوب وربطه بسؤاله بدقة وحدد الجواب الأخير بأنه خاطئ وأعطى ملاحظة عن سبب الخطأ:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="130144" href="https://academy.hsoub.com/uploads/monthly_2023_07/test-result.png.50ee82a12f61515a24a7912312d131ae.png" rel=""><img alt="test-result.png" class="ipsImage ipsImage_thumbnailed" data-fileid="130144" data-unique="qr8x9yt2u" src="https://academy.hsoub.com/uploads/monthly_2023_07/test-result.thumb.png.4d589f3e5fe1e287097a302a19df97db.png"> </a>
</p>

<h2>
	خاتمة
</h2>

<p>
	كما رأينا سهولة التعامل مع ChatGPT والاحتمالات الواسعة الممكنة ضمنه، من توليد محتوى محدد إلى قراء وفهم مدخلات المستخدم إلى إمكانية تحديد شكل الخرج المطلوب لتسهيل التعامل معه برمجيًا، ويمكن التطوير على التطبيق الذي طورناه في هذا المقال بعدة أفكار، فمثلًا يمكن تحديد عدد الأسئلة من قبل المستخدم والطلب من ChatGPT ذلك العدد من الأسئلة، أو يمكن حفظ سجل الأسئلة المجاب عنها من قبل المستخدم وإرسالها مع طلب توليد الأسئلة الجديدة والطلب منه أن تكون الأسئلة مختلفة في مواضيعها عن السابقة ما يغني تجربة المستخدم، أو إذا كان موضوع الأسئلة محددًا يمكن توليد العديد من أسئلة اختيار من متعدد عن ذلك الموضوع دفعة واحدة مع تحديد الإجابة الصحيحة لكل سؤال ثم تخزينها ضمن قاعدة بيانات أسئلة كبيرة وتُعرض للمستخدم عشوائيًا كي يتمرن عليها ونحدد مستواه وبذلك لا داعي للاتصال مع النموذج خلال مرحلة الإنتاج ونستفيد منه لتوليد محتوى الأسئلة فقط.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%84%D8%AE%D8%B5%D9%84%D9%8A-%D9%84%D8%AA%D9%84%D8%AE%D9%8A%D8%B5-%D8%A7%D9%84%D9%85%D9%82%D8%A7%D9%84%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%88%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%84-r2008/" rel="">برمجة تطبيق 'لخصلي' لتلخيص المقالات باستخدام ChatGPT ولارافل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D8%B5%D9%81%D8%A9-%D9%84%D8%A7%D9%82%D8%AA%D8%B1%D8%A7%D8%AD-%D8%A7%D9%84%D9%88%D8%AC%D8%A8%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-%D9%88-dall-e-%D9%81%D9%8A-php-r2005/" rel="">تطوير تطبيق 'وصفة' لاقتراح الوجبات باستخدام ChatGPT و DALL-E في PHP</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2018</guid><pubDate>Tue, 04 Jul 2023 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x637;&#x644;&#x628;&#x627;&#x62A; HTTP &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-http-%D9%81%D9%8A-nodejs-r1868/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_01/1775192389_--HTTP--Node.png.8612960b0846b28461dfeefaa931b69a.png" /></p>
<p>
	تحتاج معظم التطبيقات حاليًا إلى التواصل مع بعض الخوادم لجلب البيانات منها أو لإتمام بعض المهام، فمثلًا في تطبيق ويب لشراء الكتب سيحتاج للتواصل مع خادم إدارة طلبات الزبائن وخادم مستودع الكتب وخادم إتمام الدفع، حيث تتواصل تلك الخدمات مع بعضها عن طريق الويب عبر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> وتتبادل البيانات برمجيًا.
</p>

<p>
	توفر نود دعمًا للتواصل عن طريق طلبات HTTP مع واجهات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> عبر الويب، فتتيح الوحدة البرمجية <code>‎http‎</code> والوحدة <code>‎https‎</code>، حيث تحتوي كل منهما على التوابع اللازمة لإنشاء خادم HTTP لمعالجة الطلبات الواردة إلى الخادم، وتوابع لإنشاء طلبات HTTP وإرسالها إلى الخوادم الأخرى، حيث تسمح هاتين الميزتين بتطوير تطبيقات ويب حديثة تعتمد على الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> للتواصل بينها، ولا حاجة لتثبيت أي وحدة برمجية خارجية حيث تأتي تلك الوحدات جاهزة مع نود افتراضيًا.
</p>

<p>
	سنتعلم في هذا المقال كيف يمكننا الاستفادة من الوحدة <a href="https://wiki.hsoub.com/Node.js/https" rel="external"><code>‎https‎</code></a> لإرسال طلبات HTTP بمثال عن التعامل مع خادم <a href="https://jsonplaceholder.typicode.com" rel="external nofollow">JSON Placeholder</a> وهو <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-api%D8%9F-r1512/" rel="">واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> وهمية تستخدم في عمليات التدريب والاختبار، حيث سنتعلم طريقة إرسال طلب HTTP لطلب البيانات من نوع <code>‎GET‎</code>، ثم سنتعرف على طرق تخصيص الطلب المرسل كإضافة الترويسات، وسنتعرف أيضًا على الطلبات بمختلف أنواعها مثل <code>‎POST‎</code> و <code>‎PUT‎</code> و <code>‎DELETE‎</code> والتي تستخدم لتعديل البيانات على الخوادم الأخرى.
</p>

<h2>
	المستلزمات
</h2>

<ul>
	<li>
		نسخة نود مثبتة على جهازك، حيث استخدمنا في هذا المقال الإصدار رقم 10.19.0.
	</li>
	<li>
		تعتمد توابع إرسال طلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> على واجهة مجرى البيانات stream في نود وهي بدورها نسخة من كائن مرسل الأحداث event emitter، لذا يمكن التعامل مع البيانات الواردة منها بنفس طريقة تعاملنا مع الأحداث المرسلة، وللتعرف على مرسل الأحداث وطريقة التعامل معه يمكنك مراجعة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%B1%D8%B3%D9%84-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-event-emitter-%D9%81%D9%8A-nodejs-r1806/" rel="">المقال التاسع</a> من <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">هذه السلسلة</a>.
	</li>
</ul>

<h2>
	إرسال طلب من نوع ‎GET‎
</h2>

<p>
	إن أردنا طلب بيانات من خادم ويب ما عبر واجهته البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> نرسل إليه عادة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-r73/" rel="">طلب HTTP</a> من نوع <code>‎GET‎</code>، ففي هذه الفقرة سنتعلم طريقة إرسال تلك الطلبات في نود وتحديدًا لجلب مصفوفة بيانات <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">بصيغة JSON</a> تحتوي على بيانات حسابات شخصية لمستخدمين وهميين من واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> متاحة للعموم، حيث تحوي الوحدة البرمجية <code>‎https‎</code> على تابعين يمكن استخدامهما لإرسال طلبات من نوع <code>‎GET‎</code> هما التابع <a href="https://wiki.hsoub.com/Node.js/https#https.get.28options.5B.2C_callback.5D.29" rel="external"><code>‎get()‎</code></a>، والتابع <a href="https://wiki.hsoub.com/Node.js/https#https.request.28options.5B.2C_callback.5D.29" rel="external"><code>‎request()‎</code></a> الذي يمكن استخدامه لإرسال طلبات من أنواع متعددة أخرى كما سنتعلم لاحقًا، ولنبدأ أولًا بالتعرف على التابع <code>‎get()‎</code>.
</p>

<h3>
	إرسال الطلبات باستخدام التابع ‎get()‎
</h3>

<p>
	صيغة استخدام التابع <code>‎get()‎</code> تكون كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_12" style=""><span class="pln">https</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">URL_String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Callback_Function</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Action</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حيث نمرر له سلسلة نصية كمعامل أول تحوي المسار الذي سنرسل الطلب إليه، والمعامل الثاني يكون دالة رد النداء callback function لمعالجة نتيجة الطلب.
</p>

<p>
	سنبدأ بإنشاء مجلد جديد للمشروع سيحوي الأمثلة التي سنكتبها ضمن هذا الفصل كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_15" style=""><span class="pln">$ mkdir requests</span></pre>

<p>
	وندخل إلى المجلد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_18" style=""><span class="pln">$ cd requests</span></pre>

<p>
	ننشئ ملف جافاسكربت جديد ونفتحه باستخدام أي محرر نصوص حيث سنستخدم في أمثلتنا محرر نانو ‎nano‎ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_20" style=""><span class="pln">$ nano getRequestWithGet</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونبدأ باستيراد الوحدة <code>‎https‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_22" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span></pre>

<p>
	<strong>ملاحظة</strong>: يوجد في نود وحدتين برمجيتين هما <code>‎http‎</code> و <code>‎https‎</code> تحويان على نفس التوابع التي تعمل بنفس الطريقة، والفرق بينها أن التوابع ضمن <code>‎https‎</code> ترسل الطلبات عبر طبقة أمان النقل Transport Layer Security أو <abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل"><abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr></abbr>/<abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></abbr>، وسنرسل الطلبات في أمثلتنا عبر HTTPS لذا سنستخدم تلك التوابع من الوحدة <code>‎https‎</code>، بينما لو كنا سنرسل الطلبات عبر HTTP فيجب استخدام توابع الوحدة <code>‎http‎</code> بدلًا منها.
</p>

<p>
	نبدأ بكتابة شيفرة إرسال طلب <code>‎GET‎</code> إلى الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لجلب بيانات المستخدمين، حيث سنرسل طلبًا إلى واجهة <a href="https://jsonplaceholder.typicode.com" rel="external nofollow">JSON Placeholder</a> وهي واجهة برمجية متاحة للاستخدام العام لأغراض الاختبار، ولا تتأثر البيانات على ذلك <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">الخادم</a> بالطلبات المرسلة فمهمته فقط محاكاة عمل خادم حقيقي، حيث سيرجع لنا دومًا بيانات وهمية طالما أن الطلب المرسل إليه سليم، لنبدأ بكتابة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_24" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://jsonplaceholder.typicode.com/users?_limit=2'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">});</span></pre>

<p>
	كما ذكرنا سابقًا يقبل التابع <code>‎get()‎</code> معاملين وهما المسار الوجهة للطلب URL كسلسلة نصية للواجهة البرمجية، ودالة رد نداء لمعالجة نتيجة طلب HTTP الواردة، حيث يمكن استخراج البيانات من الرد ضمن دالة رد النداء.
</p>

<p>
	يحمل طلب HTTP على رمز الحالة status code وهو عدد يشير إلى نجاح الطلب من عدمه، فمثلًا إذا كانت قيمة الرمز بين 200 و 299 فالطلب ناجح، أما إذا كان بين 400 و 599 فهناك خطأ ما، وفي مثالنا الرد من الواجهة البرمجية يجب أن يحتوي على رمز الحالة 200 إن نجح وهو أول ما سنتحقق منه ضمن تابع رد النداء لمعالجة الطلب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_26" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://jsonplaceholder.typicode.com/users?_limit=2'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Did</span><span class="pln"> not </span><span class="kwd">get</span><span class="pln"> an OK from the server</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}`);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يحوي كائن الرد <code>res</code> المُمرر لدالة رد النداء على الخاصية <code>‎statusCode‎</code> والتي تمثِّل قيمة رمز الحالة، وإذا لم تكن قيمته تساوي 200 سنطبع رسالة خطأ إلى الطرفية ونخرج مباشرةً.
</p>

<p>
	نلاحظ استدعاء التابع <code>‎res.resume()‎</code> من كائن الرد وهي طريقة لتحسين أداء البرنامج فعند إرسال طلبات HTTP في نود يتم عادة معالجة البيانات المرسلة ضمن الطلب كاملةً، أما عند استدعائنا للتابع <code>‎res.resume()‎</code> فإننا نخبر نود بتجاهل البيانات ضمن مجرى كائن الرد، وهي طريقة أسرع من لو تُركت تلك البيانات لالتقاطها في مرحلة كنس المهملات garbage collection التي تتم دوريًا لتفريغ الذاكرة المستخدمة من قبل التطبيق.
</p>

<p>
	والآن بعد أن تحققنا من رمز الحالة للرد سنبدأ بقراءة البيانات الواردة حيث تتوفر البيانات ضمن كائن مجرى الرد على دفعات، ويمكننا قراءتها بالاستماع إلى الحدث <code>‎data‎</code> من كائن الرد ثم تجميعها معًا ثم تحليلها <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">بصيغة JSON</a> لنتمكن من استخدامها ضمن التطبيق، لذلك سنضيف الشيفرة التالية ضمن تابع رد النداء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_29" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://jsonplaceholder.typicode.com/users?_limit=2'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Did</span><span class="pln"> not </span><span class="kwd">get</span><span class="pln"> an OK from the server</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}`);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">let</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Retrieved all data'</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">data</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عرفنا متغيرًا جديدًا بالاسم <code>‎data‎</code> والذي يحتوي على سلسلة نصية فارغة، حيث يمكننا تجميع البيانات الواردة إما على شكل مصفوفة من الأعداد تمثل البيانات للبايتات المكونة لها، أو على شكل سلسلة نصية وهو ما سنستخدمه في مثالنا لسهولة تحويل السلسلة النصية الناتجة عن عملية التجميع إلى <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r796/" rel="">كائن جافاسكربت</a>.
</p>

<p>
	نضيف بعد ذلك تابع الاستماع للبيانات الواردة على دفعات من الحدث <code>‎data‎</code> ونجمع البيانات كلها ضمن المتغير السابق <code>‎data‎</code>، ويمكننا التأكد من انتهاء دفعات البيانات الواردة عند إطلاق حدث الإغلاق <code>‎close‎</code> من كائن الرد، وبعدها يمكننا تحويل السلسلة النصية بصيغة JSON ضمن المتغير <code>‎data‎</code> وطباعة القيمة النهائية إلى الطرفية، وبذلك نكون قد أكملنا كتابة عملية إرسال طلب إلى واجهة برمجية ستُرسل بدورها مصفوفة من بيانات حسابات شخصية لثلاثة مستخدمين بصيغة JSON ونقرأ الرد الوارد.
</p>

<p>
	بقي لدينا إضافة معالجة لحالة رمي خطأ في حال لم نتمكن من إرسال الطلب لسبب ما، كحالة عدم وجود اتصال بالإنترنت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_32" style=""><span class="pun">...</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Retrieved all data'</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">data</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

</span><span class="pun">});</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Encountered</span><span class="pln"> an error trying to make a request</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عند حدوث خطأ في عملية الإرسال سنتلقى الحدث <code>‎error‎</code> من كائن الطلب، وإذا لم نستمع لهذا الحدث فسيرمى الخطأ الناتج ما يؤدي لإيقاف عمل البرنامج، لذلك نضيف دالة استماع للحدث <code>‎error‎</code> على كائن الطلب باستخدام التابع <code>‎on()‎</code> والذي سيطبع رسالة الخطأ الوارد إلى الطرفية، وبذلك نكون قد انتهينا من كتابة البرنامج.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذه باستخدام الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_34" style=""><span class="pln">$ node getRequestWithGet</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على الخرج التالي الذي يمثل الرد الوارد على الطلب المُرسل:
</p>

<pre class="ipsCode" id="ips_uid_7257_7">Retrieved all data
[
  {
    id: 1,
    name: 'Leanne Graham',
    username: 'Bret',
    email: 'Sincere@april.biz',
    address: {
      street: 'Kulas Light',
      suite: 'Apt. 556',
      city: 'Gwenborough',
      zipcode: '92998-3874',
      geo: [Object]
    },
    phone: '1-770-736-8031 x56442',
    website: 'hildegard.org',
    company: {
      name: 'Romaguera-Crona',
      catchPhrase: 'Multi-layered client-server neural-net',
      bs: 'harness real-time e-markets'
    }
  },
  {
    id: 2,
    name: 'Ervin Howell',
    username: 'Antonette',
    email: 'Shanna@melissa.tv',
    address: {
      street: 'Victor Plains',
      suite: 'Suite 879',
      city: 'Wisokyburgh',
      zipcode: '90566-7771',
      geo: [Object]
    },
    phone: '010-692-6593 x09125',
    website: 'anastasia.net',
    company: {
      name: 'Deckow-Crist',
      catchPhrase: 'Proactive didactic contingency',
      bs: 'synergize scalable supply-chains'
    }
  }
]</pre>

<p>
	بذلك نكون قد أرسلنا طلبًا من نوع <code>‎GET‎</code> بنجاح باستخدام مكتبات نود فقط، حيث أن التابع الذي استخدمناه <code>‎get()‎</code> يوجد في نود بسبب كثرة الحاجة لإرسال الطلبات من نوع <code>‎GET‎</code>، بينما الطريقة الأساسية لإرسال الطلبات هي باستخدام التابع <code>‎request()‎</code> والذي يمكنه إرسال أي نوع من الطلبات، وهو ما سنتعرف عليه في القسم التالي حيث سنستخدمه لإرسال طلب من نوع <code>‎GET‎</code>.
</p>

<h2>
	إرسال الطلبات باستخدام التابع ‎request()‎
</h2>

<p>
	يمكن استخدام التابع <a href="https://wiki.hsoub.com/Node.js/https#https.request.28options.5B.2C_callback.5D.29" rel="external"><code>‎request()‎</code></a> بعدة صيغ والصيغة التي سنستخدمها في أمثلتنا هي كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_38" style=""><span class="pln">https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">URL_String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Options_Object</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Callback_Function</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Action</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حيث نمرر له سلسلة نصية كمعامل أول تحتوي على مسار الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الذي سنرسل الطلب إليه، والمعامل الثاني هو كائن جافاسكربت يحتوي على عدة خيارات للطلب المرسل، والمعامل الأخير المُمرر هو دالة رد النداء callback لمعالجة نتيجة الطلب.
</p>

<p>
	نبدأ بإنشاء ملف جافاسكربت جديد بالاسم <code>‎getRequestWithRequest.js‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_40" style=""><span class="pln">$ nano getRequestWithRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنكتب برنامجًا مشابهًا لما كتبناه في القسم السابق ضمن الملف <code>‎getRequestWithGet.js‎</code>، حيث نبدأ باستيراد الوحدة <code>‎https‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_42" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span></pre>

<p>
	ثم نعرف كائن جافاسكربت يحتوي على الخاصية <code>‎method‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_44" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'GET'</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تعبر الخاصية <code>‎method‎</code> ضمن كائن خيارات التابع <code>‎request()‎</code> عن نوع الطلب الذي نريد إرساله، والآن نُرسل الطلب كما فعلنا سابقًا لكن مع بعض الاختلافات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_46" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="str">'https://jsonplaceholder.typicode.com/users?_limit=2'</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Did</span><span class="pln"> not </span><span class="kwd">get</span><span class="pln"> an OK from the server</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}`);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">let</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Retrieved all data'</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">data</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Encountered</span><span class="pln"> an error trying to make a request</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	مررنا مسار الوجهة للطلب كمعامل أول للتابع <code>‎request()‎</code> ثم كائن خيارات HTTP كمعامل ثاني، وبعدها دالة رد النداء لمعالجة الرد، حيث حددنا نوع الطلب المرسل كطلب <code>‎GET‎</code> ضمن كائن الخيارات <code>‎options‎</code> الذي عرفناه سابقًا، وبقي دالة رد النداء لمعالجة الطلب كما هو في المثال السابق، وأضفنا استدعاءً للتابع <code>‎end()‎</code> من كائن الطلب <code>‎request‎</code>، حيث يجب استدعاء هذا التابع عند إرسال الطلبات باستخدام <code>‎request()‎</code> لإتمام الطلب وإرساله، وفي حال لم نستدعيه فلن يُرسل الطلب ويبقى نود ينتظر منا إضافة بيانات جديدة إلى الطلب.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_48" style=""><span class="pln">$ node getRequestWithRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر الخرج التالي كما المثال السابق تمامًا:
</p>

<pre class="ipsCode" id="ips_uid_7257_10">Retrieved all data
[
  {
    id: 1,
    name: 'Leanne Graham',
    username: 'Bret',
    email: 'Sincere@april.biz',
    address: {
      street: 'Kulas Light',
      suite: 'Apt. 556',
      city: 'Gwenborough',
      zipcode: '92998-3874',
      geo: [Object]
    },
    phone: '1-770-736-8031 x56442',
    website: 'hildegard.org',
    company: {
      name: 'Romaguera-Crona',
      catchPhrase: 'Multi-layered client-server neural-net',
      bs: 'harness real-time e-markets'
    }
  },
  {
    id: 2,
    name: 'Ervin Howell',
    username: 'Antonette',
    email: 'Shanna@melissa.tv',
    address: {
      street: 'Victor Plains',
      suite: 'Suite 879',
      city: 'Wisokyburgh',
      zipcode: '90566-7771',
      geo: [Object]
    },
    phone: '010-692-6593 x09125',
    website: 'anastasia.net',
    company: {
      name: 'Deckow-Crist',
      catchPhrase: 'Proactive didactic contingency',
      bs: 'synergize scalable supply-chains'
    }
  }
]</pre>

<p>
	وبذلك نكون قد تعرفنا على طريقة استخدام التابع <code>‎request()‎</code> لإرسال الطلبات من نوع <code>‎GET‎</code> وهو تابع أقوى من التابع السابق <code>‎get()‎</code> حيث يسمح بتخصيصات عدة على الطلب المرسل كتحديد نوعه وخيارات أخرى سنتعرف عليها في الفقرة التالية.
</p>

<h2>
	تخصيص خيارات HTTP للتابع ‎request()‎
</h2>

<p>
	يمكن استخدام التابع <code>‎request()‎</code> لإرسال طلبات HTTP دون تمرير عنوان مسار الوجهة للطلب كمعامل أول بل بتمريره ضمن كائن الخيارات <code>‎options‎</code> لتصبح صيغة استدعاء التابع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_52" style=""><span class="pln">https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="typ">Options_Object</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Callback_Function</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Action</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في هذه الفقرة سنستخدم هذه الصيغة للتركيز على إعداد وتخصيص خيارات التابع <code>‎request()‎</code>، لذا نعود إلى ملف المثال السابق <code>‎getRequestWithRequest.js‎</code> ونعدله بأن نزيل المسار URL المُمرر للتابع <code>‎request()‎</code> لتصبح المعاملات المُمررة له هي كائن الخيارات <code>‎options‎</code> ودالة رد النداء فقط كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_54" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'GET'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	لنضيف الخيارات الجديدة إلى الكائن <code>‎options‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_56" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jsonplaceholder.typicode.com'</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/users?_limit=2'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'GET'</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نلاحظ أنه وبدلًا من تمرير المسار كاملًا فإننا نمرره على قسمين ضمن الخاصيتين <code>‎host‎</code> و <code>‎path‎</code> حيث تُعبّر الخاصية <code>‎host‎</code> عن عنوان النطاق أو عنوان IP للخادم الوجهة، أما الخاصية <code>‎path‎</code> فهي كل ما يلي بعد ذلك ضمن المسار بما فيها معاملات الاستعلام query parameters التي تأتي بعد إشارة الاستفهام.
</p>

<p>
	ويمكن أن تحتوي الخيارات على بيانات مفيدة أخرى للطلب المرسل مثل الترويسات المرسلة وهي بيانات وصفية عن الطلب نفسه، فمثلًا عادة تتطلب الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> تحديد صيغة البيانات المرسلة من عدة صيغ مدعومة مثل JSON أو CSV أو XML، ولتحديد الصيغة التي يطلبها المستخدم يمكن للواجهة البرمجية معاينة قيمة الترويسة <code>‎Accept‎</code> ضمن الطلب الوارد إليها وتحدد على أساسه الصيغة المناسبة لإرسالها.
</p>

<p>
	تُعبّر الترويسة <code>‎Accept‎</code> عن نوع البيانات التي يمكن للمستخدم التعامل معها، وبما أننا نتعامل مع واجهة برمجية تدعم صيغة JSON فقط، فيمكننا إضافة الترويسة <code>‎Accept‎</code> وإضافة قيمة لها توضح أننا نريد البيانات بصيغة JSON كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_58" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jsonplaceholder.typicode.com'</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/users?_limit=2'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'GET'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Accept'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وبذلك نكون قد تعرفنا على أكثر أربعة خيارات استخدامًا ضمن طلبات HTTP وهي عنوان المضيف <code>‎host‎</code> و المسار <code>‎path‎</code> ونوع الطلب <code>‎method‎</code> والترويسات <code>‎headers‎</code>، ويوجد العديد من الخيارات الأخرى المدعومة يمكنك الرجوع إلى <a href="https://wiki.hsoub.com/Node.js/http#http.request.28options.5B.2C_callback.5D.29" rel="external">التوثيق الرسمي</a> لها ضمن نود للتعرف عليها.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج لنختبر طريقة إرسال الطلبات بتمرير كائن الخيارات فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_60" style=""><span class="pln">$ node getRequestWithRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر لنا بيانات الرد مطابقة للأمثلة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_62" style=""><span class="typ">Retrieved</span><span class="pln"> all data
</span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Leanne Graham'</span><span class="pun">,</span><span class="pln">
    username</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Bret'</span><span class="pun">,</span><span class="pln">
    email</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Sincere@april.biz'</span><span class="pun">,</span><span class="pln">
    address</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      street</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Kulas Light'</span><span class="pun">,</span><span class="pln">
      suite</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Apt. 556'</span><span class="pun">,</span><span class="pln">
      city</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Gwenborough'</span><span class="pun">,</span><span class="pln">
      zipcode</span><span class="pun">:</span><span class="pln"> </span><span class="str">'92998-3874'</span><span class="pun">,</span><span class="pln">
      geo</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Object</span><span class="pun">]</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">'1-770-736-8031 x56442'</span><span class="pun">,</span><span class="pln">
    website</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hildegard.org'</span><span class="pun">,</span><span class="pln">
    company</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Romaguera-Crona'</span><span class="pun">,</span><span class="pln">
      catchPhrase</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Multi-layered client-server neural-net'</span><span class="pun">,</span><span class="pln">
      bs</span><span class="pun">:</span><span class="pln"> </span><span class="str">'harness real-time e-markets'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ervin Howell'</span><span class="pun">,</span><span class="pln">
    username</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Antonette'</span><span class="pun">,</span><span class="pln">
    email</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Shanna@melissa.tv'</span><span class="pun">,</span><span class="pln">
    address</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      street</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Victor Plains'</span><span class="pun">,</span><span class="pln">
      suite</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Suite 879'</span><span class="pun">,</span><span class="pln">
      city</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Wisokyburgh'</span><span class="pun">,</span><span class="pln">
      zipcode</span><span class="pun">:</span><span class="pln"> </span><span class="str">'90566-7771'</span><span class="pun">,</span><span class="pln">
      geo</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Object</span><span class="pun">]</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">'010-692-6593 x09125'</span><span class="pun">,</span><span class="pln">
    website</span><span class="pun">:</span><span class="pln"> </span><span class="str">'anastasia.net'</span><span class="pun">,</span><span class="pln">
    company</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Deckow-Crist'</span><span class="pun">,</span><span class="pln">
      catchPhrase</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Proactive didactic contingency'</span><span class="pun">,</span><span class="pln">
      bs</span><span class="pun">:</span><span class="pln"> </span><span class="str">'synergize scalable supply-chains'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	تختلف متطلبات الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> بحسب الجهة المطورة لها، لذا من الضروري التعامل مع كائن الخيارات <code>‎options‎</code> لتخصيص الطلب بحسب حاجة التطبيق والخادم، من تحديد نوع البيانات المطلوبة وإضافة الترويسات المناسبة وبعض التخصيصات الأخرى.
</p>

<p>
	أرسلنا ضمن كل الأمثلة السابقة طلبات فقط من نوع <code>‎GET‎</code> لجلب البيانات، وفي الفقرة التالية سنتعلم طريقة إرسال الطلبات من نوع <code>‎POST‎</code> والتي تستخدم لرفع البيانات إلى الخادم.
</p>

<h2>
	إرسال طلب من نوع ‎POST‎
</h2>

<p>
	نستخدم الطلبات من نوع <code>‎POST‎</code> لرفع البيانات إلى الخادم أو لطلب إنشاء بيانات جديدة من قبل الخادم، وفي هذه الفقرة سنتعرف على طريقة إرسال مثل هذه الطلبات في نود عبر إرسال طلب إنشاء مستخدم جديد إلى المسار <code>‎users‎</code> على الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<p>
	يمكننا إعادة استخدام بعض الشيفرات من مثال إرسال طلب من نوع <code>‎GET‎</code> السابق لإرسال طلبات من نوع <code>‎POST‎</code> مع إجراء بعض التعديلات عليها:
</p>

<ul>
	<li>
		تعديل نوع الطلب ضمن كائن الخيارات <code>‎options‎</code> ليصبح <code>‎POST‎</code>.
	</li>
	<li>
		تعيين ترويسة نوع المحتوى المُرسل وهو في حالتنا بصيغة JSON.
	</li>
	<li>
		التأكد من رمز الحالة للرد لتأكيد نجاح إنشاء مستخدم جديد.
	</li>
	<li>
		رفع بيانات المستخدم الجديد.
	</li>
</ul>

<p>
	نبدأ بإنشاء ملف جافاسكربت جديد بالاسم <code>‎postRequest.js‎</code> ونفتحه ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_64" style=""><span class="pln">$ nano postRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونبدأ كما سابقًا باستيراد الوحدة <code>‎https‎</code> وتعريف كائن الخيارات <code>‎options‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_66" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jsonplaceholder.typicode.com'</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/users'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Accept'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
    </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json; charset=UTF-8'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ونعدل خيار مسار الطلب <code>‎path‎</code> ليتوافق مع مسار إرسال الطلبات من نوع <code>‎POST‎</code> على الخادم، ونعدل خيار نوع الطلب المرسل <code>‎method‎</code> إلى القيمة <code>‎POST‎</code>، وأخيرًا نضيف الترويسة <code>‎Content-Type‎</code> ضمن الخيارات والتي تدل الخادم على نوع البيانات التي أرسلناها مع الطلب وهي في حالتنا بصيغة JSON وبترميز من نوع UTF-8، ثم نُرسل الطلب باستدعاء التابع <code>‎request()‎</code> كما فعلنا تمامًا عند إرسال طلب من نوع <code>‎GET‎</code> سابقًا ولكن هذه المرة سنتحقق من رمز الحالة للرد بقيمة تختلف عن 200 كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_68" style=""><span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">201</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Did</span><span class="pln"> not </span><span class="kwd">get</span><span class="pln"> a </span><span class="typ">Created</span><span class="pln"> from the server</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}‎`);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">let</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Added new user'</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">data</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تحققنا من صحة العملية بالتحقق من قيمة رمز الحالة بأن يساوي 201، وهو الرمز الذي يدل على إنشاء مورد جديد على الخادم بنجاح، ويُعبّر الطلب المرسل عن إنشاء مستخدم جديد لهذا سنحتاج لرفع بيانات هذا المستخدم وإرفاقها ضمن الطلب، لذا سننشئ تلك البيانات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_70" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestData </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'New User'</span><span class="pun">,</span><span class="pln">
  username</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hsoub'</span><span class="pun">,</span><span class="pln">
  email</span><span class="pun">:</span><span class="pln"> </span><span class="str">'user@hsoub.com'</span><span class="pun">,</span><span class="pln">
  address</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="str">'North Pole'</span><span class="pun">,</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Murmansk'</span><span class="pun">,</span><span class="pln">
    zipcode</span><span class="pun">:</span><span class="pln"> </span><span class="str">'12345-6789'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">'555-1212'</span><span class="pun">,</span><span class="pln">
  website</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hsoub.com'</span><span class="pun">,</span><span class="pln">
  company</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Hsoub'</span><span class="pun">,</span><span class="pln">
    catchPhrase</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Welcome to hsoub academy'</span><span class="pun">,</span><span class="pln">
    bs</span><span class="pun">:</span><span class="pln"> </span><span class="str">'cloud scale security'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">requestData</span><span class="pun">));</span></pre>

<p>
	عرفنا بيانات المستخدم الجديد ضمن المتغير <code>‎requestData‎</code> على شكل كائن جافاسكربت يحتوي على بيانات المستخدم، ونلاحظ أننا لم نرفق قيمة المعرف <code>‎id‎</code> للمستخدم حيث أن هذه القيمة يولدها الخادم تلقائيًا للبيانات الجديدة، ثم استدعينا التابع <code>‎request.write()‎</code> والذي يقبل سلسلة نصية أو كائن <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA%D8%A9-buffers-%D9%81%D9%8A-nodejs-r1805/" rel="">مخزن مؤقت buffer</a> ليتم إرسالها ضمن الطلب، وبما أن البيانات لدينا ضمن المتغير <code>‎requestData‎</code> هي كائن جافاسكربت فيجب تحويله إلى سلسلة نصية باستخدام <code>‎JSON.stringify‎</code>.
</p>

<p>
	ولإنهاء عملية الإرسال ننهي الطلب عبر استدعاء <code>request.end()‎</code> ونتحقق من حدوث أي أخطاء في عملية الإرسال كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_72" style=""><span class="pun">...</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Encountered</span><span class="pln"> an error trying to make a request</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	من الضروري استدعاء التابع <code>‎end()‎</code> لإنهاء الطلب وللإشارة إلى نود بأن كل البيانات التي نريد إرسالها ضمن الطلب قد أرفقت وأصبح بالإمكان إرساله.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج ونتأكد من عملية إنشاء المستخدم الجديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_74" style=""><span class="pln">$ node postRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنحصل على الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_7257_12">Added new user
{
  name: 'New User',
  username: 'hsoub',
  email: 'user@hsoub.com',
  address: { street: 'North Pole', city: 'Murmansk', zipcode: '12345-6789' },
  phone: '555-1212',
  website: 'hsoub.com',
  company: {
    name: 'Hsoub',
    catchPhrase: 'Welcome to the hsoub academy',
    bs: 'cloud scale security'
  },
  id: 11
}</pre>

<p>
	ما يعني أن الطلب تم بنجاح، حيث أعاد الخادم بيانات المستخدم التي أرسلناها مضافًا إليها قيمة معرّف المستخدم ID التي تم توليدها له، وبذلك نكون قد تعلمنا طريقة إرسال الطلبات من نوع <code>‎POST‎</code> لرفع البيانات إلى الخادم باستخدام نود، وفي الفقرة التالية سنتعلم طريقة إرسال الطلبات من نوع <code>‎PUT‎</code> للتعديل على بيانات موجودة مسبقًا.
</p>

<h2>
	إرسال طلب من نوع ‎PUT‎
</h2>

<p>
	تُستخدم الطلبات من نوع <code>‎PUT‎</code> لرفع البيانات إلى الخادم بشكل مشابه للطلبات من نوع <code>‎POST‎</code>، ولكن الفرق أنه عند تنفيذ طلب من نوع <code>‎PUT‎</code> عدة مرات سنحصل على نفس النتيجة، بينما عند تكرار نفس طلب <code>‎POST‎</code> عدة مرات سنضيف بذلك البيانات المرسلة أكثر من مرة إلى الخادم، وطريقة إرسال هذا الطلب مشابهة للطلب من نوع <code>‎POST‎</code> حيث نعرف الخيارات ونُنشئ الطلب ونكتب البيانات التي نريد رفعها إلى الطلب ثم نتحقق من الرد الوارد في نتيجة الطلب.
</p>

<p>
	نختبر ذلك بإنشاء طلب من نوع <code>‎PUT‎</code> لتعديل اسم المستخدم لأول مستخدم، وبما أن طريقة إرسال الطلب مشابهة لطريقة إرسال الطلب من نوع <code>‎POST‎</code> يمكننا الاستفادة من المثال السابق ونسخ الملف <code>‎postRequest.js‎</code> إلى ملف جديد بالاسم <code>‎putRequest.js‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_78" style=""><span class="pln">$ cp postRequest</span><span class="pun">.</span><span class="pln">js putRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نفتح الملف <code>‎putRequest.js‎</code> ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_80" style=""><span class="pln">$ nano putRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونعدل نوع الطلب إلى <code>‎PUT‎</code> ومساره إلى <code>‎https://jsonplaceholder.typicode.com/users/1‎</code> والبيانات المرسلة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_82" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jsonplaceholder.typicode.com'</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/users/1'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'PUT'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Accept'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
    </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json; charset=UTF-8'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Did</span><span class="pln"> not </span><span class="kwd">get</span><span class="pln"> an OK from the server</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}‎`);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">let</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Updated data'</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">data</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestData </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  username</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hsoub'</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">requestData</span><span class="pun">));</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Encountered</span><span class="pln"> an error trying to make a request</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نلاحظ تعديل قيم المسار <code>‎path‎</code> ونوع الطلب <code>‎method‎</code> ضمن كائن الخصائص <code>‎options‎</code> حيث يحوي المسار على معرّف المستخدم الذي نود تعديل بياناته، ثم نتحقق من من رمز الحالة للطلب بأن يكون بالقيمة 200 ما يدل على نجاح الطلب، ونلاحظ أن البيانات التي أرسلناها تحوي فقط على الخصائص التي نريد تحديثها من بيانات المستخدم.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_84" style=""><span class="pln">$ node putRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر الخرج التالي:
</p>

<pre class="ipsCode" id="ips_uid_7257_14">Updated data
{ username: 'hsoub', id: 1 }</pre>

<p>
	أرسلنا بنجاح طلب من نوع <code>‎PUT‎</code> لتعديل بيانات مستخدم موجودة مسبقًا على الخادم، وبذلك نكون قد تعلمنا طرق طلب البيانات ورفعها وتحديثها، وسنتعلم في الفقرة التالية كيف يمكن حذف البيانات من الخادم بإرسال طلب من النوع <code>‎DELETE‎</code>.
</p>

<h2>
	إرسال طلب من نوع ‎DELETE‎
</h2>

<p>
	تستخدم الطلبات من نوع <code>‎DELETE‎</code> لحذف البيانات من الخادم، ويمكن أن يحتوي الطلب على بيانات مرفقة ضمنه ولكن معظم الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لا تتطلب ذلك، حيث يستخدم هذا النوع من الطلبات لحذف بيانات كائن ما كليًا من الخادم.
</p>

<p>
	سنرسل في هذه الفقرة طلب من هذا النوع لحذف بيانات أحد المستخدمين، وطريقة إرسال هذا الطلب مشابهة لطريقة إرسال طلب من نوع <code>‎GET‎</code>، لذا يمكننا نسخ ملف المثال السابق <code>‎getRequestWithRequest.js‎</code> إلى ملف جديد بالاسم <code>‎deleteRequest.js‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_88" style=""><span class="pln">$ cp getRequestWithRequest</span><span class="pun">.</span><span class="pln">js deleteRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونفتح الملف الجديد ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_90" style=""><span class="pln">$ nano deleteRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونعدل الشيفرة لإرسال طلب حذف لأول مستخدم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_92" style=""><span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jsonplaceholder.typicode.com'</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/users/1'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'DELETE'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Accept'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Did</span><span class="pln"> not </span><span class="kwd">get</span><span class="pln"> an OK from the server</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}‎`);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">let</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Deleted user'</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">data</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln">

request</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Encountered</span><span class="pln"> an error trying to make a request</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عدلنا قيمة المسار <code>‎path‎</code> ضمن كائن خيارات الطلب ليحوي معرّف المستخدم الذي نريد حذفه، وعدلنا نوع الطلب إلى <code>‎DELETE‎</code>، والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9106_94" style=""><span class="pln">$ node deleteRequest</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	لنحصل على الخرج:
</p>

<pre class="ipsCode" id="ips_uid_7257_16">Deleted user
{}</pre>

<p>
	لا تعيد الواجهة البرمجية أي بيانات ضمن جسم الرد الوارد، ولكن رمز الحالة لهذا الرد يكون 200 أي تم حذف بيانات المستخدم بنجاح، وبذلك نكون قد تعرفنا على طريقة إرسال طلبات من نوع <code>‎DELETE‎</code> أيضًا في نود.
</p>

<h2>
	خاتمة
</h2>

<p>
	تعرفنا في هذا الفصل على طريقة إرسال الطلبات في نود بأنواعها مختلفة مثل <code>‎GET‎</code> و <code>‎POST‎</code> و <code>‎PUT‎</code> و <code>‎DELETE‎</code> دون استخدام أي مكتبات خارجية وفقط باستخدام الوحدة البرمجية <code>‎https‎</code> التي يوفرها نود، وتعرفنا على طريقة خاصة لإرسال الطلبات من نوع <code>‎GET‎</code> باستخدام التابع <code>‎get()‎</code> وكيف أن باقي الطلبات يمكن إرسالها باستخدام التابع <code>‎request()‎</code>، وتعاملنا ضمن الأمثلة مع واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> عامة ويمكن بنفس الطريقة إرسال الطلبات إلى مختلف أنواع الواجهات البرمجية.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-create-an-http-client-with-core-http-in-node-js" rel="external nofollow">How To Create an HTTP Client with Core HTTP in Node.js</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-fs-%D9%81%D9%8A-nodejs-r1867/" rel="">التعامل مع الملفات باستخدام الوحدة fs في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">مدخل إلى خادم الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">مدخل إلى HTTP: شرح التخاطب بين العميل والخادم</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%B1%D9%85%D9%88%D8%B2-%D8%A7%D9%84%D8%A5%D8%AC%D8%A7%D8%A8%D8%A9-%D9%81%D9%8A-http-r75/" rel="">رموز الإجابة في HTTP</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1868</guid><pubDate>Sun, 29 Jan 2023 16:04:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x644;&#x641;&#x627;&#x62A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x629; fs &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-fs-%D9%81%D9%8A-nodejs-r1867/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_01/1017666926_------fs--Node.png.49e1bff3894840e00503d5ce65f764ff.png" /></p>
<p>
	كثيرًا ما نحتاج للتعامل مع نظام الملفات، فمثلًا لتخزين بعض الملفات بعد تنزيلها، أو لترتيب بعض البيانات ضمن مجلدات أو لقراءة الملفات للتعامل مع محتوياتها ضمن بعض التطبيقات، وحتى تطبيقات النظم الخلفية أو أدوات واجهة سطر الأوامر CLI قد تحتاج أحيانًا لحفظ بعض البيانات إلى ملفات، والتطبيقات التي تتعامل مع البيانات تحتاج أحيانًا لتصديرها بمختلف الصيغ مثل <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> أو <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D9%85%D9%84%D9%81%D8%A7%D8%AA-csv-%D8%A8%D9%88%D8%A7%D8%B3%D8%B7%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r521/" rel="">CSV</a> أو ملفات برنامج إكسل، فكل تلك المتطلبات تحتاج للتعامل مع نظام الملفات ضمن نظام التشغيل التي تعمل عليه.
</p>

<p>
	توفر <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">نود</a> طريقة برمجية للتعامل مع الملفات باستخدام الوحدة البرمجية <a href="https://wiki.hsoub.com/Node.js/fs" rel="external"><code>‎fs‎</code></a>، وهي اختصار لجملة "نظام الملفات" أو "file system" حيث تحتوي على العديد من التوابع التي نحتاجها لقراءة الملفات أو الكتابة إليها أو حذفها، وهذه المزايا تجعل من <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">لغة جافاسكربت</a> لغة مفيدة لاستخدامها ضمن تطبيقات النظم الخلفية و أدوات <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a>.
</p>

<p>
	سنتعرف في هذا المقال على الوحدة البرمجية <code>‎fs‎</code> وسنستخدمها لقراءة الملفات وإنشاء ملفات جديدة والكتابة إليها وحذف الملفات وحتى نقل الملفات من مجلد إلى آخر، حيث توفر الوحدة البرمجية <code>‎fs‎</code> توابع للتعامل مع الملفات بالطريقتين المتزامنة synchronously واللامتزامنة asynchronously وباستخدام مجاري البيانات streams، حيث سنستخدم في هذا المقال الطريقة اللامتزامنة باستخدام <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/" rel="">الوعود Promises</a> وهي الطريقة الأكثر استخدامًا.
</p>

<h2>
	المستلزمات
</h2>

<ul>
	<li>
		نسخة نود مثبتة على الجهاز، والتي ستوفر الوحدة البرمجية <code>‎fs‎</code> التي سنتعامل معها، حيث استخدمنا في هذا المقال الإصدار رقم 10.22.0.
	</li>
	<li>
		سنستخدم ضمن الأمثلة الوعود في جافاسكربت مع صيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/" rel="">اللاتزامن والانتظار ‎async/await‎</a> للتعامل مع الملفات، وللتعرف أكثر على هذه الصيغة يمكنك مراجعة مقال <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%BA%D9%8A%D8%B1-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-nodejs-r1743/" rel="">طرق كتابة شيفرات غير متزامنة التنفيذ في Node.js</a> من <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">هذه السلسة</a>.
	</li>
</ul>

<h2>
	قراءة الملفات باستخدام ‎readFile()‎
</h2>

<p>
	سنطور في هذه الفقرة برنامجًا في نود لقراءة الملفات، وسنستعين بالتابع <a href="https://wiki.hsoub.com/Node.js/fs#filehandle.readFile.28options.29" rel="external"><code>‎readFile()‎</code></a> الذي توفره الوحدة البرمجية <code>‎fs‎</code> في نود لقراءة محتوى ملف معين وتخزينه ضمن متغير ثم طباعته إلى الطرفية.
</p>

<p>
	سنبدأ بإعداد المجلد الذي سيحوي على ملفات الأمثلة المستخدمة في هذا المقال ونُنشئ لذلك مجلدًا جديدًا بالاسم <code>‎node-files‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_23" style=""><span class="pln">$ mkdir node</span><span class="pun">-</span><span class="pln">files</span></pre>

<p>
	وندخل لذلك المجلد باستخدام الأمر <code>‎cd‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_25" style=""><span class="pln">$ cd node</span><span class="pun">-</span><span class="pln">files</span></pre>

<p>
	ننشئ داخل المجلد ملفين الأول هو الملف الذي سنحاول قراءته باستخدام البرنامج، والثاني هو ملف جافاسكربت للبرنامج الذي سنطوره، ونبدأ بإنشاء ملف جديد يحوي المحتوى النصي <code>‎greetings.txt‎</code>، وهنا سنُنشئ الملف عن طريق سطر الأوامر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_27" style=""><span class="pln">$ echo </span><span class="str">"hello, hola, bonjour, hallo"</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> greetings</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	في الأمر السابق سيطبع الأمر <code>‎echo‎</code> النص المُمرر له إلى الطرفية، واستخدمنا المعامل <code>‎&gt;‎</code> لإعادة توجيه خرج الأمر <code>‎echo‎</code> إلى الملف النصي الجديد <code>‎greetings.txt‎</code>.
</p>

<p>
	والآن ننشئ ملف <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">جافاسكربت</a> جديد للبرنامج بالاسم <code>‎readFile.js‎</code> ونفتحه باستخدام أي محرر نصوص، حيث سنستخدم ضمن أمثلتنا محرر النصوص <code>‎nano‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_29" style=""><span class="pln">$ nano readFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	يتكون البرنامج الذي سنكتبه من ثلاث أقسام رئيسية، حيث نبدأ أولًا باستيراد الوحدة البرمجية التي تحوي توابع التعامل مع الملفات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_31" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span></pre>

<p>
	تحوي الوحدة <code>‎fs‎</code> كافة التوابع المستخدمة في التعامل مع نظام الملفات، ونلاحظ كيف استوردنا منها الجزء <code>‎.promises‎</code> حيث كانت طريقة كتابة الشيفرة اللامتزامنة سابقًا ضمن الوحدة <code>‎fs‎</code> عبر استخدام دوال رد النداء callbacks، ولاحقًا وبعد أن انتشر استخدام الوعود كطريقة بديلة أضاف فريق التطوير في نود دعمًا لها ضمن الوحدة <code>‎fs‎</code>، حيث وبدءًا من الإصدار رقم 10 من نود أضيفت الخاصية <code>‎promises‎</code> ضمن كائن الوحدة البرمجية <code>‎fs‎</code> والتي تحوي التوابع التي تدعم طريقة الوعود، بينما بقي عمل الوحدة البرمجية <code>‎fs‎</code> الأساسية كما هي سابقًا باستخدام توابع رد النداء لدعم البرامج التي تستخدم الطريقة القديمة، وفي أمثلتنا سنستخدم نسخة التوابع التي تعتمد على الوعود.
</p>

<p>
	في القسم الثاني من البرنامج سنضيف دالة لامتزامنة لقراءة محتوى الملف، حيث يمكن تعريف <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r781/" rel="">الدوال اللامتزامنة في جافاسكربت</a> بإضافة الكلمة <code>‎async‎</code> في بدايتها، وبذلك نستطيع ضمن التابع انتظار نتيجة كائنات الوعود باستخدام الكلمة <code>‎await‎</code> مباشرةً بدلًا من ربط العمليات المتتالية باستخدام التابع <code>‎.then()‎</code>.
</p>

<p>
	والآن نعرف الدالة <code>‎readFile()‎</code> التي تقبل سلسلة نصية <code>‎filePath‎</code> تمثِّل مسار الملف الذي نود قراءته، حيث سنستعين بتوابع الوحدة <code>‎fs‎</code> لقراءة محتوى الملف المطلوب وتخزينه ضمن متغير باستخدام صيفة <code>‎async/await‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_21" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> readFile</span><span class="pun">(</span><span class="pln">filePath</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">filePath</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Got</span><span class="pln"> an error trying to read the file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن التقاط الأخطاء التي قد يرميها استدعاء التابع <code>‎fs.readFile()‎</code> باستخدام <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1%D8%8C-%D8%AC%D8%B1%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%B7-trycatch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r908/" rel=""><code>‎try...catch‎</code></a>، حيث نستدعي التابع <code>‎fs.readFile()‎</code> ضمن جسم <code>‎try‎</code> ثم نخزن النتيجة ضمن المتغير <code>‎data‎</code>، ويقبل ذلك التابع معاملًا وحيدًا إجباريًا وهو مسار الملف الذي نود قراءته، ويعيد كائن <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA%D8%A9-buffers-%D9%81%D9%8A-nodejs-r1805/" rel="">مخزن مؤقت <code>‎buffer‎</code></a> كنتيجة لعملية القراءة حيث يمكن لهذا الكائن أن يحوي أي نوع من الملفات، ولكي نطبع ذلك المحتوى إلى الطرفية يجب تحويله إلى سلسلة نصية باستخدام التابع <code>‎toString()‎</code> من كائن المخزن المؤقت.
</p>

<p>
	وفي حال رمي خطأ ما فالسبب يكون إما لعدم وجود الملف الذي نريد قراءته، أو لأن المستخدم لا يملك إذنًا لقراءته، ففي هذه الحالة سنطبع رسالة خطأ إلى الطرفية.
</p>

<p>
	أما القسم الثالث والأخير من البرنامج هو استدعاء دالة قراءة الملف مع تمرير اسم الملف <code>‎greetings.txt‎</code> ليصبح البرنامج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_34" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> readFile</span><span class="pun">(</span><span class="pln">filePath</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">filePath</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to read the file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

readFile</span><span class="pun">(</span><span class="str">'greetings.txt'</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه وفي حال كنت تستخدم أيضًا محرر النصوص nano يمكنك الخروج بالضغط على الاختصار ‎CTRL+X‎، وعند تنفيذ البرنامج سيقرأ المحتوى النصي للملف <code>‎greetings.txt‎</code> ويطبع محتواه إلى الطرفية، والآن ننفذ البرنامج عبر الأمر <code>‎node‎</code> لنرى النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_36" style=""><span class="pln">$ node readFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	بعد تنفيذ الأمر سيظهر الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_38" style=""><span class="pln">hello</span><span class="pun">,</span><span class="pln"> hola</span><span class="pun">,</span><span class="pln"> bonjour</span><span class="pun">,</span><span class="pln"> hallo</span></pre>

<p>
	وبذلك نكون قد استخدمنا التابع <code>‎readFile()‎</code> من الوحدة <code>‎fs‎</code> لقراءة محتوى الملف باستخدام صيغة <code>‎async/await‎</code>.
</p>

<p>
	<strong>ملاحظة</strong>: إذا كنت تستخدم إصدارًا قديمًا من نود وحاولت استخدام الوحدة <code>‎fs‎</code> بالطريقة السابقة سيظهر لك رسالة التحذير التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_40" style=""><span class="pun">(</span><span class="pln">node</span><span class="pun">:</span><span class="lit">13085</span><span class="pun">)</span><span class="pln"> </span><span class="typ">ExperimentalWarning</span><span class="pun">:</span><span class="pln"> </span><span class="typ">The</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">promises API is experimental</span></pre>

<p>
	حيث أن الخاصية <code>‎promises‎</code> من الوحدة <code>‎fs‎</code> تم اعتمادها رسميًا منذ الإصدار 10 من نود، وقبل ذلك كانت في المرحلة التجريبية وهذا سبب رسالة التحذير السابقة، ولاحقًا وتحديدًا ضمن إصدار نود رقم 12.6 أصبحت التوابع ضمن تلك الخاصية مستقرة وأزيلت رسالة التحذير تلك.
</p>

<p>
	الآن وبعد أن تعرفنا على طريقة قراءة الملفات باستخدام الوحدة <code>‎fs‎</code> سنتعلم في الفقرة التالية طريقة إنشاء الملفات الجديدة وكتابة المحتوى النصي إليها.
</p>

<h2>
	كتابة الملفات باستخدام ‎writeFile()‎
</h2>

<p>
	سنتعلم في هذه الفقرة طريقة كتابة الملفات باستخدام التابع <a href="https://wiki.hsoub.com/Node.js/fs#filehandle.writeFile.28data.2C_options.29" rel="external"><code>‎writeFile()‎</code></a> من الوحدة البرمجية <code>‎fs‎</code>، وذلك بكتابة ملف بصيغة CSV يحـوي على بيانات لفاتورة شراء، حيث سنبدأ بإنشاء ملف جديد وإضافة ترويسات عناوين الأعمدة له، ثم سنتعلم طريقة إضافة بيانات جديدة إلى نهاية الملف.
</p>

<p>
	نبدأ أولًا بإنشاء ملف جافاسكربت جديد للبرنامج ونفتحه باستخدام محرر النصوص كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_42" style=""><span class="pln">$ nano writeFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونستورد الوحدة <code>‎fs‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_44" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span></pre>

<p>
	وسنستخدم في هذا المثال أيضًا صيغة <code>‎async/await‎</code> لتعريف دالتين، الأولى لإنشاء ملف CSV جديد والثانية لكتابة بيانات جديدة إليه.
</p>

<p>
	نفتح الملف ضمن محرر النصوص ونضيف الدالة التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_46" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> openFile</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> csvHeaders </span><span class="pun">=</span><span class="pln"> </span><span class="str">'name,quantity,price'</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'groceries.csv'</span><span class="pun">,</span><span class="pln"> csvHeaders</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to write to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نُعرّف المتغير <code>‎csvHeaders‎</code> والذي يحتوي على عناوين رؤوس الأعمدة لملف CSV، ثم نستدعي التابع <code>‎writeFile()‎</code> من وحدة <code>‎fs‎</code> لإنشاء ملف جديد وكتابة البيانات إليه، حيث أن المعامل الأول المُمرر له هو مسار الملف الجديد، وإذا مررنا اسم الملف فقط فسيُنشَأ الملف الجديد ضمن المسار الحالي لتنفيذ البرنامج، وأما المعامل الثاني المُمرر هو البيانات التي نريد كتابتها ضمن الملف، وفي حالتنا هي عناوين الأعمدة الموجودة ضمن المتغير <code>‎csvHeaders‎</code>.
</p>

<p>
	والآن نضيف الدالة الثانية ومهمتها إضافة بيانات جديدة ضمن ملف الفاتورة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_48" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> openFile</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> csvHeaders </span><span class="pun">=</span><span class="pln"> </span><span class="str">'name,quantity,price'</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'groceries.csv'</span><span class="pun">,</span><span class="pln"> csvHeaders</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to write to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> addGroceryItem</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> quantity</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> csvLine </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">\n$</span><span class="pun">{</span><span class="pln">name</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">quantity</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">price</span><span class="pun">}`</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'groceries.csv'</span><span class="pun">,</span><span class="pln"> csvLine</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> flag</span><span class="pun">:</span><span class="pln"> </span><span class="str">'a'</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to write to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرفنا الدالة اللامتزامنة <code>‎addGroceryItem()‎</code> التي تقبل ثلاثة مُعاملات، وهي اسم المنتج والكمية والسعر للقطعة الواحدة منه، ويتم إنشاء السطر الجديد الذي نود كتابته إلى الملف باستخدام قالب نص template literal وتخزينه ضمن المتغير <code>‎csvLine‎</code>، ثم نستدعي التابع <code>‎writeFile()‎</code> كما فعلنا سابقًا ضمن التابع الأول <code>‎openFile()‎</code>، ولكن هذه المرة سنمرر كائن جافاسكربت كمعامل ثالث يحتوي على المفتاح <code>‎flag‎</code> بالقيمة <code>‎a‎</code>، وتعبر تلك القيمة عن الرايات المستخدمة للتعامل مع نظام الملفات، والراية <code>‎a‎</code> هنا تخبر نود بأننا نريد إضافة ذلك المحتوى إلى الملف، وليس إعادة كتابة محتوى الملف كاملًا، وفي حال لم نمرر أي راية عند كتابة الملف كما فعلنا ضمن الدالة الأولى فإن القيمة الافتراضية هي الراية <code>‎w‎</code> والتي تعني إنشاء ملف جديد في حال لم يكن الملف موجودًا، وإذا كان موجودًا سيتم تبديله وإعادة كتابة محتواه كاملًا، ويمكنك الرجوع إلى <a href="https://wiki.hsoub.com/Node.js/fs#.D8.B1.D8.A7.D9.8A.D8.A7.D8.AA_.D9.86.D8.B8.D8.A7.D9.85_.D8.A7.D9.84.D9.85.D9.84.D9.81.D8.A7.D8.AA" rel="external">التوثيق الرسمي</a> لتلك الرايات من نود للتعرف عليها أكثر.
</p>

<p>
	والآن لننهي كتابة البرنامج باستدعاء الدوال التي عرّفناها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_50" style=""><span class="pun">...</span><span class="pln">
</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> addGroceryItem</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> quantity</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> csvLine </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">\n$</span><span class="pun">{</span><span class="pln">name</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">quantity</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">price</span><span class="pun">}`</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'groceries.csv'</span><span class="pun">,</span><span class="pln"> csvLine</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> flag</span><span class="pun">:</span><span class="pln"> </span><span class="str">'a'</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to write to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">(</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> openFile</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> addGroceryItem</span><span class="pun">(</span><span class="str">'eggs'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.50</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> addGroceryItem</span><span class="pun">(</span><span class="str">'nutella'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">);</span><span class="pln">
</span><span class="pun">})();</span></pre>

<p>
	وبما أن الدوال التي سنستدعيها لامتزامنة، فيمكننا تغليفها بدالة لامتزامنة واستدعاءها مباشرة كي نستطيع استخدام <code>‎await‎</code> لانتظار إكمال تنفيذها، وذلك لأنه لا يمكن ضمن إصدار نود الذي نستخدمه حاليًا استخدام <code>await</code> مباشرة ضمن النطاق العام global scope، بل حصرًا ضمن دوال لامتزامنة تُستخدم ضمن تعريفها الكلمة <code>async</code>، ولا حاجة لتسمية تلك الدالة ويمكننا تعريفها كدالة مجهولة لأن الغرض منها فقط التغليف والتنفيذ المباشر ولن نشير إليها من أي مكان آخر.
</p>

<p>
	وبما أن كلا الدالتين <code>‎openFile()‎</code> و <code>‎addGroceryItem()‎</code> لا متزامنين فبدون انتظار نتيجة استدعاء الدالة الأولى ثم استدعاء الثانية لا يمكن ضمان ترتيب التنفيذ وبالتالي ترتيب المحتوى ضمن الملف الذي نريد إنشاءه، لذلك عرفنا دالة التغليف تلك الغير متزامنة بين قوسين وأضفنا قوسي الاستدعاء في النهاية قبل الفاصلة المنقوطة كي لاستدعائها مباشرةً، وتُدعى تلك الصيغة بصيغة التنفيذ المباشر لدالة Immediately-Invoked Function Expression أو IIFE، وباستخدام تلك الصيغة في مثالنا نضمن احتواء ملف CSV الجديد على الترويسات بدايةً ثم أول سطر للمنتج <code>‎eggs‎</code> وبعده المنتج الثاني <code>‎nutella‎</code>.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج باستخدام الأمر <code>‎node‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_52" style=""><span class="pln">$ node writeFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	لن نلاحظ أي خرج من التنفيذ ولكن سنلاحظ إنشاء ملف جديد ضمن المجلد الحالي ويمكن معاينة محتوى الملف <code>‎groceries.csv‎</code> باستخدام الأمر <code>‎cat‎</code> كالتالي:
</p>

<pre class="ipsCode">$ cat groceries.csv
</pre>

<p>
	ليظهر الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_54" style=""><span class="pln">name</span><span class="pun">,</span><span class="pln">quantity</span><span class="pun">,</span><span class="pln">price
eggs</span><span class="pun">,</span><span class="lit">12</span><span class="pun">,</span><span class="lit">1.5</span><span class="pln">
nutella</span><span class="pun">,</span><span class="lit">1</span><span class="pun">,</span><span class="lit">4</span></pre>

<p>
	أنشأت الدالة <code>‎openFile()‎</code> ملف CSV وأضافت الترويسات له، ثم أضافت استدعاءات الدالة <code>‎addGroceryItem()‎</code> التي تليها سطرين من البيانات إلى ذلك الملف، وبذلك نكون قد تعلمنا طريقة استخدام التابع <code>‎writeFile()‎</code> لإنشاء الملفات الجديدة والتعديل على محتواها.
</p>

<p>
	سنتعلم في الفقرة التالية كيف يمكننا حذف الملفات في حال أردنا إنشاء ملفات مؤقتة مثلًا، أو لإزالة بعض الملفات لتوفير مساحة التخزين على الجهاز.
</p>

<h2>
	حذف الملفات باستخدام ‎unlink()‎
</h2>

<p>
	سنتعلم في هذه الفقرة طريقة حذف الملفات باستخدام التابع <a href="https://wiki.hsoub.com/Node.js/fs#fsPromises.unlink.28path.29" rel="external"><code>‎unlink()‎</code></a> من الوحدة البرمجية <code>‎fs‎</code>، حيث سنكتب برنامجًا لحذف الملف <code>‎groceries.csv‎</code> الذي أنشأناه في الفقرة السابقة.
</p>

<p>
	نبدأ بإنشاء ملف جافاسكربت جديد بالاسم <code>deleteFile.js</code> نُعرف ضمنه الدالة اللامتزامنة <code>‎deleteFile()‎</code> التي تقبل مسار الملف المراد حذفه، وبدورها ستمرر ذلك المعامل إلى التابع <code>‎unlink()‎</code> والذي سيحذف ذلك الملف من نظام الملفات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_56" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> deleteFile</span><span class="pun">(</span><span class="pln">filePath</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">unlink</span><span class="pun">(</span><span class="pln">filePath</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Deleted</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">filePath</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to </span><span class="kwd">delete</span><span class="pln"> the file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

deleteFile</span><span class="pun">(</span><span class="str">'groceries.csv'</span><span class="pun">);</span></pre>

<p>
	<strong>تحذير</strong>: لن تُنقل الملفات المحذوفة باستخدام التابع <code>‎unlink()‎</code> إلى سلة المحذوفات بل ستُحذف نهائيًا من نظام الملفات، لذا تلك العملية لا يمكن الرجوع عنها ويجب الحذر والتأكد من الملفات التي نحاول حذفها قبل تنفيذ البرنامج.
</p>

<p>
	والآن نخرج من الملف وننفذه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_58" style=""><span class="pln">$ node deleteFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_60" style=""><span class="typ">Deleted</span><span class="pln"> groceries</span><span class="pun">.</span><span class="pln">csv</span></pre>

<p>
	نستعرض الملفات الموجودة حاليًا بعد التنفيذ للتأكد من نجاح عملية الحذف باستخدام الأمر <code>‎ls‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_62" style=""><span class="pln">$ ls</span></pre>

<p>
	ليظهر لنا الملفات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_64" style=""><span class="pln">deleteFile</span><span class="pun">.</span><span class="pln">js   greetings</span><span class="pun">.</span><span class="pln">txt   readFile</span><span class="pun">.</span><span class="pln">js     writeFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ حذف الملف بنجاح باستخدام التابع <code>‎unlink()‎</code>، وبذلك نكون قد تعلمنا طريقة قراءة وكتابة وحذف الملفات، وسنتعلم في الفقرة التالية كيف يمكن نقل الملفات من مجلد لآخر، لنكون بذلك قد تعلمنا كافة العمليات التي تسمح بإدارة الملفات عن طريق نود.
</p>

<h2>
	نقل الملفات باستخدام ‎rename()‎
</h2>

<p>
	تُستخدم المجلدات لتنظيم وترتيب الملفات معًا، لذا من المفيد تعلم طريقة نقل تلك الملفات برمجيًا، حيث يتم ذلك في نود باستخدام التابع <a href="https://wiki.hsoub.com/Node.js/fs#fsPromises.rename.28oldPath.2C_newPath.29" rel="external"><code>‎rename()‎</code></a> من الوحدة <code>‎fs‎</code>، وسنتعلم طريقة استخدامه بنقل الملف السابق <code>‎greetings.txt‎</code> إلى مجلد جديد مع إعادة تسميته.
</p>

<p>
	نبدأ بإنشاء ذلك مجلد جديد بالاسم <code>‎test-data‎</code> ضمن المجلد الحالي كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_66" style=""><span class="pln">$ mkdir test</span><span class="pun">-</span><span class="pln">data</span></pre>

<p>
	ونُنشئ نسخة عن الملف <code>‎greetings.txt‎</code> بتنفيذ أمر النسخ <code>‎cp‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_68" style=""><span class="pln">$ cp greetings</span><span class="pun">.</span><span class="pln">txt greetings</span><span class="pun">-</span><span class="lit">2.txt</span></pre>

<p>
	ثم نُنشئ ملف جافاسكربت للبرنامج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_70" style=""><span class="pln">$ nano moveFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونُعرف ضمنه الدالة <code>‎moveFile()‎</code> لنقل الملف، والتي ستستدعي بدورها التابع <code>‎rename()‎</code> الذي يقبل مسار الملف المراد نقله كمعامل أول، ثم المسار الجديد الوجهة كمعامل ثانِ، ففي حالتنا نريد استخدام الدالة <code>‎moveFile()‎</code> لنقل الملف الجديد <code>‎greetings-2.txt‎</code> إلى المجلد الذي أنشأناه <code>‎test-data‎</code> مع إعادة تسمية ذلك الملف إلى <code>‎salutations.txt‎</code>، ولذلك نضيف الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_73" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">async</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> moveFile</span><span class="pun">(</span><span class="pln">source</span><span class="pun">,</span><span class="pln"> destination</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">rename</span><span class="pun">(</span><span class="pln">source</span><span class="pun">,</span><span class="pln"> destination</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Moved</span><span class="pln"> file from $</span><span class="pun">{</span><span class="pln">source</span><span class="pun">}</span><span class="pln"> to $</span><span class="pun">{</span><span class="pln">destination</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> an error trying to move the file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

moveFile</span><span class="pun">(</span><span class="str">'greetings-2.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test-data/salutations.txt'</span><span class="pun">);</span></pre>

<p>
	كما ذكرنا سابقًا فالتابع <code>‎rename()‎</code> يقبل معاملين هما المسار المصدر والوجهة لنقل الملف، ويمكن استخدام هذا التابع إما لنقل الملفات من مجلد لآخر أو لإعادة تسمية الملفات، أو نقل وإعادة تسمية ملف ما معًا، وهو ما نريد تنفيذه في مثالنا.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذه باستخدام الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_75" style=""><span class="pln">$ node moveFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_77" style=""><span class="typ">Moved</span><span class="pln"> file from greetings</span><span class="pun">-</span><span class="lit">2.txt</span><span class="pln"> to test</span><span class="pun">-</span><span class="pln">data</span><span class="pun">/</span><span class="pln">salutations</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	نستعرض الملفات الموجودة حاليًا بعد التنفيذ للتأكد من نجاح عملية النقل باستخدام الأمر <code>‎ls‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_79" style=""><span class="pln">$ ls</span></pre>

<p>
	ليظهر لنا الملفات والمجلدات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_81" style=""><span class="pln">deleteFile</span><span class="pun">.</span><span class="pln">js   greetings</span><span class="pun">.</span><span class="pln">txt   moveFile</span><span class="pun">.</span><span class="pln">js     readFile</span><span class="pun">.</span><span class="pln">js     test</span><span class="pun">-</span><span class="pln">data       writeFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونستخدم الأمر <code>‎ls‎</code> مجددًا لعرض الملفات ضمن المجلد الوجهة <code>‎test-data‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_83" style=""><span class="pln">$ ls test</span><span class="pun">-</span><span class="pln">data</span></pre>

<p>
	ليظهر لنا الملف الذي نقلناه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3242_85" style=""><span class="pln">salutations</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	وبذلك نكون قد تعلمنا كيف يمكن استخدام التابع <code>‎rename()‎</code> لنقل الملفات من مجلد لآخر مع إعادة تسمية الملف ضمن نفس العملية.
</p>

<h2>
	خاتمة
</h2>

<p>
	تعرفنا في هذا المقال على مختلف عمليات إدارة الملفات ضمن نود، بداية بقراءة محتوى الملفات باستخدام <code>‎readFile()‎</code> ثم إنشاء ملفات جديدة وكتابة البيانات إليها باستخدام <code>‎writeFile()‎</code> ثم طريقة حذف الملفات باستخدام <code>‎unlink()‎</code> ونقلها وإعادة تسميتها باستخدام <code>‎rename()‎</code>.
</p>

<p>
	إنَّ التعامل مع الملفات من المهام الضرورية في نود فقد تحتاج البرامج أحيانًا إلى تصدير بعض الملفات للمستخدم أو تخزين البيانات الخاصة بها ضمن الملفات لاستعادتها لاحقًا، ولذلك توفر الوحدة البرمجية <code>‎fs‎</code> في نود كل التوابع الضرورية للتعامل مع الملفات في نود، ويمكنك الرجوع إلى <a href="https://wiki.hsoub.com/Node.js/fs" rel="external">التوثيق الرسمي</a> للوحدة البرمجية <code>‎fs‎</code> من نود للتعرف عليها أكثر.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-work-with-files-using-the-fs-module-in-node-js" rel="external nofollow">How To Work with Files using the fs Module in Node.js</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%A8%D9%86%D8%A7%D8%A1-child-process-%D9%81%D9%8A-nodejs-r1808/" rel="">التعامل مع العمليات الأبناء Child Process في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-nodejs-r1469/" rel="">التعامل مع الملفات في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">إدارة الوحدات البرمجية في Node.js باستخدام npm وملف package.json</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1867</guid><pubDate>Wed, 25 Jan 2023 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x639;&#x645;&#x644;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x623;&#x628;&#x646;&#x627;&#x621; Child Process &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%A8%D9%86%D8%A7%D8%A1-child-process-%D9%81%D9%8A-nodejs-r1808/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/638af4ea48d34_-----Node.png.e84a684123c2413e9f76dc697e11f5f0.png" /></p>

<p>
	عند تشغيل أي برنامج في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">نود Node.js</a> ستعمل نسخة منه افتراضيًا ضمن عملية process واحدة في <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a>، وسيُنفذ فيها البرنامج ضمن خيط معالجة thread وحيد، وكما تعلمنا في المقال الخامس <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%BA%D9%8A%D8%B1-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-nodejs-r1743/" rel="">طرق كتابة شيفرات غير متزامنة التنفيذ في Node.js</a> من <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">هذه السلسلة</a> فإن تنفيذ البرنامج ضمن خيط وحيد ضمن العملية سيؤدي لأن تعيق العمليات التي تحتاج مدة طويلة لتنفيذها في <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">جافاسكربت</a> تنفيذ العمليات أو الشيفرات التي تليها ضمن خيط التنفيذ لحين انتهاءها، وهنا يأتي دور إنشاء عملية ابن child process منفصلة عن العملية الرئيسية، وهي عملية تُنشئها عملية أخرى وتُستخدم لتنفيذ المهام الطويلة، وبهذه الطريقة يمكن لنظام التشغيل تنفيذ كلا العمليتين الأب والابن معًا أو بنفس الوقت على التوازي دون أن يعيق أي منهما تنفيذ الآخر.
</p>

<p>
	توفر نود لذلك الغرض الوحدة البرمجية <a href="https://wiki.hsoub.com/Node.js/child_process#.D8.A7.D9.84.D8.B5.D9.86.D9.81_ChildProcess" rel="external"><code>‎child_process‎</code></a> التي تحتوي على توابع عدة تساعد في إنشاء عمليات جديدة، وحتى توابع للتعامل مع نظام التشغيل مباشرةً وتنفيذ الأوامر ضمن الصدفة shell، لذا يمكن لمسؤولي إدارة النظام الاستفادة من نود في تنفيذ أوامر الصدفة لإدارة نظام التشغيل وترتيب تلك الأوامر ضمن وحدات برمجية بدلًا من تنفيذ ملفات أوامر الصدفة مباشرةً.
</p>

<p>
	سنتعلم في هذا المقال طرق إنشاء عمليات أبناء بتطبيق عدة أمثلة حيث سننشئ تلك العمليات بالاستعانة بالوحدة البرمجية <code>‎child_process‎</code> ونعاين نتيجة تنفيذها على شكل مخزن مؤقت buffer أو سلسلة نصية باستخدام التابع <code>‎exec()‎</code>، وسنتعلم كيف يمكن قراءة نتيجة تنفيذ تلك العملية من مجرى للبيانات data stream باستخدام التابع <code>‎spawn()‎</code>، ثم سننفذ برنامج نود آخر ضمن عملية منفصلة باستخدام <code>‎fork()‎</code> ونتعلم طريقة التواصل معه أثناء تشغيله، وسنطبق هذه الأفكار على مثال لبرنامج مهمته عرض قائمة محتويات مجلد ما، وبرنامج آخر للبحث عن الملفات، وآخر لخادم ويب يدعم عدة مسارات فرعية.
</p>

<h2>
	المستلزمات
</h2>

<p>
	هذا المقال جزء من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">دليل تعلم Node.js</a> لذا يجب قبل قراءته:
</p>

<ul>
<li>
		تثبيت نسخة نود على الجهاز، حيث استخدمنا في هذا المقال الإصدار رقم 10.22.0.
	</li>
	<li>
		معرفة عن طريقة عمل <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خوادم الويب</a> في نود، ويمكنك مراجعة مقال السابع <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-http-r1745/" rel="">إنشاء خادم ويب في Node.js</a> من هذه السلسلة.
	</li>
</ul>
<h2>
	إنشاء عملية ابن باستخدام ‎exec
</h2>

<p>
	عادة ما ننشئ عملية ابن لتنفيذ بعض الأوامر ضمن نظام التشغيل، فمثلًا للتعديل على خرج برنامج ما في نود بعد تنفيذه ضمن الصدفة نمرر خرج ذلك البرنامج أو نعيد توجيهه إلى أمر آخر، وهنا يأتي دور التابع <a href="https://wiki.hsoub.com/Node.js/child_process#child_process.exec.28command.5B.2C_options.5D.5B.2C_callback.5D.29" rel="external"><code>‎exec()‎</code></a> الذي يمكننا من إنشاء عملية صدفة جديدة بنفس الطريقة وتنفيذ الأوامر ضمنها لكن من قبل برنامج نود، حيث يُخزَّن خرج ذلك الأمر ضمن مخزن مؤقت في الذاكرة ويمكننا بعدها الوصول إليه بتمرير دالة رد نداء callback function للتابع <code>‎exec()‎</code>.
</p>

<p>
	لنبدأ بإنشاء عملية ابن جديدة في نود ولكن أولًا ننشئ مجلد جديد سيحتوي على البرامج التي سنعمل عليها في هذا المقال بالاسم <code>‎child-processes‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_11" style="">
<span class="pln">$ mkdir child</span><span class="pun">-</span><span class="pln">processes</span></pre>

<p>
	وندخل إلى المجلد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_13" style="">
<span class="pln">$ cd child</span><span class="pun">-</span><span class="pln">processes</span></pre>

<p>
	ننشئ ملف <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> جديد بالاسم ‎listFiles.js‎ ونفتحه باستخدام أي محرر نصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_15" style="">
<span class="pln">$ nano listFiles</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنستخدم في هذه الوحدة البرمجية التابع <code>‎exec()‎</code> لتنفيذ أمر عرض الملفات والمجلدات ضمن المجلد الحالي <code>‎ls‎</code>، ومهمة برنامجنا هو قراءة خرج ذلك الأمر وعرضه للمستخدم، لذا نضيف الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_19" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> exec </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">);</span><span class="pln">

exec</span><span class="pun">(</span><span class="str">'ls -lh'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> stdout</span><span class="pun">,</span><span class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">stderr</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">stderr</span><span class="pun">}‎`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="pln">stdout</span><span class="pun">:</span><span class="pln">\n$</span><span class="pun">{</span><span class="pln">stdout</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بدأنا باستيراد التابع <code>‎exec()‎</code> من الوحدة <code>‎child_process‎</code>، ثم استدعيناه بتمرير الأمر الذي نريد تنفيذه كمعامل أول، وهو الأمر <code>‎ls -lh‎</code> الذي سيعرض كافة الملفات والمجلدات الموجودة ضمن المجلد الحالي بصيغة مفصلة، وسيعرض وحدة الحجم للملفات بصيغة مقروءة، وسيعرض أيضًا الحجم الكلي لها في أول سطر من الخرج.
</p>

<p>
	والمعامل الثاني المُمرر هو دالة رد النداء تقبل ثلاث معاملات، الأول كائن الخطأ <code>‎error‎</code> والثاني الخرج القياسي <code>‎stdout‎</code> والثالث خرج الخطأ <code>‎stderr‎</code>، فإذا فشل تنفيذ الأمر سيحتوي المعامل <code>‎error‎</code> على كائن خطأ يشرح سبب حدوثه مثلًا عندما لا تعثر الصدفة على الأمر الذي نحاول تنفيذه، وإذا نُفذ الأمر بنجاح سيحتوي المعامل الثاني <code>‎stdout‎</code> على البيانات التي تكتب في مجرى الخرج القياسي، أما المعامل الثالث <code>‎stderr‎</code> سيمثل مجرى الخطأ القياسي ويحوي على أي بيانات يكتبها الأمر إلى ذلك المجرى.
</p>

<p>
	<strong>ملاحظة</strong>: يوجد فرق بين كائن الخطأ <code>‎error‎</code> ومجرى الخطأ <code>‎stderr‎</code>، فإذا فشل تنفيذ الأمر كليًا سيمثل المعامل <code>‎error‎</code> ذلك الخطأ، بينما إذا نُفذ الأمر وكتب هو إلى مجرى الخطأ فيمكننا قراءة أي بيانات تكتب فيه من المعامل <code>‎stderr‎</code>، ويفضل دومًا معالجة كل احتمالات الخرج الممكنة من كلا هذين المعاملين مع أي عملية ابن.
</p>

<p>
	نتحقق داخل دالة رد النداء الممررة من وجود أي خطأ أولًا، فإذا وُجد خطأ سنطبع رسالة الخطأ <code>‎message‎</code> وهي الخاصية ضمن كائن الخطأ <code>‎Error‎</code> باستدعاء أمر طباعة الخطأ <code>‎console.error()‎</code>، ثم ننهي تنفيذ التابع مباشرةً باستخدام <code>‎return‎</code>، وبعدها نتحقق من طباعة الأمر لأي أخطاء تُكتَب ضمن مجرى الخطأ القياسي وإذا وجد نطبع الرسالة وننهي تنفيذ التابع باستخدام <code>‎return‎</code> أيضًا، وإلا يكون الأمر قد نُفِّذ بنجاح، ونطبع حينها الخرج إلى الطرفية باستخدام <code>‎console.log()‎</code>.
</p>

<p>
	والآن نخرج من الملف ثم ننفذ البرنامج ونعاين النتيجة، وفي حال كنت تستخدم محرر النصوص نانو ‎nano‎ كما في أمثلتنا يمكنك الخروج منه بالضغط على الاختصار ‎CTRL+X‎، ولتشغيل البرنامج ننفذ الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_21" style="">
<span class="pln">$ node listFiles</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_23" style="">
<span class="pln">stdout</span><span class="pun">:</span><span class="pln">
total </span><span class="lit">4.0K</span><span class="pln">
</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">r</span><span class="pun">--</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> hassan hassan </span><span class="lit">280</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">27</span><span class="pln"> </span><span class="lit">16</span><span class="pun">:</span><span class="lit">35</span><span class="pln"> listFiles</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	وهو محتوى المجلد ‎child-processes‎ مع تفاصيل عن الملفات الموجودة ضمنه، وحجم المجلد الكلي في السطر الأول، وهو ما يدل على تنفيذ البرنامج ‎listFiles.js‎ للأمر <code>‎ls -lh‎</code> ضمن الصدفة وقراءة نتيجته وطباعتها بنجاح.
</p>

<p>
	والآن سنتعرف على طريقة مختلفة لتنفيذ عملية ما على التوازي مع العملية الحالية، حيث توفر الوحدة <code>‎child_process‎</code> التابع <code>‎execFile()‎</code> الذي يُمكننا من تشغيل الملفات التنفيذية، والفرق بينه وبين الأمر <code>‎exec()‎</code> أن المعامل الأول المُمرر له سيكون مسار الملف التنفيذي الذي نريد تشغيله بدلًا من أمر يراد تنفيذه في الصدفة، وبطريقة مشابهة لعمل التابع <code>‎exec()‎</code> سيُخزن ناتج التنفيذ ضمن مخزن مؤقت يمكننا الوصول إليه ضمن دالة رد النداء الممررة، والتي تقبل المعاملات الثلاث نفسها <code>‎error‎</code> و <code>‎stdout‎</code> و <code>‎stderr‎</code>.
</p>

<p>
	<strong>ملاحظة</strong>: يجب الانتباه أنه لا يمكن تشغيل الملفات التنفيذية ذات الصيغ <code>‎.bat‎</code> و <code>‎.cmd‎</code> على ويندوز، وذلك لأن التابع <code>‎execFile()‎</code> لا ينشئ الصدفة التي تحتاج إليها تلك الملفات لتشغيلها، بينما على الأنظمة مثل يونكس و <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس</a> و نظام ماك لا تحتاج الملفات التنفيذية إلى صدفة لتشغيلها، لذا لتنفيذ الملفات التنفيذية على ويندوز يمكن استخدام التابع <code>‎exec()‎</code> لأنه سيُنشئ لها صدفة عند التنفيذ، أو يمكن استدعاؤها باستخدام التابع <code>‎spawn()‎</code> وهو ما سنتعرف عليه لاحقًا، ولكن الملفات التنفيذية ذات اللاحقة <code>‎.exe‎</code> يمكن تشغيلها ضمن ويندوز باستخدام <code>‎execFile()‎</code> مباشرةً، حيث أنها لا تحتاج لصدفة لتشغيلها.
</p>

<p>
	والآن نبدأ بإنشاء الملف التنفيذي الذي سنحاول تنفيذه باستخدام <code>‎execFile()‎</code>، حيث سنكتب نصًا برمجيًا ضمن <a href="https://wiki.hsoub.com/Bash" rel="external">صدفة باش bash</a> مهمته تنزيل صورة شعار بيئة نود من الموقع الرئيسي لها، ثم يعيد ترميز صورة الشعار تلك بصيغة Base64 لنتعامل معها كسلسلة نصية بمحارف ASCII، ونبدأ بإنشاء ملف تنفيذي جديد بالاسم ‎processNodejsImage.sh‎:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_26" style="">
<span class="pln">$ nano processNodejsImage</span><span class="pun">.</span><span class="pln">sh</span></pre>

<p>
	ونضيف إليه الشيفرة التالية لتحميل وتحويل صورة الشعار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_28" style="">
<span class="pun">#!</span><span class="str">/bin/</span><span class="pln">bash
curl </span><span class="pun">-</span><span class="pln">s https</span><span class="pun">:</span><span class="com">//nodejs.org/static/images/logos/nodejs-new-pantone-black.svg &gt; nodejs-logo.svg</span><span class="pln">
base64 nodejs</span><span class="pun">-</span><span class="pln">logo</span><span class="pun">.</span><span class="pln">svg</span></pre>

<p>
	التعليمة في السطر الأول تسمى شِبانغ shebang، وتستخدم ضمن أنظمة يونكس ولينكس ونظام ماك لتحديد الصدفة التي نريد تشغيل النص البرمجي أو السكربت ضمنها، والتعليمة التالية هي الأمر <a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AC%D9%84%D8%B3%D8%A7%D8%AA-%D9%88%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D8%A7%D8%B1%D8%AA%D8%A8%D8%A7%D8%B7-%D9%88%D9%85%D9%83%D8%AA%D8%A8%D8%A9-curl-%D9%81%D9%8A-php-r1081/" rel=""><code>‎curl‎</code></a> وهي أداة <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر أوامر</a> تمكننا من نقل البيانات من وإلى الخوادم، ويمكننا الاستفادة منها لتنزيل شعار نود من الموقع الرئيسي له، ثم نعيد توجيه الخرج لحفظ الصورة بعد تنزيلها إلى ملف بالاسم ‎nodejs-logo.svg‎، أما التعليمة الأخيرة تستخدم الأداة <code>‎base64‎</code> لإعادة ترميز محتوى ملف الشعار nodejs-logo.svg‎ الذي نزلناه سابقًا، ثم سيُطبَع نتيجة الترميز إلى الطرفية أي مجرى الخرج القياسي وهو خرج تنفيذ النص البرمجي هذا بالكامل.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونضيف إذن تنفيذ هذا النص البرمجي لكي نستطيع تنفيذه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_31" style="">
<span class="pln">$ chmod u</span><span class="pun">+</span><span class="pln">x processNodejsImage</span><span class="pun">.</span><span class="pln">sh</span></pre>

<p>
	يمنح هذا الأمر المستخدم الحالي صلاحية التنفيذ لذلك الملف.
</p>

<p>
	يمكننا الآن البدء بكتابة برنامج نود الذي سيُنفذ ذلك النص البرمجي باستخدام التابع <code>‎execFile()‎</code> ضمن عملية ابن منفصلة ثم طباعة خرج التنفيذ، لذا نُنشئ ملف جافاسكربت جديد بالاسم ‎getNodejsImage.js‎:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_33" style="">
<span class="pln">$ nano getNodejsImage</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونكتب الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_35" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> execFile </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">);</span><span class="pln">

execFile</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/processNodejsImage.sh'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> stdout</span><span class="pun">,</span><span class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">stderr</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">stderr</span><span class="pun">}‎`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="pln">stdout</span><span class="pun">:</span><span class="pln">\n$</span><span class="pun">{</span><span class="pln">stdout</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	استوردنا التابع <code>‎execFile()‎</code> من الوحدة <code>‎child_process‎</code> واستدعيناه بتمرير مسار ملف النص البرمجي، حيث استفدنا من قيمة الثابت <code>‎__dirname‎</code> الذي توفره نود للحصول على مسار المجلد الحالي الذي يحتوي على النص البرمجي، وبذلك يمكن للبرنامج الإشارة إلى النص البرمجي ‎processNodejsImage.sh‎ دومًا مهما كان نظام التشغيل الذي ينفذه أو مكان تنفيذ البرنامج ‎getNodejsImage.js‎ على نظام الملفات، وفي حالتنا يجب أن يكون مكان كل من الملفين ‎getNodejsImage.js‎ و ‎processNodejsImage.sh‎ في نفس المجلد.
</p>

<p>
	أما المعامل الثاني المُمرر هو رد نداء ويقبل ثلاثة معاملات، الأول كائن الخطأ <code>‎error‎</code> والثاني الخرج القياسي <code>‎stdout‎</code> والثالث خرج الخطأ <code>‎stderr‎</code>، وكما فعلنا سابقًا عند استخدام <code>‎exec()‎</code> سنتحقق من حالة وخرج التنفيذ ونطبعها إلى الطرفية.
</p>

<p>
	والآن نحفظ الملف ونخرج من محرر النصوص ثم نشغله باستخدام الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_37" style="">
<span class="pln">$ node getNodejsImage</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	لنحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_39" style="">
<span class="pln">stdout</span><span class="pun">:</span><span class="pln">
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDQyLjQgMjcwLjkiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjE4MC43IiB5MT0iODAuNyIge
</span><span class="pun">...</span></pre>

<p>
	تجاهلنا عرض الخرج كاملًا بسبب حجمه الكبير، ولكن النص البرمجي ‎processNodejsImage.sh‎ نزل الصورة أولًا بعدها أعاد ترميزها بصيغة base64، ويمكن التأكد من ذلك بمعاينة الصورة التي تم تنزيلها والموجودة ضمن المجلد الحالي، ولنتأكد يمكننا تنفيذ البرنامج السابق ‎listFiles.js‎ لمعاينة المحتوى الجديد للمجلد الحالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_41" style="">
<span class="pln">$ node listFiles</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنلاحظ ظهور الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_43" style="">
<span class="pln">stdout</span><span class="pun">:</span><span class="pln">
total </span><span class="lit">20K</span><span class="pln">
</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">r</span><span class="pun">--</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> hassan hassan  </span><span class="lit">316</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">27</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">56</span><span class="pln"> getNodejsImage</span><span class="pun">.</span><span class="pln">js
</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">r</span><span class="pun">--</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> hassan hassan  </span><span class="lit">280</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">27</span><span class="pln"> </span><span class="lit">16</span><span class="pun">:</span><span class="lit">35</span><span class="pln"> listFiles</span><span class="pun">.</span><span class="pln">js
</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">rw</span><span class="pun">-</span><span class="pln">r</span><span class="pun">--</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> hassan hassan </span><span class="lit">5.4K</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">27</span><span class="pln"> </span><span class="lit">18</span><span class="pun">:</span><span class="lit">01</span><span class="pln"> nodejs</span><span class="pun">-</span><span class="pln">logo</span><span class="pun">.</span><span class="pln">svg
</span><span class="pun">-</span><span class="pln">rwxrw</span><span class="pun">-</span><span class="pln">r</span><span class="pun">--</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> hassan hassan  </span><span class="lit">129</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">27</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">56</span><span class="pln"> processNodejsImage</span><span class="pun">.</span><span class="pln">sh</span></pre>

<p>
	بذلك نكون قد نفذنا بنجاح النص البرمجي ‎processNodejsImage.sh‎ ضمن عملية ابن من برنامج نود باستخدام التابع <code>‎execFile()‎</code>.
</p>

<p>
	تعلمنا في هذه الفقرة كيف يمكن للتابعين <code>‎exec()‎</code> و <code>‎execFile()‎</code> تنفيذ الأوامر ضمن صدفة نظام التشغيل داخل عملية ابن منفصلة في نود، وتوفر نود أيضًا التابع <code>‎spawn()‎</code> والذي يشبه في عمله هذين التابعين، ولكن الفرق في عمله أنه لا يقرأ خرج تنفيذ الأمر دفعة واحدة بل على عدة دفعات ضمن مجرى للبيانات stream، وهو ما سنتعرف عليه بالتفصيل في الفقرة التالية.
</p>

<h2>
	إنشاء عملية ابن باستخدام ‎spawn
</h2>

<p>
	يمكن استدعاء التابع <a href="https://wiki.hsoub.com/Node.js/child_process#child_process.spawn.28command.5B.2C_args.5D.5B.2C_options.5D.29" rel="external"><code>‎spawn()‎</code></a> لتنفيذ الأوامر ضمن عملية منفصلة والحصول على بيانات الخرج من ذلك الأمر عن طريق <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> لمجرى البيانات في نود، وذلك عبر الاستماع لبعض الأحداث المعينة على كائن المجرى لخرج ذلك الأمر.
</p>

<p>
	مجاري البيانات streams في نود هي نسخة من صنف <a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">مرسل الأحداث</a> event emitter الذي تعرفنا عليه بالتفصيل في المقال التاسع من هذه السلسلة وعندما يكون خرج الأمر الذي سننفذه كبير نسبيًا فيفضل استخدام التابع <code>‎spawn()‎</code> بدلًا من التابعين<code>‎exec()‎</code> و <code>‎execFile()‎</code>، وذلك لأن التابعين <code>‎exec()‎</code> و <code>‎execFile()‎</code> سيخزنان خرج الأمر كاملًا ضمن مخزن مؤقت في الذاكرة، ما سيؤثر على أداء النظام، بينما باستعمال المجرى stream يمكننا قراءة البيانات من الخرج ومعالجتها على عدة دفعات، ما يؤدي لخفض استعمال الذاكرة والسماح لنا بمعالجة البيانات الكبيرة.
</p>

<p>
	سنتعرف الآن على طريقة استخدام التابع <code>‎spawn()‎</code> لإنشاء عملية ابن، لذلك نبدأ بكتابة برنامج في نود مهمته تنفيذ أمر البحث عن الملفات <code>‎find‎</code> ضمن عملية ابن لعرض كل الملفات الموجودة ضمن المجلد الحالي، ونبدأ بإنشاء ملف جافاسكربت جديد بالاسم ‎findFiles.js‎:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_46" style="">
<span class="pln">$ nano findFiles</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونستدعي التابع <code>‎spawn()‎</code> لتنفيذ أمر البحث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_48" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> spawn </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> spawn</span><span class="pun">(</span><span class="str">'find'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'.'</span><span class="pun">]);</span></pre>

<p>
	بدأنا باستيراد التابع <code>‎spawn()‎</code> من الوحدة <code>‎child_process‎</code>، ثم استدعيناه لإنشاء عملية ابن جديدة يُنفذ ضمنها الأمر <code>‎find‎</code> وخزنّا نتيجة تنفيذ التابع ضمن المتغير <code>‎child‎</code> للاستماع لاحقًا إلى الأحداث الذي ستطلقها العملية الابن، ونلاحظ تمرير الأمر الذي نريد تنفيذه <code>‎find‎</code> كمعامل أول للتابع <code>‎spawn()‎</code>، أما المعامل الثاني فهو مصفوفة من المعاملات التي نريد تمريرها لذلك الأمر، ويكون الأمر النهائي الذي سينفذ هو أمر البحث <code>‎find‎</code> مع تمرير المعامل <code>‎.‎</code> للدلالة على البحث عن كل الملفات الموجودة ضمن المجلد الحالي، أي شكل الأمر المنفذ النهائي هو <code>‎find .‎</code>.
</p>

<p>
	وسابقًا عند استخدام التابعين <code>‎exec()‎</code> و <code>‎execFile()‎</code> مررنا لهما شكل الأمر الذي نريد تنفيذه بصيغته النهائية ضمن السلسلة النصية، أما عند استدعاء <code>‎spawn()‎</code> فيجب تمرير المعاملات للأمر المُنفذ ضمن مصفوفة، وذلك لأن هذا التابع لا يُنشئ صَدفة جديدة قبل إنشاء وتشغيل العملية، أما إذا أردنا تمرير المعاملات مع الأمر بنفس السلسلة النصية يجب إنشاء صَدفة جديدة لتفسر ذلك.
</p>

<p>
	ولنكمل معالجة تنفيذ الأمر بإضافة توابع استماع للخرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_50" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> spawn </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> spawn</span><span class="pun">(</span><span class="str">'find'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'.'</span><span class="pun">]);</span><span class="pln">

child</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="pln">stdout</span><span class="pun">:</span><span class="pln">\n$</span><span class="pun">{</span><span class="pln">data</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

child</span><span class="pun">.</span><span class="pln">stderr</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">stderr</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">data</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	كما ذكرنا سابقًا يمكن للأوامر كتابة الخرج على كل من مجرى الخرج القياسي <code>‎stdout‎</code> ومجرى خرج الأخطاء <code>‎stderr‎</code>، لذا يجب إضافة من يستمع لهما على كل مجرى باستخدام التابع <code>‎on()‎</code> ونطبع البيانات التي تُرسل ضمن ذلك الحدث إلى الطرفية.
</p>

<p>
	نستمع بعدها للحدث <code>‎error‎</code> الذي سيُطلق في حال فشل تنفيذ الأمر، والحدث <code>‎close‎</code> الذي سيُطلق بعد انتهاء تنفيذ الأمر وإغلاق المجرى، ونكمل الآن كتابة البرنامج ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_52" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> spawn </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> spawn</span><span class="pun">(</span><span class="str">'find'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'.'</span><span class="pun">]);</span><span class="pln">

child</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="pln">stdout</span><span class="pun">:</span><span class="pln">\n$</span><span class="pun">{</span><span class="pln">data</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

child</span><span class="pun">.</span><span class="pln">stderr</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">stderr</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">data</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

child</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="pln">error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

child</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="pln">child process exited </span><span class="kwd">with</span><span class="pln"> code $</span><span class="pun">{</span><span class="pln">code</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لاحظ أن الاستماع لكل من الحدثين <code>‎error‎</code> و <code>‎close‎</code> يكون على كائن العملية <code>‎child‎</code> مباشرةً، ولاحظ ضمن حدث الخطأ <code>‎error‎</code> أنه يوفر لنا كائن خطأ <code>‎Error‎</code> يعبر عن المشكلة، وفي تلك الحالة سنطبع رسالة الخطأ <code>‎message‎</code> إلى الطرفية، أما ضمن حدث الإغلاق <code>‎close‎</code> تمرر نود رمز الخروج للأمر بعد تنفيذه، ومنه يمكننا معرفة نجاح أو فشل تنفيذ الأمر، فعند نجاح التنفيذ سيعيد الأمر الرمز صفر <code>‎0‎</code> وإلا سيعيد رمز خروج أكبر من الصفر.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج باستخدام الأمر <code>‎node‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_54" style="">
<span class="pln">$ node findFiles</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_56" style="">
<span class="pln">stdout</span><span class="pun">:</span><span class="pln">
</span><span class="pun">.</span><span class="pln">
</span><span class="pun">./</span><span class="pln">findFiles</span><span class="pun">.</span><span class="pln">js
</span><span class="pun">./</span><span class="pln">listFiles</span><span class="pun">.</span><span class="pln">js
</span><span class="pun">./</span><span class="pln">nodejs</span><span class="pun">-</span><span class="pln">logo</span><span class="pun">.</span><span class="pln">svg
</span><span class="pun">./</span><span class="pln">processNodejsImage</span><span class="pun">.</span><span class="pln">sh
</span><span class="pun">./</span><span class="pln">getNodejsImage</span><span class="pun">.</span><span class="pln">js

child process exited </span><span class="kwd">with</span><span class="pln"> code </span><span class="lit">0</span></pre>

<p>
	يظهر لنا قائمة بكافة الملفات الموجودة ضمن المجلد الحالي، وفي آخر سطر يظهر رمز الخروج <code>‎0‎</code> ما يدل على نجاح التنفيذ، ومع أن الملفات ضمن المجلد الحالي قليلة لكن في حال نفذنا نفس الأمر ضمن مجلد آخر قد يظهر لنا قائمة طويلة جدًا من الملفات الموجودة ضمن كل المجلدات التي يمكن للمستخدم الوصول إليها، ولكن وبما أننا استخدمنا التابع <code>‎spawn()‎</code> فلا مشكلة في ذلك حيث سنعالج الخرج بأفضل طريقة ممكنة باستخدام مجاري البيانات بدلًا من تخزين الخرج كاملًا في الذاكرة ضمن مخزن مؤقت.
</p>

<p>
	وبذلك نكون قد تعلمنا طرق إنشاء عملية ابن في نود لتنفيذ الأوامر الخارجية ضمن نظام التشغيل، وتتيح نود أيضًا طريقة لإنشاء عملية ابن لتنفيذ برامج نود أخرى، وذلك باستعمال التابع <code>‎fork()‎</code> وهو ما سنتعرف عليه في الفقرة التالية.
</p>

<h2>
	إنشاء عملية ابن باستخدام ‎fork
</h2>

<p>
	تتيح نود التابع <a href="https://wiki.hsoub.com/Node.js/child_process#child_process.fork.28modulePath.5B.2C_args.5D.5B.2C_options.5D.29" rel="external"><code>‎fork()‎</code></a> المشابه للتابع <code>‎spawn()‎</code> لإنشاء عملية جديدة ابن لتنفيذ برنامج نود ما، وما يميز التابع <code>‎fork()‎</code> عن التوابع الأخرى مثل <code>‎spawn()‎</code> أو <code>‎exec()‎</code> هو إمكانية التواصل بين العملية الأب والابن، إذ إضافة لقراءة خرج الأمر الذي ننفذه باستخدام <code>‎fork()‎</code> يمكن للعملية الأب إرسال رسائل للعملية الابن والتواصل معها، ويمكن للعملية الابن أيضًا التواصل مع العملية الأب بنفس الطريقة.
</p>

<p>
	وسنتعرف في هذا المثال على طريقة إنشاء عملية ابن باستخدام <code>‎fork()‎</code> والاستفادة منها في تحسين أداء التطبيق الذي نطوره، حيث وبما أن البرامج في نود تعمل ضمن عملية واحدة فالمهام التي تحتاج لمعالجة طويلة من من قبل المعالج ستعيق عمل الشيفرات التالية في باقي البرنامج، فمثلًا المهام التي تتطلب تكرار تنفيذ حلقة برمجية ما لمرات عديدة طويلة، أو تفسير ملفات كبيرة من <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">صيغة JSON</a>، حيث ستشكل هذه العمليات عائقًا في بعض التطبيقات وتؤثر على الأداء، فمثلًا لا يمكن لخادم ويب أن تعيق عمله مثل تلك المهام الطويلة، حيث سيمنعه ذلك من استقبال الطلبات الجديدة ومعالجتها لحين الانتهاء من تنفيذ تلك المهام، لذا سنختبر ذلك بإنشاء خادم ويب يحتوي على مسارين الأول سيُنفذ عملية تحتاج لمعالجة طويلة وتعيق عمل عملية نود للخادم، والثاني سيعيد كائن بصيغة JSON يحوي على الرسالة <code>hello‎</code>.
</p>

<p>
	لنبدأ بإنشاء ملف جافاسكربت جديد لخادم HTTP بالاسم ‎httpServer.js‎ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_58" style="">
<span class="pln">$ nano httpServer</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نبدأ بإعداد الخادم أولًا باستيراد الوحدة البرمجية <code>‎http‎</code> ثم إنشاء تابع استماع لمعالجة الطلبات الواردة، وكائن للخادم وربط تابع الاستماع معه، والآن نضيف الشيفرة التالية إلى الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_60" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> host </span><span class="pun">=</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">requestListener</span><span class="pun">);</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> host</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//${host}:${port}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	سيكون الخادم متاحًا للوصول على العنوان <code>‎http://localhost:8000‎</code>، والآن سنكتب دالة مهمتها إعاقة عمل الخادم عبر حلقة ستُنفذ لعدد كبير من المرات، ونضيفها قبل التابع <code>‎requestListener()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_62" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> slowFunction </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let counter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">5000000000</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    counter</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> counter</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	وضمن تابع معالجة الطلب <code>‎requestListener()‎</code> سنستدعي تابع الإعاقة <code>‎slowFunction()‎</code> على المسار الفرعي، بينما سنعيد رسالة JSON على المسار الآخر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_64" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url </span><span class="pun">===</span><span class="pln"> </span><span class="str">'/total'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let slowResult </span><span class="pun">=</span><span class="pln"> slowFunction</span><span class="pun">();</span><span class="pln">
    let message </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`‎{</span><span class="str">"totalCount"</span><span class="pun">:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">slowResult</span><span class="pun">}}‎`;</span><span class="pln">

    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Returning /total results'</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url </span><span class="pun">===</span><span class="pln"> </span><span class="str">'/hello'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Returning /hello results'</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(`‎{</span><span class="str">"message"</span><span class="pun">:</span><span class="str">"hello"</span><span class="pun">}‎`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	إذا تواصلنا مع الخادم على المسار الفرعي <code>‎/total‎</code> سيُنفذ تابع الإعاقة <code>‎slowFunction()‎</code>، أما على المسار الفرعي <code>‎/hello‎</code> سنعيد الرسالة التالية بصيغة JSON <code>‎{"message":"hello"}‎</code>، والآن نحفظ الملف ونخرج منه ثم نشغل الخادم باستخدام الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_66" style="">
<span class="pln">$ node httpServer</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر لنا الرسالة التالية ضمن الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_68" style="">
<span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	يمكننا بدء الاختبار الآن ولهذا نحتاج لطرفيتين إضافيتين، ففي الأولى سنستخدم الأمر <code>‎curl‎</code> لإرسال طلب للخادم على المسار <code>‎/total‎</code> لإبطاء الخادم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_70" style="">
<span class="pln">$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/total</span></pre>

<p>
	وضمن الطرفية الثانية نستخدم الأمر <code>‎curl‎</code> لإرسال طلب على المسار الآخر <code>‎/hello‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_72" style="">
<span class="pln">$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/hello</span></pre>

<p>
	سيعيد الطلب الأول القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_75" style="">
<span class="pun">{</span><span class="str">"totalCount"</span><span class="pun">:</span><span class="lit">5000000000</span><span class="pun">}</span></pre>

<p>
	بينما سيعيد الطلب الثاني القيمة:
</p>

<pre class="ipsCode">
{"message":"hello"}
</pre>

<p>
	ونلاحظ أن الطلب الثاني للمسار <code>‎/hello‎</code> اكتمل بعد انتهاء معالجة الطلب على المسار <code>‎/total‎</code>، حيث أعاق تنفيذ التابع <code>‎slowFunction()‎</code> معالجة أي طلبات وتنفيذ أي شيفرات على الخادم لحين انتهائه، ويمكننا التأكد من ذلك من خرج طرفية الخادم نفسه حيث نلاحظ ترتيب إرسال الرد على تلك الطلبات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_77" style="">
<span class="typ">Returning</span><span class="pln"> </span><span class="pun">/</span><span class="pln">total results
</span><span class="typ">Returning</span><span class="pln"> </span><span class="pun">/</span><span class="pln">hello results</span></pre>

<p>
	في مثل تلك الحالات يأتي دور التابع <code>‎fork()‎</code> لإنشاء عملية ابن جديدة يمكن توكيل معالجة المهام الطويلة إليها للسماح للخادم بالعمل على معالجة الطلبات الجديدة القادمة دون توقف، وسنطبق ذلك في مثالنا بنقل تابع المهمة الطويلة إلى وحدة برمجية منفصلة، حيث سيستدعيها خادم الويب لاحقًا ضمن عملية ابن منفصلة عند كل طلب إلى المسار الفرعي <code>‎/total‎</code> ويستمع إلى نتيجة التنفيذ.
</p>

<p>
	نبدأ بإنشاء ملف جافاسكربت بالاسم ‎getCount.js‎ سيحوي على التابع <code>‎slowFunction()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_79" style="">
<span class="pln">$ nano getCount</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونضيف داخله ذلك التابع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_81" style="">
<span class="kwd">const</span><span class="pln"> slowFunction </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let counter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">5000000000</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    counter</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> counter</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبما أننا ننوي استدعاء هذا التابع كعملية ابن باستخدام <code>‎fork()‎</code> يمكننا إضافة شيفرة للتواصل مع العملية الأب تعلمه عند انتهاء تنفيذ التابع <code>‎slowFunction()‎</code>، لهذا نضيف الشيفرة التالية التي سترسل رسالة للعملية الأب تحوي على كائن JSON لنتيجة التنفيذ ولإرسالها إلى المستخدم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_83" style="">
<span class="kwd">const</span><span class="pln"> slowFunction </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let counter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">5000000000</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    counter</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> counter</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

process</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message </span><span class="pun">==</span><span class="pln"> </span><span class="str">'START'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Child process received START message'</span><span class="pun">);</span><span class="pln">
    let slowResult </span><span class="pun">=</span><span class="pln"> slowFunction</span><span class="pun">();</span><span class="pln">
    let message </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`‎{</span><span class="str">"totalCount"</span><span class="pun">:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">slowResult</span><span class="pun">}}‎`;</span><span class="pln">
    process</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	كما نلاحظ بإمكاننا الوصول للرسائل التي يُنشئها التابع <code>‎fork()‎</code> بين العملية الأب والابن عن طريق القيمة العامة للكائن <code>‎process‎</code> الذي يمثل العملية، حيث يمكننا إضافة مُستمع لحدث إرسال الرسائل <code>‎message‎</code> والتحقق ما إذا كانت الرسالة هي حدث بدء عملية المعالجة <code>‎START‎</code> الذي سيرسلها الخادم عند ورود طلب إلى المسار الفرعي <code>‎/total‎</code>، ونستجيب لتلك الرسالة بتنفيذ تابع المعالجة <code>‎slowFunction()‎</code> ثم ننشئ السلسلة النصية للرد بصيغة JSON والتي تحوي على نتيجة التنفيذ، ثم نستدعي التابع <code>‎process.send()‎</code> لإرسال رسالة للعملية الأب تعلمه بالنتيجة.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونعود لملف الخادم ‎httpServer.js‎ للتعديل عليه وإضافة استدعاء للتابع <code>‎slowFunction()‎</code> بإنشاء عملية ابن لتنفيذ البرنامج ضمن الملف ‎getCount.js‎، فنبدأ باستيراد التابع <code>‎fork()‎</code> من الوحدة البرمجية <code>‎child_process‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_85" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> fork </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ثم نزيل التابع <code>‎slowFunction()‎</code> من هذا الملف بما أننا نقلناه إلى وحدة برمجية منفصلة، ونعدل تابع معالجة الطلبات <code>‎requestListener()‎</code> ليُنشئ العملية الابن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_87" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url </span><span class="pun">===</span><span class="pln"> </span><span class="str">'/total'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> fork</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/getCount'</span><span class="pun">);</span><span class="pln">

    child</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Returning /total results'</span><span class="pun">);</span><span class="pln">
      res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">);</span><span class="pln">
      res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
      res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    child</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">'START'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url </span><span class="pun">===</span><span class="pln"> </span><span class="str">'/hello'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Returning /hello results'</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(`‎{</span><span class="str">"message"</span><span class="pun">:</span><span class="str">"hello"</span><span class="pun">}‎`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ينتج الآن عن الطلبات الواردة إلى المسار <code>‎/total‎</code> إنشاء عملية ابن باستخدام <code>‎fork()‎</code>، حيث مررنا لهذا التابع مسار وحدة نود البرمجية التي نريد تنفيذها، وهو الملف ‎getCount.js‎ في حالتنا ضمن المجلد الحالي، لهذا استفدنا هذه المرة أيضًا من قيمة المتغير <code>‎__dirname‎</code>، وخزنا قيمة العملية الابن ضمن المتغير <code>‎child‎</code> للتعامل معها.
</p>

<p>
	أضفنا بعدها مستمعًا إلى الكائن <code>‎child‎</code> ليستقبل الرسائل الواردة من العملية الابن، وتحديدًا لاستقبال الرسالة التي سيرسلها تنفيذ الملف ‎getCount.js‎ الحاوية على سلسلة نصية بصيغة JSON لنتيجة تنفيذ حلقة <code>‎while‎</code>، وعند وصول تلك الرسالة نرسلها مباشرة إلى المستخدم كما هي.
</p>

<p>
	ويمكننا التواصل مع العملية الابن باستدعاء التابع <code>‎send()‎</code> من الكائن <code>‎child‎</code> لإرسال رسالة لها، حيث نرسل الرسالة <code>‎START‎</code> التي سيستقبلها البرنامج ضمن العملية الابن لينفذ التابع <code>‎slowFunction()‎</code> داخله استجابة لها.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونختبر الميزة التي قدمها استخدام <code>‎fork()‎</code> لخادم HTTP بتشغيل الخادم من ملف ‎httpServer.js‎ باستخدام الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_89" style="">
<span class="pln">$ node httpServer</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	وسيظهر لنا الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_91" style="">
<span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	وكما فعلنا سابقًا لاختبار عمل الخادم سنحتاج لطرفيتين، ففي الأولى سنستخدم الأمر <code>‎curl‎</code> لإرسال طلب للخادم على المسار <code>‎/total‎</code> والذي سيحتاج بعض الوقت للاكتمال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_93" style="">
<span class="pln">$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/total</span></pre>

<p>
	وضمن الطرفية الثانية نستخدم الأمر <code>‎curl‎</code> لإرسال طلب على المسار الآخر <code>‎/hello‎</code> والذي سيرسل لنا الرد هذه المرة بسرعة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_95" style="">
<span class="pln">$ curl http</span><span class="pun">:</span><span class="com">//localhost:8000/hello</span></pre>

<p>
	سيعيد الطلب الأول القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_97" style="">
<span class="pun">{</span><span class="str">"totalCount"</span><span class="pun">:</span><span class="lit">5000000000</span><span class="pun">}</span></pre>

<p>
	بينما سيعيد الطلب الثاني القيمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_99" style="">
<span class="pun">{</span><span class="str">"message"</span><span class="pun">:</span><span class="str">"hello"</span><span class="pun">}</span></pre>

<p>
	نلاحظ الفرق هذه المرة بأن الطلب للمسار <code>‎/hello‎</code> تم بسرعة، ويمكننا التأكد من ذلك أيضًا من الرسائل الظاهرة في طرفية الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5457_101" style="">
<span class="typ">Child</span><span class="pln"> process received START message
</span><span class="typ">Returning</span><span class="pln"> </span><span class="pun">/</span><span class="pln">hello results
</span><span class="typ">Returning</span><span class="pln"> </span><span class="pun">/</span><span class="pln">total results</span></pre>

<p>
	حيث يظهر أن الطلب على المسار <code>‎/hello‎</code> تم استقباله بعد إنشاء العملية الابن ومعالجته قبل انتهاء عملها، وهذا بسبب نقل العملية التي تأخذ وقتا طويلًا إلى عملية ابن منفصلة واستدعائها باستخدام <code>‎fork()‎</code>، حيث بقي الخادم متفرغًا لمعالجة الطلبات الجديدة الواردة وتنفيذ شيفرات جافاسكربت، وهذا بفضل الميزة الذي يوفرها التابع <code>‎fork()‎</code> من إرسال الرسائل إلى العملية الابن للتحكم بتنفيذ العمليات ضمنها، وإمكانية قراءة البيانات المُرسلة من قبل العملية لمعالجتها ضمن العملية الأب.
</p>

<h2>
	ختامًا
</h2>

<p>
	تعرفنا في هذا المقال على طرق مختلفة لإنشاء عملية ابن في نود، حيث تعلمنا كيف يمكن استخدام التابع <code>‎exec()‎</code> لإنشاء عملية ابن جديدة لتنفيذ أوامر الصدفة من قبل شيفرة برنامج نود، وبعدها تعرفنا على التابع <code>‎execFile()‎</code> الذي يُمكننا من تشغيل الملفات التنفيذية، ثم تعرفنا على التابع <code>‎spawn()‎</code> الذي يسمح بتنفيذ الأوامر وقراءة نتيجتها عبر مجرى للبيانات دون إنشاء صدفة لها كما يفعل التابعان <code>‎exec()‎</code> و <code>‎execFile()‎</code>، وأخيرًا تعرفنا على التابع <code>‎fork()‎</code> الذي يسمح بالتواصل بين العملية الأب والابن.
</p>

<p>
	ويمكنك الرجوع إلى <a href="https://wiki.hsoub.com/Node.js/child_process" rel="external">التوثيق الرسمي</a> للوحدة البرمجية <code>‎child_process‎</code> من نود للتعرف عليها أكثر.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-launch-child-processes-in-node-js" rel="external nofollow">How To Launch Child Processes in Node.js</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D9%86%D9%82%D9%8A%D8%AD-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%86%D9%82%D8%AD-debugger-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1-devtools-r1807/" rel="">تنقيح أخطاء Node.js باستخدام المنقح debugger وأدوات المطور DevTools</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AA%D8%A7%D8%B3%D8%B9-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-r1012/" rel="">مفهوم الخيوط Threads في عملية المعالجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%AA%D8%B3%D9%84%D8%B3%D9%84-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%87%D8%B1%D9%85%D9%8A-%D9%88%D8%A7%D8%B3%D8%AA%D8%AF%D8%B9%D8%A7%D8%A1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B8%D8%A7%D9%85-fork-%D9%88-exec-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1759/" rel="">تسلسل العمليات الهرمي واستدعاءات النظام Fork و Exec في نظام تشغيل الحاسوب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D8%AF%D9%81%D8%A9-%D8%A8%D8%A7%D8%B4-bash-r606/" rel="">مدخل إلى صدفة باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%AF%D9%84%D9%8A%D9%84-%D9%85%D9%8A%D9%8E%D8%B3%D9%91%D9%8E%D8%B1-%D9%84%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-shell-r56/" rel="">دليل ميسر لكتابة سكربتات shell</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1808</guid><pubDate>Sat, 03 Dec 2022 07:24:04 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x642;&#x64A;&#x62D; &#x623;&#x62E;&#x637;&#x627;&#x621; Node.js &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x645;&#x646;&#x642;&#x62D; debugger &#x648;&#x623;&#x62F;&#x648;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x637;&#x648;&#x631; DevTools</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D9%86%D9%82%D9%8A%D8%AD-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%86%D9%82%D8%AD-debugger-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1-devtools-r1807/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/638aebdc83ef7_--Node--------Chrome.png.420b2c742924cc75deab23186e22e7d4.png" /></p>
<p>
	عملية تتبع أخطاء البرامج لمعرفة مصدر المشكلة في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">نود Node.js</a> خلال مرحلة التطوير توفر على المطور الكثير من وقت تطوير المشروع، وتزداد صعوبة تلك المهمة مع كبر حجم المشروع وزيادة تعقيده، وهنا يأتي دور مُنقِّح الأخطاء debugger ليساعد في ذلك، وهو برنامج يسمح للمطور بمعاينة البرنامج أثناء تشغيله عبر تنفيذ الشيفرة سطرًا تلو الآخر ومعاينة حالة التطبيق وتغيرها، مما يوفر للمبرمج نظرة أقرب على طريقة عمل البرنامج ما يسهل العثور على الأخطاء وإصلاحها.
</p>

<p>
	وعادة ما يضيف المطورون تعليمات الطباعة داخل شيفرة البرنامج لمعاينة بعض القيم أثناء تشغيله، حيث يضيف المطور في نود تعليمات طباعة مثل <code>‎console.log()‎</code> و <code>‎console.debug()‎</code>، ومع أن هذه الطريقة سهلة وسريعة لكنها تبقى يدوية ولا تخدم دومًا في الحالات المعقدة أو عندما يكون التطبيق كبيرًا، فقد ينسى أحيانًا المطور بعض تعليمات الطباعة تلك ما قد يؤدي لطباعة معلومات خاصة وحساسة عن التطبيق يجعله عرضة للاختراق، وهنا يوفر لنا المنقح طريقة أفضل لمراقبة البرنامج أثناء التشغيل دون أن يُعرّض البرنامج لمثل تلك الأخطار.
</p>

<p>
	وأهم ميزتين في منقح الأخطاء الداخلي هما مراقبة الكائنات، وإضافة نقاط الوقوف breakpoints، حيث تتيح مراقبة الكائنات طريقة لمشاهدة التغير في حالة المتغيرات أثناء تنفيذ البرنامج خطوة بخطوة، أما نقاط الوقوف فهي أماكن ضمن الشيفرة يمكن للمبرمج تحديدها ليتوقف البرنامج عن التنفيذ مؤقتًا عند الوصول إليها، ليعطي فرصة للمبرمج لمعاينة حالة البرنامج في تلك اللحظة.
</p>

<p>
	سنتعلم في هذا المقال طريقة استخدام المنقح لاستكشاف الأخطاء ضمن بعض البرامج في نود، حيث سنستخدم بدايةً <a href="https://wiki.hsoub.com/Node.js/debugger" rel="external">أداة تنقيح الأخطاء الداخلية في نود</a> ونتعلم طريقة إعداد المراقبة للمتغيرات وإضافة نقاط التوقف لنتمكن من اكتشاف المشاكل وإصلاحها، ثم سنتعلم استخدام واجهة <a href="https://academy.hsoub.com/programming/workflow/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-devtools-%D9%81%D9%8A-chrome-r554/" rel="">أداة المطور في متصفح جوجل كروم</a> بدلًا من التعامل مع المنقح من <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a>.
</p>

<h2>
	المستلزمات
</h2>

<p>
	هذا المقال جزء من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">دليل تعلم Node.js</a> لذا يجب قبل قراءته:
</p>

<ul>
	<li>
		تثبيت بيئة Node.js على الجهاز، حيث استخدمنا في هذا المقال الإصدار رقم 10.19.0.
	</li>
	<li>
		معرفة <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">بأساسيات جافاسكربت</a> والتعامل مع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r781/" rel="">الدوال</a>.
	</li>
	<li>
		تثبيت <a href="https://www.google.com/chrome/" rel="external nofollow">متصفح جوجل كروم</a> أو متصفح <a href="https://www.chromium.org/" rel="external nofollow">كروميوم مفتوح المصدر</a>.
	</li>
</ul>

<h2>
	استخدام الراصدات Watchers مع المنقح Debugger
</h2>

<p>
	الميزتين الأساسيتين لمنقح الأخطاء هما مراقبة المتغيرات وتغير قيمها أثناء التنفيذ، وميزة الإيقاف المؤقت لعمل البرنامج عند أماكن محددة من الشيفرة باستخدام نقاط الوقوف، وسنتعلم في هذه الفقرة طريقة مراقبة المتغيرات لتساعدنا في اكتشاف الأخطاء.
</p>

<p>
	تساعدنا عملية مراقبة المتغيرات ورصدها في فهم كيفية تغير قيم تلك المتغيرات أثناء تنفيذ البرنامج، وسنستفيد من هذه الميزة في اكتشاف الأخطاء في منطق عمل البرنامج وإصلاحها، وسنبدأ بإنشاء مجلد جديد بالاسم ‎debugging‎ سيحوي على البرامج التي سنتعامل معها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_11" style=""><span class="pln">$ mkdir debugging</span></pre>

<p>
	وندخل إلى المجلد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_13" style=""><span class="pln">$ cd debugging</span></pre>

<p>
	ننشئ داخله ملف جافاسكربت جديد بالاسم ‎badLoop.js‎ ونفتحه ضمن أي محرر نصوص، حيث سنستخدم في أمثلتنا محرر نانو ‎nano‎ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_15" style=""><span class="pln">$ nano badLoop</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنكتب برنامجًا يمر على عناصر المصفوفة ويجمع قيمها لحساب المجموع الكلي لها، حيث تمثل تلك الأرقام عدد الطلبات اليومي لمتجر خلال فترة أسبوع، حيث سيطبع البرنامج المجموع الكلي للأرقام في تلك المصفوفة، ليكون البرنامج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_17" style=""><span class="pln">let orders </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">341</span><span class="pun">,</span><span class="pln"> </span><span class="lit">454</span><span class="pun">,</span><span class="pln"> </span><span class="lit">198</span><span class="pun">,</span><span class="pln"> </span><span class="lit">264</span><span class="pun">,</span><span class="pln"> </span><span class="lit">307</span><span class="pun">];</span><span class="pln">

let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">totalOrders</span><span class="pun">);</span></pre>

<p>
	أنشأنا بداية مصفوفة الطلبات <code>‎orders‎</code> والتي تحوي خمسة أعداد، ثم أنشأنا متغير المجموع الكلي للطلبات <code>‎totalOrders‎</code> وضبطنا قيمته الأولية إلى الصفر <code>‎0‎</code>، حيث سنخزن ضمنه المجموع الكلي للأرقام السابقة، ومررنا ضمن حلقة <code>‎for‎</code> على عناصر المصفوفة <code>‎orders‎</code> وأضفنا كل قيمة منها إلى متغير المجموع الكلي <code>‎totalOrders‎</code>، ثم أخيرًا طبعنا قيمة المجموع الكلي.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذ البرنامج ونعاين النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_19" style=""><span class="pln">$ node badLoop</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	يظهر لنا الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_22" style=""><span class="kwd">NaN</span></pre>

<p>
	القيمة <code>‎NaN‎</code> في جافاسكربت هي اختصار لجملة "ليس عددًا" أو "Not a Number"، ولكن كيف حصلنا على تلك القيمة مع أن المصفوفة لا تحوي سوى قيم عددية؟ الطريقة الأفضل لمعرفة سبب المشكلة هي استخدام منقح الأخطاء، وهنا سنبدأ بالتعرف على منقح نود ونستخدمه رصد قيمة كل من المتغيرين <code>‎totalOrders‎</code> و <code>‎i‎</code> ضمن حلقة <code>‎for‎</code>، ولتشغيله نضيف خيار <code>‎inspect‎</code> قبل اسم الملف عند تشغيله بواسطة الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_26" style=""><span class="pln">$ node inspect badLoop</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنلاحظ ظهور الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_29" style=""><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Debugger</span><span class="pln"> listening on ws</span><span class="pun">:</span><span class="com">//127.0.0.1:9229/e1ebba25-04b8-410b-811e-8a0c0902717a</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">For</span><span class="pln"> help</span><span class="pun">,</span><span class="pln"> see</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//nodejs.org/en/docs/inspector</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Debugger</span><span class="pln"> attached</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Break</span><span class="pln"> on start in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> let orders </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">341</span><span class="pun">,</span><span class="pln"> </span><span class="lit">454</span><span class="pun">,</span><span class="pln"> </span><span class="lit">198</span><span class="pun">,</span><span class="pln"> </span><span class="lit">264</span><span class="pun">,</span><span class="pln"> </span><span class="lit">307</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">2</span><span class="pln"> 
  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	يحتوي السطر الأول من الخرج على رابط خادم تنقيح الأخطاء، حيث تستفيد منه أدوات تنقيح الأخطاء الخارجية مثل متصفح الويب للتواصل مع خادم التنقيح الخاص بنود وهو ما سنتعامل معه لاحقًا، وافتراضيًا يكون هذا الخادم متاحًا على المنفذ <code>‎:9229‎</code> والعنوان المحلي <code>‎localhost‎</code> أو <code>‎127.0.0.1‎</code>، ويفضل منع الوصول لهذا المنفذ من الشبكة الخارجية والوصول إليه من الجهاز محليًا فقط.
</p>

<p>
	وبعد ربط منقح الأخطاء ستظهر الرسالة <code>‎Break on start in badLoop.js:1‎</code> والتي تعني توقف التنفيذ عند أول سطر من الملف، حيث يمكن وضع نقاط الوقوف ضمن الشيفرة لتحديد مكان توقف التنفيذ وكما لاحظنا فمنقح الأخطاء يتوقف افتراضيًا عند أول سطر من الملف دومًا ويُظهر لنا مقطع من الشيفرة عند مكان التوقف وبعده سطر جديد يبدأ بالكلمة <code>‎debug‎</code> يمكننا كتابة الأوامر ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_31" style=""><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> let orders </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">341</span><span class="pun">,</span><span class="pln"> </span><span class="lit">454</span><span class="pun">,</span><span class="pln"> </span><span class="lit">198</span><span class="pun">,</span><span class="pln"> </span><span class="lit">264</span><span class="pun">,</span><span class="pln"> </span><span class="lit">307</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">2</span><span class="pln"> 
  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
debug</span><span class="pun">&gt;</span></pre>

<p>
	نلاحظ وجود الرمز <code>‎&gt;‎</code> بجانب رقم السطر الأول <code>‎1‎</code> وهو دلالة على مكان توقف التنفيذ الحالي، ويُظهر السطر الأخير استعداد منقح الأخطاء لتلقي الأوامر، حيث يمكننا مثلًا تنفيذ أمر لتوجيهه لتقديم عملية التنفيذ خطوة إلى الأمام والذهاب إلى السطر التالي من التنفيذ، ويمكن إدخال أحد الأوامر التالية:
</p>

<ul>
	<li>
		<code>‎c‎</code> أو <code>‎cont‎</code>: لإكمال عملية التنفيذ حتى الوصول إلى نقطة الوقوف التالية أو حتى الانتهاء من تنفيذ البرنامج.
	</li>
	<li>
		<code>‎n‎</code> أو <code>‎next‎</code>: للتقدم خطوة إلى الأمام في التنفيذ إلى السطر التالي من الشيفرة.
	</li>
	<li>
		<code>‎s‎</code> أو <code>‎step‎</code>: للدخول إلى دالة ما، حيث تكون عملية التقدم افتراضيًا ضمن النطاق scope الذي نصحح الأخطاء ضمنه فقط، وتمكننا هذه العملية من الدخول ضمن دالة استدعتها الشيفرة التي نفحصها لمعاينة عملها من الداخل ومراقبة تعاملها مع البيانات المُمررة لها.
	</li>
	<li>
		<code>‎o‎</code>: للخروج من دالة حيث سيعود التنفيذ لخارجها إلى مكان استدعائها، وهو المكان الذي ستُرجع قيمة تنفيذ الدالة إليه، حيث يفيد هذا الأمر في العودة مباشرةً إلى خارج الدالة إلى المكان الذي كنا نعاينه قبل الدخول إليها.
	</li>
	<li>
		<code>‎pause‎</code>: لإيقاف التنفيذ مباشرةً مؤقتًا.
	</li>
</ul>

<p>
	لنتقدم بتنفيذ البرنامج سطرًا تلو الآخر بتنفيذ الأمر <code>‎n‎</code> للانتقال إلى السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_33" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> n</span></pre>

<p>
	نلاحظ تقدم التنفيذ إلى السطر الثالث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_35" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">3</span><span class="pln">
  </span><span class="lit">1</span><span class="pln"> let orders </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">341</span><span class="pun">,</span><span class="pln"> </span><span class="lit">454</span><span class="pun">,</span><span class="pln"> </span><span class="lit">198</span><span class="pun">,</span><span class="pln"> </span><span class="lit">264</span><span class="pun">,</span><span class="pln"> </span><span class="lit">307</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">2</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
  </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	يتم تجاوز الأسطر الفارغة، لذا إذا قدّمنا علمية التنفيذ سطرًا آخر الآن بتنفيذ الأمر <code>‎n‎</code> مجددًا سينتقل التنفيذ إلى السطر الخامس:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_37" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	يوقف التنفيذ الآن في بداية الحلقة، وإذا كانت الطرفية تدعم إظهار الألوان في الخرج سنلاحظ تحديد القيمة <code>‎0‎</code> ضمن التعليمة <code>‎let i = 0‎</code>، حيث يحدد المنقح أي قسم من الشيفرة على وشك التنفيذ، ففي الحلقة <code>‎for‎</code> أول ما ينفذ هو إسناد القيمة لعداد الحلقة، وسنبدأ هنا بمعاينة القيم للمتغيرات لنحدد سبب الحصول على القيمة <code>‎NaN‎</code> بدلًا من القيمة العددية لمتغير المجموع <code>‎totalOrders‎</code>، حيث أن قيمتي المتغيرين <code>‎totalOrders‎</code> و <code>‎i‎</code> تتغيران عند كل دورة للحلقة، وسنستفيد من ميزة الرصد والمراقبة التي يوفرها المنقح في مراقبة قيم هذين المتغيرين.
</p>

<p>
	نبدأ بإعداد المراقبة لمتغير المجموع الكلي <code>‎totalOrders‎</code> بتنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_39" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'totalOrders'</span><span class="pun">)</span></pre>

<p>
	لمراقبة أي متغير خلال تنقيح الأخطاء نستدعي الدالة <code>‎watch()‎</code> الذي يوفرها المنقح ونمرر لها سلسلة نصية تحوي على اسم المتغير الذي نريد مراقبته، وبعد الضغط على زر الإدخال ‎ENTER‎ وتنفيذ الدالة <code>‎watch()‎</code> سينتقل التنفيذ إلى سطر جديد دون ظهور أي خرج، وستظهر القيم التي نراقبها عند الانتقال للسطر التالي.
</p>

<p>
	لنراقب أيضًا المتغير الآخر <code>‎i‎</code> بنفس الطريقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_41" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'i'</span><span class="pun">)</span></pre>

<p>
	سنشاهد الآن عملية المراقبة للمتغيرات السابقة، ننفذ الأمر <code>‎n‎</code> للانتقال خطوة للأمام وسيظهر لنا التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_43" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">

  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	نلاحظ ظهور قيم المتغيرين اللذين نراقبهما <code>‎totalOrders‎</code> و <code>‎i‎</code> قبل الشيفرة حيث سيتم تحديث هذه القيم عند تغيرها، ونلاحظ أن المنقح يحدد حاليًا الخاصية <code>‎length‎</code> من التعليمة <code>‎orders.length‎</code>، ما يعني أن الخطوة التالية هي التحقق من شرط إكمال التنفيذ للحلقة قبل إعادة تنفيذ التعليمات في جسم الحلقة، وبعدها ستُنفذ تعليمة زيادة قيمة عداد الحلقة <code>‎i++‎</code>.
</p>

<p>
	والآن نتقدم خطوة للأمام بتنفيذ الأمر <code>‎n‎</code> مجددًا للدخول إلى جسم الحلقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_46" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">6</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">

  </span><span class="lit">4</span><span class="pln"> 
  </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="lit">8</span></pre>

<p>
	ستُعدِّل التعليمة الحالية من قيمة المتغير <code>‎totalOrders‎</code>، وسنلاحظ ذلك من تغير تلك القيمة ضمن قسم المراقبة في الأعلى.
</p>

<p>
	والآن نتقدم خطوة إلى الأمام بتنفيذ <code>‎n‎</code> ليظهر لنا ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_48" style=""><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">341</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">

  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	نلاحظ أن قيمة متغير المجموع الكلي <code>‎totalOrders‎</code> تساوي قيمة أول عنصر من المصفوفة <code>‎341‎</code>، والخطوة التالية الآن هي التحقق من شرط إكمال تنفيذ الحلقة، لذا ننفذ الأمر <code>‎n‎</code> لتعديل قيمة عداد الحلقة <code>‎i‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_50" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">341</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">

  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	إلى الآن قد تقدمنا عدة خطوات يدويًا ضمن الشيفرة لمراقبة التغير في قيم المتغيرات، لكن تلك الطريقة غير عملية حيث سنتعرف في الفقرة التالية على حل لهذه المشكلة باستخدام نقاط الوقوف، وسنُكمل حاليًا العمل بتقديم عملية التنفيذ يدويًا ومراقبة قيم المتغيرات للعثور على سبب المشكلة.
</p>

<p>
	والآن نتقدم في التنفيذ 12 خطوة للأمام لنلاحظ الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_52" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1564</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">

  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	عدد القيم ضمن المصفوفة <code>‎orders‎</code> هو خمسة، ولكن قيمة عداد الحلقة <code>‎i‎</code> الحالية هي <code>‎5‎</code>، وبما أننا نستخدم قيمة المتغير <code>‎i‎</code> للوصول إلى العنصر ضمن المصفوفة بالترتيب الحالي فالقيمة عند الترتيب <code>‎orders[5]‎</code> غير موجودة، وترتيب آخر قيمة ضمن المصفوفة <code>‎orders‎</code> هو <code>‎4‎</code>، ما يعني أن محاولة الوصول للعنصر السادس باستخدام <code>‎orders[5]‎</code> سيعيد القيمة <code>‎undefined‎</code>.
</p>

<p>
	والآن نتقدم بالتنفيذ خطوة للأمام بتنفيذ الأمر <code>‎n‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_54" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">6</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1564</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">

  </span><span class="lit">4</span><span class="pln"> 
  </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="lit">8</span></pre>

<p>
	وبالتقدم خطوة إضافية بتنفيذ <code>‎n‎</code> نلاحظ القيمة الجديدة للمتغير <code>‎totalOrders‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_56" style=""><span class="kwd">break</span><span class="pln"> in badLoop</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">

  </span><span class="lit">3</span><span class="pln"> let totalOrders </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">4</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   totalOrders </span><span class="pun">+=</span><span class="pln"> orders</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="lit">7</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	لاحظنا بالاستفادة من عملية تنقيح الشيفرة ومراقبة قيم المتغيرين <code>‎totalOrders‎</code> و <code>‎i‎</code> أن الحلقة تُنفَّذ ستة مرات بدلًا من خمسة، وعندما تكون قيمة عداد الحلقة <code>‎i‎</code> هي <code>‎5‎</code> فمحاولة الوصول للعنصر الحالي <code>‎orders[5]‎</code> وإضافته للمتغير <code>‎totalOrders‎</code> ستجعل من قيمة المجموع تساوي <code>‎NaN‎</code>، لأن قيمة العنصر السادس <code>‎orders[5]‎</code> الغير موجود ستكون <code>‎undefined‎</code>، فإذًا المشكلة هي في شرط الحلقة <code>‎for‎</code> فبدلًا من التحقق من أن قيمة العداد <code>‎i‎</code> هي أصغر أو تساوي طول المصفوفة <code>‎orders‎</code> يجب أن نتحقق من أنها أصغر من الطول فقط.
</p>

<p>
	وبعد أن حددنا المشكلة نخرج من المنقح ونصحح الخطأ ضمن الشيفرة ونعيد تنفيذ البرنامج ونتحقق من النتيجة، لكن أولًا ننفذ أمر الخروج <code>‎.exit‎</code> ثم نضغط زر الإدخال ‎ENTER‎:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_58" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">exit</span></pre>

<p>
	نخرج بذلك من وضع المنقح ونعود إلى الملف ‎badLoop.js‎ ونفتحه ضمن محرر النصوص ونعدل شرط حلقة <code>‎for‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_61" style=""><span class="pun">...</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> orders</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نحفظ الملف ونخرج منه ونشغل البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_63" style=""><span class="pln">$ node badLoop</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنلاحظ ظهور قيمة المجموع الصحيحة ونكون بذلك حللنا المشكلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_65" style=""><span class="lit">1564</span></pre>

<p>
	نكون بذلك قد تعلمنا طريقة استخدام المنقح ودالة مراقبة المتغيرات <code>‎watch‎</code> الخاصة به لاستكشاف وتحديد الأخطاء أثناء التنفيذ!
</p>

<p>
	وسنتعلم الآن في الفقرة التالية كيف يمكننا الاستفادة من نقاط الوقوف لتنقيح الأخطاء ضمن البرنامج دون الحاجة لتقديم التنفيذ يدويًا سطرًا تلو الآخر.
</p>

<h2>
	استخدام نقاط الوقوف Breakpoints
</h2>

<p>
	تتألف البرامج في نود عادة من عدة وحدات برمجية يتشابك عملها مع بعضها بعضًا، لذا محاولة تنقيح الأخطاء سطرًا تلو الآخر كما فعلنا في الفقرة السابقة أمر صعب وغير مجدي في التطبيقات الكبيرة المعقدة، وهنا يأتي دور نقاط الوقوف breakpoints لحل تلك المشكلة.
</p>

<p>
	تسمح نقاط الوقوف بتخطي التنفيذ إلى السطر الذي نريده مباشرةً وإيقاف البرنامج لمعاينة حالته آنذاك، حيث لإضافة نقطة وقوف في نود نضيف الكلمة المحجوزة <code>‎debugger‎</code> ضمن الشيفرة مباشرةً، ويمكننا بعدها وخلال عملية التنقيح التنقل بين نقاط الوقوف ضمن الشيفرة بتنفيذ الأمر <code>‎c‎</code> في طرفية التنقيح بدلًا من الأمر <code>‎n‎</code> السابق، ويمكننا إضافة المراقبة للتعليمات التي نرغب بها عند نقاط الوقوف تلك.
</p>

<p>
	سنتعرف على طريقة استخدام نقاط الوقوف بمثال عن برنامج يقرأ قائمة من الجمل ويستخرج منها الكلمة الأكثر تكرارًا ويعيدها لنا، لذلك سنُنشئ لهذا المثال ثلاث ملفات، الأول هو ملف يحوي الجمل النصية ‎sentences.txt‎ التي سيعالجها البرنامج، حيث سنضيف داخله كمثال أول فقرة من <a href="https://www.britannica.com/animal/whale-shark" rel="external nofollow">مقال عن سمكة قرش الحوت من موسوعة بريتانيكا Britannica</a> بعد إزالة علامات الترقيم منها، لذلك ننشئ الملف ونفتحه ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_67" style=""><span class="pln">$ nano sentences</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	ونكتب داخله النص التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_69" style=""><span class="typ">Whale</span><span class="pln"> shark </span><span class="typ">Rhincodon</span><span class="pln"> typus gigantic but harmless shark family </span><span class="typ">Rhincodontidae</span><span class="pln"> that is the largest living fish
</span><span class="typ">Whale</span><span class="pln"> sharks are found in marine environments worldwide but mainly in tropical oceans
</span><span class="typ">They</span><span class="pln"> make up the only species of the genus </span><span class="typ">Rhincodon</span><span class="pln"> and are classified within the order </span><span class="typ">Orectolobiformes</span><span class="pln"> a group containing the carpet sharks
</span><span class="typ">The</span><span class="pln"> whale shark is enormous and reportedly capable of reaching a maximum length of about </span><span class="lit">18</span><span class="pln"> metres </span><span class="lit">59</span><span class="pln"> feet
</span><span class="typ">Most</span><span class="pln"> specimens that have been studied however weighed about </span><span class="lit">15</span><span class="pln"> tons about </span><span class="lit">14</span><span class="pln"> metric tons and averaged about </span><span class="lit">12</span><span class="pln"> metres </span><span class="lit">39</span><span class="pln"> feet in length
</span><span class="typ">The</span><span class="pln"> body coloration is distinctive
</span><span class="typ">Light</span><span class="pln"> vertical and horizontal stripes form a checkerboard pattern on a dark background and light spots mark the fins and dark areas of the body</span></pre>

<p>
	نحفظ الملف ونخرج منه، ونضيف الشيفرة التالية إلى ملف <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> جديد بالاسم ‎textHelper.js‎، حيث سيحوي هذا الملف على بعض الدوال المساعدة في معالجة الملف النصي السابق خلال عملية تحديد الكلمة الأكثر تكرارًا من النص، ونبدأ بإنشاء الملف ‎textHelper.js‎ ونفتحه ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_71" style=""><span class="pln">$ nano textHelper</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونضيف ثلاث دوال لمعالجة النص ضمن الملف ‎sentences.txt‎ الأول لقراءة الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_75" style=""><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> readFile </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let data </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">'sentences.txt'</span><span class="pun">);</span><span class="pln">
  let sentences </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> sentences</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	نستورد الوحدة البرمجية <code>‎fs‎</code> من نود لنتمكن من قراءة الملف، بعدها نضيف الدالة <code>‎readFile()‎</code> التي تستخدم التابع <code>‎readFileSync()‎</code> لتحميل محتوى الملف ‎sentences.txt‎ ككائن <a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">مخزن مؤقت</a> <code>‎Buffer‎</code> ثم تستدعي منه التابع <code>‎toString()‎</code> لتحويل المحتوى إلى سلسلة نصية.
</p>

<p>
	نضيف بعدها دالة لتجزئة السلسلة نصية السابقة إلى مصفوفة من الكلمات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_79" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let allSentences </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'\n'</span><span class="pun">);</span><span class="pln">
  let flatSentence </span><span class="pun">=</span><span class="pln"> allSentences</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
  let words </span><span class="pun">=</span><span class="pln"> flatSentence</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
  words </span><span class="pun">=</span><span class="pln"> words</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">().</span><span class="pln">toLowerCase</span><span class="pun">());</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> words</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	استفدنا من التوابع <code>‎split()‎</code> و <code>‎join()‎</code> و <code>‎map()‎</code> لتحويل السلسلة النصية إلى مصفوفة من الكلمات الموجودة ضمنها، وحولنا حالة كل كلمة منها إلى أحرف صغيرة لتسهيل عملية المقارنة بينها وإحصائها.
</p>

<p>
	أما الدالة الثالثة والأخيرة فستحصي تكرار كل كلمة ضمن مصفوفة الكلمات السابقة ويعيد كل الكلمات مع تكراراتها ضمن كائن يعبر عن النتيجة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8288_81" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> countWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">words</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let </span><span class="typ">map</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  words</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">word in </span><span class="typ">map</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">map</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أنشأنا كائنًا جديدًا بالاسم <code>‎map‎</code> يحوي الكلمات ضمن النص كمفاتيح وعدد مرات تكرارها كقيم لها، ثم مررنا على عناصر مصفوفة الكلمات وأضفناها إلى ذلك الكائن إن تكن موجودة أو زدنا قيمة تكرارها قيمة واحدة.
</p>

<p>
	وأخيرًا لنصدر تلك الدوال لنتمكن من استخدامها ضمن الوحدات البرمجية الأخرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_83" style=""><span class="pun">...</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> readFile</span><span class="pun">,</span><span class="pln"> getWords</span><span class="pun">,</span><span class="pln"> countWords </span><span class="pun">};</span></pre>

<p>
	نحفظ الملف ونخرج منه، والآن سننشئ الملف الثالث والأخير ضمن المثال هو الملف الأساسي الذي سيستعين بالدوال ضمن الوحدة البرمجية السابقة ‎textHelper.js‎ لاستخراج أكثر كلمة تكرارًا من النص.
</p>

<p>
	نبدأ بإنشاء الملف ‎index.js‎ ثم نفتحه ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_85" style=""><span class="pln">$ nano index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نستورد الوحدة البرمجية ‎textHelpers.js‎ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_87" style=""><span class="kwd">const</span><span class="pln"> textHelper </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./textHelper'</span><span class="pun">);</span></pre>

<p>
	وننشئ مصفوفة جديدة تحتوي على بعض الكلمات المكررة الشائعة التي نرغب بتجاهلها مثل حروف العطف والجر والضمائر وبعض الصفات، تدعى <a href="https://ar.wikipedia.org/wiki/%D8%A7%D8%B3%D8%AA%D8%A8%D8%B9%D8%A7%D8%AF_%D8%A7%D9%84%D9%83%D9%84%D9%85%D8%A7%D8%AA_%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9" rel="external nofollow">الكلمات الشائعة</a> أو <a href="https://en.wikipedia.org/wiki/Stop_words" rel="external nofollow">stop words</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_89" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> stopwords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'i'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'me'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'my'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'myself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'we'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'our'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ours'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ourselves'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'you'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'your'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yours'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yourself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yourselves'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'he'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'him'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'his'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'himself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'she'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'her'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hers'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'herself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'it'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'its'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'itself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'they'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'them'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'their'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'theirs'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'themselves'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'what'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'which'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'who'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'whom'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'this'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'that'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'these'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'those'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'am'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'is'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'are'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'was'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'were'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'be'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'been'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'being'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'have'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'has'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'had'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'having'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'do'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'does'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'did'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'doing'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'an'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'the'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'and'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'but'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'if'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'or'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'because'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'as'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'until'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'while'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'of'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'at'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'by'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'for'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'with'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'about'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'against'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'between'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'into'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'through'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'during'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'before'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'after'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'above'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'below'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'to'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'from'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'up'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'down'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'in'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'out'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'on'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'off'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'over'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'under'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'again'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'further'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'then'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'once'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'here'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'there'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'when'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'where'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'why'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'how'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'all'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'any'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'both'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'each'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'few'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'more'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'most'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'other'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'some'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'such'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'no'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'nor'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'not'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'only'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'own'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'same'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'so'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'than'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'too'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'very'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'s'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'can'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'will'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'just'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'don'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'should'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'now'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">];</span></pre>

<p>
	بهذه الطريقة سنحصل على كلمات ذات معاني من ضمن النص الذي نعالجه بدلًا من الحصول على كلمات مثل أدوات التعريف التي تتكرر كثيرًا مثل the‎ و a‎.
</p>

<p>
	نبدأ باستخدام الدوال المساعدة من الوحدة ‎textHelper.js‎ لقراءة النص واستخراج الكلمات منه وإحصاء مرات التكرار لكل منها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_91" style=""><span class="pun">...</span><span class="pln">

let sentences </span><span class="pun">=</span><span class="pln"> textHelper</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">();</span><span class="pln">
let words </span><span class="pun">=</span><span class="pln"> textHelper</span><span class="pun">.</span><span class="pln">getWords</span><span class="pun">(</span><span class="pln">sentences</span><span class="pun">);</span><span class="pln">
let wordCounts </span><span class="pun">=</span><span class="pln"> textHelper</span><span class="pun">.</span><span class="pln">countWords</span><span class="pun">(</span><span class="pln">words</span><span class="pun">);</span></pre>

<p>
	بعد ذلك سنستخرج أكثر كلمة تكرارًا منها، وخوارزمية تحديد الكلمة الأكثر تكرارًا هي بالمرور أولًا على مفاتيح كائن الكلمات المحصاة ومقارنة التكرار مع آخر أعلى قيمة مررنا عليها سابقًا، وفي حال كانت قيمة التكرار للمفتاح الحالي أعلى من الكلمة السابقة سنحدد تكرار الكلمة الحالية على أنه التكرار الأعلى، لتصبح الشيفرة لهذه الخوارزمية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_93" style=""><span class="pun">...</span><span class="pln">

let max </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="kwd">Infinity</span><span class="pun">;</span><span class="pln">
let mostPopular </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">

</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">entries</span><span class="pun">(</span><span class="pln">wordCounts</span><span class="pun">).</span><span class="pln">forEach</span><span class="pun">(([</span><span class="pln">word</span><span class="pun">,</span><span class="pln"> count</span><span class="pun">])</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stopwords</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">count </span><span class="pun">&gt;</span><span class="pln"> max</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      max </span><span class="pun">=</span><span class="pln"> count</span><span class="pun">;</span><span class="pln">
      mostPopular </span><span class="pun">=</span><span class="pln"> word</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> most popular word in the text is </span><span class="str">"${mostPopular}"</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">max</span><span class="pun">}</span><span class="pln"> occurrences</span><span class="pun">`);</span></pre>

<p>
	استخدمنا التابع <code>‎Object.entries()‎</code> لتحويل المفاتيح والقيم ضمن الكائن <code>‎wordCounts‎</code> إلى مصفوفة، ثم استخدمنا التابع <code>‎forEach()‎</code> وداخله عبارة شرطية لاختبار قيمة التكرار للكلمة الحالية مع أعلى قيمة تكرار شاهدناها سابقًا.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_103" style=""><span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ ظهور النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_97" style=""><span class="typ">The</span><span class="pln"> most popular word in the text is </span><span class="str">"whale"</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> occurrences</span></pre>

<p>
	لكن الجواب الذي ظهر خاطئ فنلاحظ تكرار الكلمة <code>‎whale‎</code> أكثر من مرة ضمن النص في الملف ‎sentences.txt‎، وهذه المرة قد يكون السبب في أحد الدوال العديدة المستخدمة في البرنامج، فقد تكون المشكلة في عملية قراءة محتوى الملف كاملًا، أو خلال معالجته وتحويله لمصفوفة الكلمات، أو خلال عملية توليد كائن إحصاء مرات التكرار للكلمات، أو قد يكون الخطأ في خوارزمية تحديد الكلمة الأكثر تكرارًا.
</p>

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

<p>
	لنبدأ بإضافة نقاط وقوف ضمن كل من التوابع المساعدة في الملف ‎textHelper.js‎ بإضافة الكلمة المحجوزة <code>‎debugger‎</code> ضمن الشيفرة في تلك الأماكن، لذا نفتح الملف ‎textHelper.js‎ ضمن محرر النصوص ونضيف أول نقطة وقوف ضمن التابع <code>‎readFile()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_106" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> readFile </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let data </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">'sentences.txt'</span><span class="pun">);</span><span class="pln">
  let sentences </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> sentences</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	بعدها نضيف نقطة وقوف أخرى ضمن الدالة <code>‎getWords()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_109" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let allSentences </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'\n'</span><span class="pun">);</span><span class="pln">
  let flatSentence </span><span class="pun">=</span><span class="pln"> allSentences</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
  let words </span><span class="pun">=</span><span class="pln"> flatSentence</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
  words </span><span class="pun">=</span><span class="pln"> words</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">().</span><span class="pln">toLowerCase</span><span class="pun">());</span><span class="pln">
  </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> words</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	وأخيرًا نضيف نقطة وقوف للدالة <code>‎countWords()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_111" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> countWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">words</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let map </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  words</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">word in map</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> map</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	نحفظ الملف ونخرج منه، ونبدأ جلسة تنقيح الأخطاء ومع أن كل نقاط الوقوف التي أضفناها موجودة ضمن الملف ‎textHelpers.js‎ لكن عملية تنقيح الأخطاء ستبدأ من الملف الرئيسي للتطبيق ‎index.js‎، لذا ندخل لجلسة تنقيح الأخطاء من ذلك الملف كما تعلمنا سابقًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_113" style=""><span class="pln">$ node inspect index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر لنا التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_115" style=""><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Debugger</span><span class="pln"> listening on ws</span><span class="pun">:</span><span class="com">//127.0.0.1:9229/b2d3ce0e-3a64-4836-bdbf-84b6083d6d30</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">For</span><span class="pln"> help</span><span class="pun">,</span><span class="pln"> see</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//nodejs.org/en/docs/inspector</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">Debugger</span><span class="pln"> attached</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Break</span><span class="pln"> on start in index</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">1</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> textHelper </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./textHelper'</span><span class="pun">);</span><span class="pln">
  </span><span class="lit">2</span><span class="pln"> 
  </span><span class="lit">3</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> stopwords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'i'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'me'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'my'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'myself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'we'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'our'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ours'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ourselves'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'you'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'your'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yours'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yourself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'yourselves'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'he'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'him'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'his'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'himself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'she'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'her'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hers'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'herself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'it'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'its'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'itself'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'they'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'them'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'their'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'theirs'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'themselves'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'what'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'which'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'who'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'whom'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'this'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'that'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'these'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'those'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'am'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'is'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'are'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'was'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'were'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'be'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'been'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'being'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'have'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'has'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'had'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'having'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'do'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'does'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'did'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'doing'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'an'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'the'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'and'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'but'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'if'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'or'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'because'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'as'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'until'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'while'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'of'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'at'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'by'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'for'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'with'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'about'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'against'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'between'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'into'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'through'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'during'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'before'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'after'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'above'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'below'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'to'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'from'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'up'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'down'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'in'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'out'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'on'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'off'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'over'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'under'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'again'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'further'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'then'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'once'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'here'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'there'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'when'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'where'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'why'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'how'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'all'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'any'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'both'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'each'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'few'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'more'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'most'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'other'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'some'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'such'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'no'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'nor'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'not'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'only'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'own'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'same'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'so'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'than'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'too'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'very'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'s'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'can'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'will'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'just'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'don'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'should'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'now'</span><span class="pun">,</span><span class="pln"> </span><span class="str">''</span><span class="pun">];</span></pre>

<p>
	هذه المرة سننفذ الأمر <code>‎c‎</code> وهو اختصار للكلمة continue وتعني إكمال التنفيذ لينتقل بذلك المنقح مباشرة إلى أول نقطة وقوف يصل إليها تنفيذ الشيفرة، وبعد الضغط على زر الإدخال ‎ENTER‎ لتنفيذ الأمر يظهر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_117" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">6</span><span class="pln">
  </span><span class="lit">4</span><span class="pln">   let data </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">'sentences.txt'</span><span class="pun">);</span><span class="pln">
  </span><span class="lit">5</span><span class="pln">   let sentences </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">6</span><span class="pln">   </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">7</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> sentences</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">8</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	نلاحظ كم من الوقت قد وفرنا في هذه العملية حيث توجهنا مباشرة إلى أول نقطة وقوف، ولنتأكد من أن هذه الدالة تعمل بشكل سليم وتقرأ محتوى الملف النصي كاملًا وتعيده، سنراقب المتغير <code>‎sentences‎</code> لنعاين قيمته ونتأكد من صحة القيمة التي تعيدها الدالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_119" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'sentences'</span><span class="pun">)</span></pre>

<p>
	نتقدم بالتنفيذ خطوة للأمام فقط بتنفيذ الأمر <code>‎n‎</code> لنعاين قيمة المتغير <code>‎sentences‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_121" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">7</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> sentences </span><span class="pun">=</span><span class="pln">
    </span><span class="str">'Whale shark Rhincodon typus gigantic but harmless shark family Rhincodontidae that is the largest living fish\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      </span><span class="str">'Whale sharks are found in marine environments worldwide but mainly in tropical oceans\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      </span><span class="str">'They make up the only species of the genus Rhincodon and are classified within the order Orectolobiformes a group containing the carpet sharks\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      </span><span class="str">'The whale shark is enormous and reportedly capable of reaching a maximum length of about 18 metres 59 feet\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      </span><span class="str">'Most specimens that have been studied however weighed about 15 tons about 14 metric tons and averaged about 12 metres 39 feet in length\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      </span><span class="str">'The body coloration is distinctive\n'</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      </span><span class="str">'Light vertical and horizontal stripes form a checkerboard pattern on a dark background and light spots mark the fins and dark areas of the body\n'</span><span class="pln">

  </span><span class="lit">5</span><span class="pln">   let sentences </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
  </span><span class="lit">6</span><span class="pln">   </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">7</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> sentences</span><span class="pun">;</span><span class="pln">
  </span><span class="lit">8</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
  </span><span class="lit">9</span></pre>

<p>
	تبدو القيمة صحيحة ولا مشاكل في عملية قراءة محتوى الملف إذًا فالمشكلة في مكان آخر.
</p>

<p>
	لننتقل إلى نقطة الوقوف التالية بتنفيذ الأمر <code>‎c‎</code> مجددًا ليظهر ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_123" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">15</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> sentences </span><span class="pun">=</span><span class="pln">
    </span><span class="typ">ReferenceError</span><span class="pun">:</span><span class="pln"> sentences is not defined
        at </span><span class="kwd">eval</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">eval</span><span class="pln"> at getWords </span><span class="pun">(</span><span class="pln">your_file_path</span><span class="pun">/</span><span class="kwd">debugger</span><span class="pun">/</span><span class="pln">textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">3</span><span class="pun">),</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">anonymous</span><span class="pun">&gt;:</span><span class="lit">1</span><span class="pun">:</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getWords </span><span class="pun">(</span><span class="pln">your_file_path</span><span class="pun">/</span><span class="kwd">debugger</span><span class="pun">/</span><span class="pln">textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">15</span><span class="pun">:</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Object</span><span class="pun">.&lt;</span><span class="pln">anonymous</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">your_file_path</span><span class="pun">/</span><span class="kwd">debugger</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">7</span><span class="pun">:</span><span class="lit">24</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Module</span><span class="pun">.</span><span class="pln">_compile </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">1125</span><span class="pun">:</span><span class="lit">14</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Object</span><span class="pun">.</span><span class="typ">Module</span><span class="pun">.</span><span class="pln">_extensions</span><span class="pun">..</span><span class="pln">js </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">1167</span><span class="pun">:</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Module</span><span class="pun">.</span><span class="pln">load </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">983</span><span class="pun">:</span><span class="lit">32</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Function</span><span class="pun">.</span><span class="typ">Module</span><span class="pun">.</span><span class="pln">_load </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">891</span><span class="pun">:</span><span class="lit">14</span><span class="pun">)</span><span class="pln">
        at </span><span class="typ">Function</span><span class="pun">.</span><span class="pln">executeUserEntryPoint </span><span class="pun">[</span><span class="pln">as runMain</span><span class="pun">]</span><span class="pln"> </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">run_main</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">71</span><span class="pun">:</span><span class="lit">12</span><span class="pun">)</span><span class="pln">
        at internal</span><span class="pun">/</span><span class="pln">main</span><span class="pun">/</span><span class="pln">run_main_module</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">17</span><span class="pun">:</span><span class="lit">47</span><span class="pln">

 </span><span class="lit">13</span><span class="pln">   let words </span><span class="pun">=</span><span class="pln"> flatSentence</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
 </span><span class="lit">14</span><span class="pln">   words </span><span class="pun">=</span><span class="pln"> words</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">().</span><span class="pln">toLowerCase</span><span class="pun">());</span><span class="pln">
</span><span class="pun">&gt;</span><span class="lit">15</span><span class="pln">   </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">16</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> words</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">17</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	رسالة الخطأ التي ظهرت سببها مراقبتنا سابقًا لقيمة المتغير <code>‎sentences‎</code> الذي لم يعد موجودًا الآن ضمن نطاق تنفيذ الدالة الحالية، حيث تبقى عملية المراقبة للمتغير طول مدة جلسة تنقيح الأخطاء، لذا سيتكرر ظهور رسالة الخطأ تلك ما دام المتغير لا يمكن الوصول إليه من مكان التنفيذ الحالي.
</p>

<p>
	ويمكننا حل تلك المشكلة بإيقاف مراقبة المتغير باستخدام الدالة <code>‎unwatch()‎</code> لإيقاف مراقبة المتغير <code>‎sentences‎</code> بتنفيذ التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_125" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> unwatch</span><span class="pun">(</span><span class="str">'sentences'</span><span class="pun">)</span></pre>

<p>
	لن تظهر أي رسالة عند تنفيذ التعليمة السابقة، والآن لنعود إلى الدالة <code>‎getWords()‎</code> ونتأكد من صحة القيمة التي تعيدها وهي قائمة من كلمات النص السابق، لهذا نضيف مراقبة للمتغير <code>‎words‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_127" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'words'</span><span class="pun">)</span></pre>

<p>
	وننتقل لتنفيذ السطر التالي بتنفيذ التعليمة <code>‎n‎</code> ونعاين قيمة المتغير <code>‎words‎</code>، ونلاحظ ظهور ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_129" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">16</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> words </span><span class="pun">=</span><span class="pln">
    </span><span class="pun">[</span><span class="pln"> </span><span class="str">'whale'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'shark'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'rhincodon'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'typus'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'gigantic'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'but'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'harmless'</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">...</span><span class="pln">
      </span><span class="str">'metres'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'39'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'feet'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'in'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'length'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">''</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'the'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'body'</span><span class="pun">,</span><span class="pln">
      </span><span class="str">'coloration'</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">...</span><span class="pln"> </span><span class="pun">]</span><span class="pln">

 </span><span class="lit">14</span><span class="pln">   words </span><span class="pun">=</span><span class="pln"> words</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">().</span><span class="pln">toLowerCase</span><span class="pun">());</span><span class="pln">
 </span><span class="lit">15</span><span class="pln">   </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
</span><span class="pun">&gt;</span><span class="lit">16</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> words</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">17</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
 </span><span class="lit">18</span></pre>

<p>
	لم يُظهر منقح الأخطاء محتوى المصفوفة كاملةً بسبب طولها وصعوبة قراءتها كاملة، ولكن ما ظهر يكفي ليؤكد أن محتوى النص ضمن المتغير <code>‎sentences‎</code> تم تجزئته إلى كلمات بحالة أحرف صغيرة، أي أن الدالة <code>‎getWords()‎</code> تعمل بشكل سليم.
</p>

<p>
	والآن ننتقل لمعاينة الدالة الثالثة وهي <code>‎countWords()‎</code>، ولكن أولًا سنزيل المراقبة للمصفوفة <code>‎words‎</code> كي لا يظهر لنا رسالة خطأ كما حدث سابقًا عند الانتقال إلى نقطة الوقوف التالية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_131" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> unwatch</span><span class="pun">(</span><span class="str">'words'</span><span class="pun">)</span></pre>

<p>
	ثم ننفذ الأمر <code>‎c‎</code> لينتقل التنفيذ إلى نقطة الوقوف التالية ويظهر ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_133" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">29</span><span class="pln">
 </span><span class="lit">27</span><span class="pln">   </span><span class="pun">});</span><span class="pln">
 </span><span class="lit">28</span><span class="pln"> 
</span><span class="pun">&gt;</span><span class="lit">29</span><span class="pln">   </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">30</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> map</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">31</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	سنتأكد ضمن هذه الدالة من احتواء المتغير <code>‎map‎</code> على كل الكلمات السابقة مع قيم تكرارها، لذا نبدأ مراقبة المتغير <code>‎map‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_135" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'map'</span><span class="pun">)</span></pre>

<p>
	ثم ننتقل بالتنفيذ إلى السطر التالي بتنفيذ الأمر <code>‎n‎</code> ليظهر لنا ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_137" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">30</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> map </span><span class="pun">=</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> </span><span class="lit">12</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      </span><span class="lit">14</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      </span><span class="lit">15</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      </span><span class="lit">18</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      </span><span class="lit">39</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      </span><span class="lit">59</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      whale</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
      shark</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
      rhincodon</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
      typus</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      gigantic</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">...</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

 </span><span class="lit">28</span><span class="pln">
 </span><span class="lit">29</span><span class="pln">   </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
</span><span class="pun">&gt;</span><span class="lit">30</span><span class="pln">   </span><span class="kwd">return</span><span class="pln"> map</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">31</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
 </span><span class="lit">32</span></pre>

<p>
	على ما يبدو أن هذه الدالة هي سبب المشكلة وعملية إحصاء تكرار الكلمات خاطئة، ولمعرفة سبب الخطأ يجب أن نعاين عمل هذه الدالة ضمن حلقة المرور على عناصر المصفوفة <code>‎words‎</code>، لذا سنعدل أماكن نقاط الوقوف الحالية.
</p>

<p>
	نبدأ بالخروج من منقح الأخطاء بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_139" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">exit</span></pre>

<p>
	ثم نفتح الملف ‎textHelper.js‎ ضمن محرر النصوص لنعدل نقاط الوقوف ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_141" style=""><span class="pln">$ nano textHelper</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	بما أننا تأكدنا من صحة عمل الدالتين <code>‎readFile()‎</code> و <code>‎getWords()‎</code> سنزيل نقاط الوقوف من داخلهما، ونزيل نقطة الوقوف من نهاية الدالة <code>‎countWords()‎</code> ونضيف نقطتي وقوف جديدتين في بداية ونهاية الدالة <code>‎forEach()‎</code>، ليصبح الملف ‎textHelper.js‎ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_143" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> readFile </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let data </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">'sentences.txt'</span><span class="pun">);</span><span class="pln">
  let sentences </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> sentences</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let allSentences </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">'\n'</span><span class="pun">);</span><span class="pln">
  let flatSentence </span><span class="pun">=</span><span class="pln"> allSentences</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
  let words </span><span class="pun">=</span><span class="pln"> flatSentence</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln">
  words </span><span class="pun">=</span><span class="pln"> words</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">().</span><span class="pln">toLowerCase</span><span class="pun">());</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> words</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> countWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">words</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let map </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  words</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">word in map</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> map</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	نحفظ الملف ونخرج منه ثم نبدأ جلسة تنقيح أخطاء جديدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_145" style=""><span class="pln">$ node inspect index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	كي نحدد سبب المشكلة يجب أن نراقب عدة قيم، أولها قيمة الكلمة الحالية <code>‎word‎</code> المُمررة كمعامل من قبل تابع حلقة التكرار <code>‎forEach()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_147" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'word'</span><span class="pun">)</span></pre>

<p>
	لا تقتصر ميزة المراقبة ضمن جلسة تنقيح الأخطاء على المتغيرات فحسب، بل يمكن مراقبة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1226/" rel="">قيم تعابير جافاسكربت</a> البرمجية المستخدمة ضمن الشيفرة، كأن نراقب قيمة تنفيذ التعليمة الشرطية <code>‎word in map‎</code> والتي تحدد ما إذا كانت الكلمة الحالية موجودة مسبقًا، ويمكن مراقبتها بتنفيذ التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_149" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'word in map'</span><span class="pun">)</span></pre>

<p>
	لنضيف مراقبة لقيمة تكرار الكلمة الحالية ضمن متغير النتيجة <code>‎map‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_155" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'map[word]'</span><span class="pun">)</span></pre>

<p>
	لا تقتصر ميزة المراقبة على التعابير البرمجية الموجودة ضمن الشيفرة فحسب، بل يمكن إضافة أي تعابير برمجية نريدها ليتم تنفيذها ومراقبة قيمتها، لذا سنستفيد من هذه الميزة ونضيف مراقبة لقيمة طول الكلمة الحالية ضمن المتغير <code>‎word‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_157" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> watch</span><span class="pun">(</span><span class="str">'word.length'</span><span class="pun">)</span></pre>

<p>
	بعد أن انتهينا من إضافة القيم التي نريد مراقبتها أثناء التنفيذ سننفذ الأمر <code>‎c‎</code> ونراقب كيف تعالج الدالة أول كلمة من مصفوفة الكلمات ضمن الحلقة داخل الدالة <code>‎countWords()‎</code>، ليظهر لنا ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_161" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">20</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> word </span><span class="pun">=</span><span class="pln"> </span><span class="str">'whale'</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> word in map </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pln">
  </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pln">
  </span><span class="lit">3</span><span class="pun">:</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">

 </span><span class="lit">18</span><span class="pln">   let map </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
 </span><span class="lit">19</span><span class="pln">   words</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">&gt;</span><span class="lit">20</span><span class="pln">     </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">21</span><span class="pln">     </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">word in map</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="lit">22</span><span class="pln">       map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	الكلمة الأولى التي يتم معالجتها هي <code>‎whale‎</code> ولا يحوي الكائن <code>‎map‎</code> على مفتاح للكلمة <code>‎whale‎</code> لأنه فارغ، لذا قيمة المراقبة للكلمة الحالية <code>‎whale‎</code> ضمن الكائن <code>‎map‎</code> كما نلاحظ هي <code>‎undefined‎</code>، وطول الكلمة الحالية <code>‎whale‎</code> هو <code>‎5‎</code>، وهذه القيمة تحديدًا لا تفيدنا في البحث عن سبب الخطأ، ولكننا أضفناها لنتعلم كيف يمكن حساب ومراقبة أي تعبير برمجي خلال جلسة تنقيح الأخطاء.
</p>

<p>
	والآن ننفذ التعليمة <code>‎c‎</code> لنرى ماذا سيحدث في نهاية تنفيذ الدورة الحالية ليظهر لنا ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_163" style=""><span class="kwd">break</span><span class="pln"> in textHelper</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">26</span><span class="pln">
</span><span class="typ">Watchers</span><span class="pun">:</span><span class="pln">
  </span><span class="lit">0</span><span class="pun">:</span><span class="pln"> word </span><span class="pun">=</span><span class="pln"> </span><span class="str">'whale'</span><span class="pln">
  </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> word in map </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pln">
  </span><span class="lit">3</span><span class="pun">:</span><span class="pln"> word</span><span class="pun">.</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln">

 </span><span class="lit">24</span><span class="pln">       map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">25</span><span class="pln">     </span><span class="pun">}</span><span class="pln">
</span><span class="pun">&gt;</span><span class="lit">26</span><span class="pln">     </span><span class="kwd">debugger</span><span class="pun">;</span><span class="pln">
 </span><span class="lit">27</span><span class="pln">   </span><span class="pun">});</span><span class="pln">
 </span><span class="lit">28</span></pre>

<p>
	أصبحت قيمة العبارة <code>‎word in map‎</code> صحيحة <code>‎true‎</code> بسبب إضافة مفتاح للكلمة الحالية <code>‎whale‎</code> ضمن الكائن <code>‎map‎</code>، ولكن قيمة المفتاح <code>‎whale‎</code> ضمن الكائن <code>‎map‎</code> هي <code>‎NaN‎</code> ما يدل على وجود مشكلة ما، وتحديدًا في العبارة الشرطية <code>‎if‎</code> ضمن الدالة <code>‎countWords()‎</code>، فوظيفتها هي تحديد فيما إذا كنا سنضيف مفتاحًا جديدًا للكلمة الحالية إذا لم تكن موجودة سابقًا، أو إضافة واحد لقيمة المفتاح إن كان موجودًا مسبقًا، والصحيح هو تعيين القيمة <code>‎map[word]‎</code> إلى <code>‎1‎</code> إذا لم تكن الكلمة <code>‎word‎</code> موجودة كمفتاح ضمن <code>‎map‎</code>، بينما حاليًا نحن نضيف قيمة واحد في حال العثور على <code>‎word‎</code> وهو عكس المطلوب.
</p>

<p>
	وكما لاحظنا في بداية الحلقة كانت قيمة التكرار للكلمة الحالية <code>‎map["whale"]‎</code> غير موجودة <code>‎undefined‎</code>، وفي جافاسكربت إذا حاولنا إضافة واحد إلى تلك القيمة <code>‎undefined + 1‎</code> سينتج عن تلك العملية القيمة <code>‎NaN‎</code> وهو ما ظهر بالفعل، ولتصحيح هذه المشكلة يمكننا تعديل الشرط ضمن <code>‎if‎</code>، فبدلًا من أن يكون <code>‎word in map‎</code> ننفي هذه العبارة لتصبح كالتالي <code>‎!(word in map)‎</code>، حيث يُستخدم الرمز <code>‎!‎</code> لنفي العبارات المنطقية فيصبح الشرط صحيحًا إذا لم يحتوي الكائن <code>‎map‎</code> على مفتاح للقيمة <code>‎word‎</code>.
</p>

<p>
	والآن لننفذ هذا التعديل ضمن الدالة <code>‎countWords()‎</code> ونختبرها مجددًا، لكن نخرج أولًا من جلسة تنقيح الأخطاء كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_165" style=""><span class="pln">debug</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">exit</span></pre>

<p>
	ونفتح الملف ‎textHelper.js‎ مجددًا ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_167" style=""><span class="pln">$ nano textHelper</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نعدل الدالة <code>‎countWords()‎</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_169" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> countWords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">words</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let map </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  words</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!(</span><span class="pln">word in map</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      map</span><span class="pun">[</span><span class="pln">word</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> map</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="pun">...</span></pre>

<p>
	نحفظ الملف ونخرج منه، وننفذ البرنامج ونراقب النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_171" style=""><span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	تظهر لنا النتيجة التالية هذه المرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_173" style=""><span class="typ">The</span><span class="pln"> most popular word in the text is </span><span class="str">"whale"</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> occurrences</span></pre>

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

<p>
	وتعلمنا أيضًا كيف يمكن إضافة نقاط الوقوف باستخدام الكلمة <code>‎debugger‎</code> وإعداد مراقبة لمختلف القيم والعبارات البرمجية لمراقبة حالة البرنامج أثناء التنفيذ وكل ذلك من سطر الأوامر، ولكن لتوفير تجربة استخدام أسهل يمكن إجراء العملية نفسها عبر واجهة مستخدم مرئية، وهذا ما سنتعرف عليه في الفقرة التالية.
</p>

<p>
	سنتعلم في الفقرة التالية طريقة استخدام منقح الأخطاء من أدوات المطور في متصفح جوجل كروم، حيث سنبدأ جلسة لتنقيح الأخطاء في نود كما فعلنا سابقًا، وسنستعمل صفحة مخصصة من واجهة متصفح كروم لتعيين نقاط الوقوف وعمليات المراقبة من واجهة مرئية بدلًا من سطر الأوامر.
</p>

<h2>
	تنقيح الأخطاء في نود باستخدام أدوات المطور في كروم
</h2>

<p>
	تعد أدوات المطور في متصفح كروم من أشهر أدوات نتقيح الأخطاء لشيفرة جافاسكربت عمومًا ونود خصوصًا ضمن متصفح الويب، وذلك لأن محرك جافاسكربت المستخدم من قبل نود V8 هو نفسه المستخدم في متصفح كروم، لذا فالتكامل بينهما يوفر تجربة مرنة لتنقيح الأخطاء.
</p>

<p>
	سنطبق في هذه الفقرة على مثال بسيط وهو <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم</a> HTTP في نود مهمته إعادة قيمة بصيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> كرد على الطلبات الواردة، وسنستخدم لاحقًا منقح الأخطاء لإعداد نقاط الوقوف ومراقبة عمل ذلك الخادم وتحديدًا كيف يتم توليد قيمة الرد على الطلبات الواردة، وللمزيد حول عملية إنشاء الخادم، راجع مقالة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-http-r1745/" rel="">إنشاء خادم ويب في Node.js باستخدام الوحدة HTTP</a>.
</p>

<p>
	نبدأ بإنشاء ملف جافاسكربت جديد بالاسم ‎server.js‎ سيحوي على برنامج الخادم ونفتح الملف ضمن محرر النصوص كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_177" style=""><span class="pln">$ nano server</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	مهمة الخادم هي إعادة العبارة <code>‎Hello World‎</code> بصيغة JSON ضمن الرد، حيث سيحوي على مصفوفة لعدة ترجمات لتلك العبارة ليختار إحداها عشوائيًا ويعيدها ضمن جسم الرد بصيغة JSON، وسيستمع الخادم إلى الطلبات الواردة على العنوان المحلي <code>‎localhost‎</code> وعلى المنفذ رقم <code>‎:8000‎</code>.
</p>

<p>
	والآن نبدأ بإضافة شيفرة البرنامج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_179" style=""><span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> host </span><span class="pun">=</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> greetings </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">"Hello world"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Hola mundo"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Bonjour le monde"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Hallo Welt"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Salve mundi"</span><span class="pun">];</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getGreeting </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let greeting </span><span class="pun">=</span><span class="pln"> greetings</span><span class="pun">[</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> greetings</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)];</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> greeting
</span><span class="pun">}</span></pre>

<p>
	استوردنا الوحدة برمجية <code>‎http‎</code> والتي تساعد في إعداد خادم HTTP، ثم وضعنا قيم عنوان الخادم ورقم المنفذ ضمن المتغيرين <code>‎host‎</code> و <code>‎port‎</code> لاستخدامها لاحقًا لتشغيل الخادم، ثم عرفنا مصفوفة العبارات <code>‎greetings‎</code> والتي تحوي على جميع العبارات الممكن إرسالها من قبل الخادم لتختار الدالة <code>‎getGreeting()‎</code> إحداها عشوائيًا ويعيده.
</p>

<p>
	والآن سنضيف دالة معالجة طلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> القادمة للخادم وشيفرة بدء تشغيل الخادم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_182" style=""><span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let message </span><span class="pun">=</span><span class="pln"> getGreeting</span><span class="pun">();</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(`‎{</span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"${message}"</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">requestListener</span><span class="pun">);</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> host</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//${host}:${port}‎`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	أصبح الخادم بذلك جاهزًا للخطوة التالية وهي إعداد منقح أخطاء كروم، لهذا نبدأ جلسة تنقيح الأخطاء بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_184" style=""><span class="pln">$ node </span><span class="pun">--</span><span class="pln">inspect server</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	<strong>ملاحظة</strong>: نلاحظ الفرق بين أمر بدء منقح الأخطاء الخاص بنود من سطر الأوامر وبين أمر منقح الأخطاء الخاص بكروم، حيث ننفذ الأمر <code>‎inspect‎</code> للأول، أما للثاني نمرر الخيار <code>‎--inspect‎</code>.
</p>

<p>
	وبعد تشغيل منقح الأخطاء سنلاحظ ظهور ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_186" style=""><span class="typ">Debugger</span><span class="pln"> listening on ws</span><span class="pun">:</span><span class="com">//127.0.0.1:9229/996cfbaf-78ca-4ebd-9fd5-893888efe8b3</span><span class="pln">
</span><span class="typ">For</span><span class="pln"> help</span><span class="pun">,</span><span class="pln"> see</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//nodejs.org/en/docs/inspector</span><span class="pln">
</span><span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	يمكننا الآن فتح متصفح جوجل كروم أو كروميوم Chromium والذهاب للعنوان <a href="chrome://xn--inspect-v06c" rel="external nofollow"><code>‎chrome://inspect‎</code></a> من شريط العنوان في الأعلى، ويمكن أيضًا استعمال منقح الأخطاء لمتصفح مايكروسوفت إيدج <a href="https://www.microsoft.com/en-us/edge" rel="external nofollow">Microsoft Edge</a> ولكن بالذهاب إلى العنوان <a href="edge://xn--inspect-v06c" rel="external nofollow"><code>‎edge://inspect‎</code></a> بدلًا من العنوان السابق.
</p>

<p>
	وبعد الذهاب لذلك العنوان ستظهر لنا الصفحة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113228" href="https://academy.hsoub.com/uploads/monthly_2022_12/Chrome_inspect_page.png.b0e202e06fc2c95c27aa1f4826205c7c.png" rel="" data-fileext="png"><img alt="صفحة DevTools" class="ipsImage ipsImage_thumbnailed" data-fileid="113228" data-unique="slen1q3bj" style="width: 650px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_12/Chrome_inspect_page.thumb.png.cf87a7e7734d5852df849e481814b743.png"></a>
</p>

<p>
	نذهب لقسم الأجهزة Devices ونضغط على أمر فتح أداوت المطور الخاصة بنود "Open dedicated DevTools for Node" لتظهر لنا نافذة منفصلة كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113230" href="https://academy.hsoub.com/uploads/monthly_2022_12/debug_window.png.b8fcc4391730ec5e0142f9337fbbaec5.png" rel="" data-fileext="png"><img alt="فتح أداوت المطور الخاصة بنود" class="ipsImage ipsImage_thumbnailed" data-fileid="113230" data-unique="z6y4wq123" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_12/debug_window.thumb.png.7eca93885e2b2240615bb49bd3d92191.png"></a>
</p>

<p>
	يمكننا الآن تنقيح أخطاء برنامج نود السابق بواسطة كروم، لذلك نذهب إلى تبويب المصادر Sources ونوسع قسم شجرة الملفات الظاهر على اليسار ونختار منه ملف البرنامج الخاص بنا وهو ‎server.js‎:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113229" href="https://academy.hsoub.com/uploads/monthly_2022_12/debugger_window_sources_tab.png.fe669f0ee733b47e105d646b67f6ef3a.png" rel="" data-fileext="png"><img alt="اختيار ملف البرنامج ‎server.js‎" class="ipsImage ipsImage_thumbnailed" data-fileid="113229" data-unique="xk6u4d8ul" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_12/debugger_window_sources_tab.thumb.png.c8048451213a8224fb1ceb22a30a8989.png"></a>
</p>

<p>
	ونضيف نقطة وقوف ضمن الشيفرة التي تظهر، حيث نريد التوقف بعد أن يختار البرنامج عبارة الترحيب التي سيعيدها ضمن الرد لنعاينها، لذلك يمكننا الضغط مباشرة على رقم السطر <strong>10</strong> لتظهر نقطة حمراء بجانبه ما يدل على إضافة نقطة وقوف في هذا السطر، وهو ما نلاحظه من قائمة نقاط الوقوف في اللوحة على اليمين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113227" href="https://academy.hsoub.com/uploads/monthly_2022_12/adding_a_breakpoint.png.84a8eddccb162c9563c0065af46f45c4.png" rel="" data-fileext="png"><img alt="إضافة نقطة وقوف في السطور" class="ipsImage ipsImage_thumbnailed" data-fileid="113227" data-unique="9op4rlhms" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_12/adding_a_breakpoint.thumb.png.2170063b1c5b4bc66e325f5d04d958da.png"></a>
</p>

<p>
	لنراقب الآن عبارة برمجية، حيث يمكننا ذلك من اللوحة على اليمين وتحديدًا بجانب عنوان قسم المراقبة Watch بالضغط على علامة الزائد "+"، ونضيف اسم المتغير <code>‎greeting‎</code> لنراقب قيمته أثناء التنفيذ ثم نضغط على زر الإدخال ‎ENTER‎.
</p>

<p>
	والآن لنبدأ بتنقيح البرنامج، فنذهب ضمن نافذة المتصفح إلى عنوان الذي يستمع إليه الخادم <code>‎http://localhost:8000‎</code> وبعد الضغط على زر الإدخال ‎ENTER‎ للذهاب إلى ذلك العنوان سنلاحظ عدم ظهور أي رد مباشرة بل ستظهر لنا نافذة تنقيح الأخطاء في الواجهة مجددًا، وفي حال لم تظهر النافذة يمكن الذهاب إليها يدويًا لنلاحظ ظهور ما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="113231" href="https://academy.hsoub.com/uploads/monthly_2022_12/execution_paused.png.8ae82238f93305319ba8e426f1eee05a.png" rel="" data-fileext="png"><img alt="الانتقال للبدأ بتنقيح البرنامج في Node.js" class="ipsImage ipsImage_thumbnailed" data-fileid="113231" data-unique="4p5jar4zx" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_12/execution_paused.thumb.png.211f6c65b7b0a96705e27c0ce48ac0ec.png"></a>
</p>

<p>
	حيث توقف تنفيذ الخادم عند نقطة الوقوف التي عيّناها سابقًا، ونلاحظ تحديث قيم المتغيرات التي نراقبها في لوحة المراقبة على الجانب الأيمن، وكذلك تظهر تلك القيمة بجانب السطر الحالي ضمن الشيفرة.
</p>

<p>
	ولمتابعة تنفيذ الشيفرة نضغط على زر المتابعة الموجود في اللوحة على الجانب الأيمن فوق العبارة "Paused on breakpoint" والتي تعني توقف التنفيذ عند نقطة الوقوف، وبعد اكتمال التنفيذ ستلاحظ ظهور رد بصيغة JSON ضمن نافذة المتصفح التي تواصلنا منها مع الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8288_198" style=""><span class="pun">{</span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hello world"</span><span class="pun">}</span></pre>

<p>
	نلاحظ أننا لم نضيف أي عبارات ضمن الشيفرة أو نعدل عليها لإضافة نقاط الوقوف، وهي الفائدة التي تقدمها أدوات تنقيح الأخطاء من الواجهة المرئية مثل كروم، وهو الخيار الأفضل لمن لا يرغب بالتعامل مع سطر الأوامر ويفضل التعامل مع الواجهات المرئية.
</p>

<h2>
	ختامًا
</h2>

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

<p>
	يمكن الاستعانة بمنقح الأخطاء ما يسهل عملية استكشاف أخطاء التنفيذ ضمن البرنامج ومعاينة حالته، ما يوفر من وقت التطوير وخصوصًا وقت حل المشكلات وإصلاح الأخطاء.
</p>

<p>
	ويمكن الرجوع إلى توثيق نود الرسمي عن <a href="https://wiki.hsoub.com/Node.js/debugger" rel="external">أدوات تنقيح الأخطاء</a> أو دليل <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D9%86%D9%82%D9%8A%D8%AD-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%81%D9%8A-chrome-r784/" rel="">أدوات المطور من كروم</a> ودليل <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1-r665/" rel="">أدوات المطور لتنقيح شيفرة جافاسكربت</a>.
</p>

<p>
	تُعَد الأخطاء البرمجية أمرًا شائعًا في المجال البرمجي، وللتعرف أكثر عليها وعلى كيفية التعامل معها عامةً ننصحك بالاطلاع على الفيديو الآتي:
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" src="https://academy.hsoub.com/applications/core/interface/index.html" title="كيفية التعامل مع الأخطاء البرمجية" width="1072" data-embed-src="https://www.youtube.com/embed/Pgje6nWuDkg"></iframe>
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-debug-node-js-with-the-built-in-debugger-and-chrome-devtools" rel="external nofollow">How To Debug Node.js with the Built-In Debugger and Chrome DevTools</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%B1%D8%B3%D9%84-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-event-emitter-%D9%81%D9%8A-nodejs-r1806/" rel="">استخدام مرسل الأحداث Event emitter في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D9%86%D9%82%D9%8A%D8%AD-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%81%D9%8A-chrome-r784/" rel="">تنقيح أخطاء شيفرة جافاسكربت في Chrome</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1807</guid><pubDate>Mon, 26 Dec 2022 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x645;&#x631;&#x633;&#x644; &#x627;&#x644;&#x623;&#x62D;&#x62F;&#x627;&#x62B; Event emitter &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%B1%D8%B3%D9%84-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-event-emitter-%D9%81%D9%8A-nodejs-r1806/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/638ae6df724a1_---Event-emitter--Node.png.e0c2025745ce20b8f5172c3a3879bd69.png" /></p>

<p>
	مرسل أو مطلق الأحداث event emitter هو كائن في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">نود Node.js</a> مهمته إطلاق حدث ما عبر إرسال رسالة تخبر بوقوع حدث، حيث يمكن استخدامه لربط تنفيذ بعض التعليمات البرمجية في جافاسكربت بحدث ما، وذلك عبر الاستماع لذلك الحدث وتنفيذ تابع ما عند كل تنبيه بحدوثه، ويتم تمييز تلك الأحداث عن بعضها بسلسلة نصية تُعبّر عن اسم الحدث ويمكن إرفاق بيانات تصف ذلك الحدث إلى التوابع المُستمعة له.
</p>

<p>
	عادة ما نربط تنفيذ التعليمات البرمجية بعد اكتمال حدث ما باستخدام طرق <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%BA%D9%8A%D8%B1-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-nodejs-r1743/" rel="">البرمجة اللامتزامنة</a> asynchronous programming، كتمرير توابع رد النداء أو ربط الوعود مع بعضها، ولكن من مساوئ تلك الطرق هو الربط بين أمر تنفيذ الحدث والتعليمات الواجب تنفيذها بعد انتهاءه، مما يزيد صعوبة التعديل على تلك التعليمات لاحقًا، وهنا يأتي دور مرسل الأحداث ليوفر طريقة بديلة للربط بين الحدث والمهام المرتبطة به، باتباع نمط ناشر-مشترك publish-subscribe، حيث يرسل فيه الناشر أو مرسل الأحداث رسالة تعبر عن حدث ما، ثم يستقبل بدوره المشترك هذه الإشارة وينفذ تعليمات برمجية استجابة لذلك الحدث، ومن ميزات هذا النمط هو المقال بين الناشر والمشترك، بحيث لا يعلم الناشر أي شيء عن المشتركين، فينشر الناشر الرسائل فقط ثم يتفاعل معها المشتركون كلٌّ بطريقته الخاصة، وبالتالي يصبح تعديل التطبيق أسهل عبر تعديل طريقة عمل المشتركين فقط دون أي تعديل على الناشر.
</p>

<p>
	سنتعلم في هذا المقال طريقة إنشاء واستخدام مرسل الأحداث عبر تطوير صنف مرسل أحداث خاص لإدارة شراء البطاقات بالاسم <code>‎TicketManager‎</code>، وسنربط به بعض المشتركين الذين سيتفاعلون مع حدث الشراء <code>‎buy‎</code> الذي سيُنشر بعد كل عملية شراء لبطاقة ما، وسنتعلم أيضًا طرقًا لمعالجة أحداث الأخطاء التي قد يرسلها المرسل، وكيفية إدارة المشتركين بالأحداث.
</p>

<h2>
	المستلزمات
</h2>

<p>
	هذا المقال جزء من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">دليل تعلم Node.js</a> لذا يجب قبل قراءته:
</p>

<ul>
<li>
		تثبيت بيئة Node.js على الجهاز، حيث استخدمنا في هذا المقال الإصدار رقم 10.20.1.
	</li>
	<li>
		معرفة بأساسيات استخدام <a href="https://academy.hsoub.com/programming/javascript/%D8%B5%D9%8A%D8%A7%D8%BA%D8%A9-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-class-basic-syntax-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r901/" rel="">الأصناف في جافاسكربت</a>، حيث سنستخدمها ضمن الأمثلة في هذا المقال وهي متوفرة في جافاسكربت منذ الإصدار ES2015 أو ES6.
	</li>
</ul>
<h2>
	إرسال أحداث Emitting Events
</h2>

<p>
	سنتعلم في هذه الفقرة طريقتين لإنشاء مرسل أحداث في نود، الأولى باستخدام صنف مرسل الأحداث مباشرةً <code>EventEmitter</code>، والثانية بإنشاء صنف خاص يرث من صنف مرسل الأحداث الأساسي، ويعتمد الاختيار بين هاتين الطريقتين على مدى الترابط بين الأحداث ضمن التطبيق وبين العمليات التي ستسبب إرسالها، فإذا كانت العمليات داخل الكائن هي ما ستسبب إرسال الأحداث، أي يوجد ترابط وثيق بين العمليات والأحداث فهنا يفضل استخدام طريقة الوراثة من صنف مرسل الأحداث الأساسي، أما إذا كان العمليات منفصلة أو متفرقة، مثلًا نتيجة عدة عمليات نُفذت ضمن أكثر من كائن، فيفضل استخدام كائن مرسل للأحداث منفصل نستخدمه ضمن التطبيق داخليًا.
</p>

<p>
	ولنبدأ بالتعرف على طريقة استخدام كائن مرسل أحداث منفصل، ونبدأ أولًا بإنشاء مجلد للمشروع بالاسم <code>‎event-emitters‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_8" style="">
<span class="pln">$ mkdir event</span><span class="pun">-</span><span class="pln">emitters</span></pre>

<p>
	وندخل إلى المجلد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_10" style="">
<span class="pln">$ cd event</span><span class="pun">-</span><span class="pln">emitters</span></pre>

<p>
	نُنشئ ملف جافاسكربت جديد بالاسم ‎firstEventEmitter.js‎ ونفتحه ضمن أي محرر نصوص، حيث سنستخدم في أمثلتنا محرر <code>‎nano‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_12" style="">
<span class="pln">$ nano firstEventEmitter</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	يمكن استخدام الصنف <code>‎EventEmitter‎</code> الموجود ضمن الوحدة <code>‎events‎</code> في نود لإرسال الأحداث، ولنبدأ باستيراد ذلك الصنف من تلك الوحدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_14" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span></pre>

<p>
	ثم ننشئ كائنًا جديدًا من ذلك الصنف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_16" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> firstEmitter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pun">();</span></pre>

<p>
	ونختبر إرسال حدث ما من هذا الكائن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_18" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> firstEmitter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pun">();</span><span class="pln">


firstEmitter</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">"My first event"</span><span class="pun">);</span></pre>

<p>
	نلاحظ استدعاء التابع <code>‎emit()‎</code> لإرسال حدث جديد، حيث نمرر له اسم ذلك الحدث كسلسلة نصية وبعدها يمكن تمرير أي عدد من المعاملات الخاصة بذلك الحدث، حيث تفيد تلك المعاملات بإرسال بيانات إضافية مع الحدث تتلقاها التوابع المستمعة للحدث وتوفر بيانات إضافية توصف ذلك الحدث، وسنستخدم ذلك في مثالنا لاحقًا عندما نرسل حدث شراء لبطاقة جديدة بتمرير بعض البيانات المتعلقة بعملية الشراء تلك، ويجب أن نميز اسم الحدث لأننا سنستخدمه لاحقًا كما هو للاستماع إليه.
</p>

<p>
	<strong>ملاحظة</strong>: يعيد تنفيذ تابع الإرسال <code>‎emit()‎</code> قيمة منطقية تكون صحيحة <code>‎true‎</code> في حال كان هناك أي تابع يستمع لذلك الحدث، وفي حال لم يكن هناك أي مستمع سيعيد القيمة <code>‎false‎</code> رغم عدم توفر معلومات أخرى عن المستمعين.
</p>

<p>
	والآن نحفظ الملف وننفذه باستخدام الأمر <code>‎node‎</code> ونلاحظ النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_20" style="">
<span class="pln">$ node firstEventEmitter</span><span class="pun">.</span><span class="pln">js</span></pre>

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

<p>
	والآن لنبدأ بتطبيق مثال مدير شراء البطاقات، حيث سيوفر هذا الصنف تابعًا لعملية الشراء وبعد أن إتمام هذه العملية بنجاح سيُرسل حدث يعبر عن ذلك مرفقًا ببيانات حول المشتري للبطاقة، ثم سنطور وحدة برمجية منفصلة لمحاكاة عملية إرسال بريد إلكتروني للمشتري استجابة لحدث الشراء لنعلمه بنجاح العملية.
</p>

<p>
	نبدأ بإنشاء مدير البطاقات حيث سيرث صنف مرسل الأحداث الأساسي <code>‎EventEmitter‎</code> مباشرة كي لا نضطر لإنشاء كائن مرسل للأحداث منفصل داخليًا واستخدامه، ونٌنشئ ملف جافاسكربت جديد بالاسم ‎ticketManager.js‎:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_22" style="">
<span class="pln">$ nano ticketManager</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	كما فعلنا سابقًا نستورد الصنف <code>‎EventEmitter‎</code> من الوحدة <code>‎events‎</code> لاستخدامه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_24" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span></pre>

<p>
	ونعرف صنف مدير البطاقات <code>‎TicketManager‎</code> الذي سيوفر تابع الشراء لاحقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_26" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> extends </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	نلاحظ أن صنف مدير البطاقات <code>‎TicketManager‎</code> يرث من صنف مرسل الأحداث الأساسي <code>‎EventEmitter‎</code> ما يعني أنه سيرث كل التوابع والخواص التي يوفرها صنف مرسل الأحداث وبالتالي يمكننا استدعاء تابع إرسال الأحداث <code>‎emit()‎</code> من الصنف نفسه مباشرةً.
</p>

<p>
	ولنبدأ بتعريف التابع الباني للصنف لتمرير كمية البطاقات المتوفرة للبيع، والذي سيُستدعى عند إنشاء كائن جديد من هذا الصنف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_28" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> extends </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">(</span><span class="pln">supply</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        super</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">supply </span><span class="pun">=</span><span class="pln"> supply</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يقبل التابع الباني معامل العدد <code>‎supply‎</code> والذي يعبر عن الكمية المتوفرة للبيع، وبما أن الصنف <code>‎TicketManager‎</code> يرث من صنف مرسل الأحداث الأساسي <code>‎EventEmitter‎</code> فيجب استدعاء التابع الباني للصنف الأب عبر استدعاء <code>‎super()‎</code> وذلك لتهيئة توابع وخاصيات الصنف الأب بشكل صحيح.
</p>

<p>
	وبعد ذلك نعرف قيمة خاصية الكمية <code>‎supply‎</code> ضمن الصنف بواسطة <code>‎this.supply‎</code> ونسند القيمة المُمررة للتابع الباني لها، والآن سنضيف تابع شراء بطاقة جديدة <code>‎buy()‎</code> حيث سيُنقص هذا التابع كمية البطاقات المتوفرة ويرسل حدثًا يجوي تفاصيل عملية الشراء كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_30" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"events"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> extends </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">(</span><span class="pln">supply</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        super</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">supply </span><span class="pun">=</span><span class="pln"> supply</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    buy</span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">supply</span><span class="pun">--;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ تمرير عنوان البريد الإلكتروني والعنوان الخاص بالمشتري والسعر المدفوع ثمنًا للبطاقة للتابع <code>‎buy()‎</code>، حيث سينقص التابع كمية البطاقات المتوفرة بمقدار واحد، ثم سيرسل حدث الشراء <code>‎buy‎</code> مع تمرير بيانات إضافية هذه المرة وهي عنوان البريد الإلكتروني للمشتري وسعر البطاقة وتوقيت عملية الشراء تلك.
</p>

<p>
	والآن ولكي تستطيع باقي الوحدات البرمجية استخدام هذا الصنف يجب تصديره في نهاية الملف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_32" style="">
<span class="pun">...</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TicketManager</span></pre>

<p>
	نحفظ الملف ونخرج منه، ونكون بذلك انتهينا من إعداد صنف مدير البطاقات المُرسل للأحداث <code>‎TicketManager‎</code>، وأصبح جاهزًا لإرسال الأحداث المتعلقة بعملية شراء البطاقات الجديدة وبقي علينا الاشتراك والاستماع لذلك الحدث ومعالجته، وهذا ما سنتعرف عليه في الفقرة التالية حيث سنُنشئ توابع تستمع لذلك الحدث.
</p>

<h2>
	الاستماع للأحداث
</h2>

<p>
	يمكن تسجيل مستمع إلى حدث ما باستدعاء التابع <code>‎on()‎</code> من كائن مرسل الأحداث، حيث سيستمع لحدث معين وعند إرساله سيستدعي لنا تابع رد النداء المُمرر له، وصيغة استدعاءه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_34" style="">
<span class="pln">eventEmitter</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="pln">event_name</span><span class="pun">,</span><span class="pln"> callback_function</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    action
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: التابع <code>‎on()‎</code> هو اسم بديل للتابع <code>‎addListener()‎</code> ضمن مرسل الأحداث ولا فرق في استخدام أي منهما، حيث سنستخدم في أمثلتنا التابع <code>‎on()‎</code> دومًا.
</p>

<p>
	والآن لنبدأ بالاستماع إلى الأحداث بإنشاء ملف <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> جديد بالاسم ‎firstListener.js‎:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_36" style="">
<span class="pln">$ nano firstListener</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنختبر عملية تسجيل المستمع بطباعة رسالة ضمنه إلى الطرفية عند تلقي الحدث، ونبدأ باستيراد الصنف <code>‎TicketManager‎</code> ضمن الملف الجديد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_41" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ticketManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span></pre>

<p>
	مررنا القيمة <code>‎10‎</code> للصنف <code>‎TicketManager‎</code> كقيمة لمخزون البطاقات المتاحة، والآن لنضيف مستمع جديد لحدث الشراء <code>‎buy‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_43" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ticketManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Someone bought a ticket!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لإضافة مستمع جديد نستدعي التابع <code>‎on()‎</code> من الكائن <code>‎ticketManager‎</code>، والمتوفر ضمن كل كائنات صنف مرسل الأحداث، وبما أن الصنف <code>‎TicketManager‎</code> يرث من صنف مرسل الأحداث الأساسي <code>‎EventEmitter‎</code> بالتالي فهذا التابع أصبح متوفرًا ضمن أي كائن من صنف مدير البطاقات <code>‎TicketManager‎</code>.
</p>

<p>
	نمرر تابع رد نداء للتابع <code>‎on()‎</code> كمعامل ثاني حيث ستنفذ التعليمات ضمنه عند كل إطلاق للحدث، حيث يطبع هذا التابع الرسالة <code>‎"Someone bought a ticket!"‎</code> إلى الطرفية عند كل حدث لعملية الشراء <code>‎buy‎</code>.
</p>

<p>
	وبعد أن سجلنا التابع كمستمع للحدث ننفذ عملية الشراء باستدعاء التابع <code>‎buy()‎</code> لينتج عنه إرسال لحدث الشراء كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_45" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span></pre>

<p>
	استدعينا تابع الشراء <code>‎buy‎</code> بعنوان البريد الإلكتروني <code>‎test@email.com‎</code> وبسعر <code>‎20‎</code> لتلك للبطاقة، والآن نحفظ الملف ونخرج منه وننفذ البرنامج بتنفيذ الأمر <code>‎node‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_47" style="">
<span class="pln">$ node firstListener</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ ظهور الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_49" style="">
<span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span></pre>

<p>
	بذلك يكون مرسل الأحداث قد أرسل الحدث بنجاح وتم معالجته من قبل تابع الاستماع.
</p>

<p>
	والآن لنجرب أكثر من عملية شراء ونراقب ماذا سيحدث، نفتح الملف ‎firstListener.js‎ للتعديل مجددًا ونستدعي تابع الشراء <code>‎buy()‎</code> مرة أخرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_51" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه وننفذ الملف مجددًا ونلاحظ النتيجة هذه المرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_53" style="">
<span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span></pre>

<p>
	بما أن تابع الشراء <code>‎buy()‎</code> قد استُدعي مرتين فقد نتج عنه إرسال لحدث الشراء <code>‎buy‎</code> مرتين أيضًا، ثم استقبل تابع الاستماع هذين الحدثين وطبع الرسالة مرتين.
</p>

<p>
	قد نحتاج في بعض الأحيان للاستماع لأول مرة يُرسَل فيها الحدث فقط وليس لكل مرة، ويمكن ذلك عبر استدعاء تابع مشابه للتابع <code>‎on()‎</code> وهو <code>‎once()‎</code> يعمل بنفس الطريقة، فهو سيسجل تابع الاستماع للحدث المحدد بالمعامل الأول، وسينفذ التابع المُمرر كمعامل ثاني له، ولكن الفرق هنا أن التابع <code>‎once()‎</code> وبعد استقبال الحدث لأول مرة سيُلغي اشتراك تابع الاستماع بالحدث ويزيله، وينفذه لمرة واحدة فقط عند أول استقبال للحدث بعد عملية التسجيل.
</p>

<p>
	ولنوضح ذلك باستخدامه ضمن الملف ‎firstListener.js‎ نفتحه مجددًا للتعديل ونضيف في نهايته الشيفرة التالية لتسجيل تابع للاستماع لحدث الشراء لمرة واحدة فقط باستخدام <code>‎once()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_55" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ticketManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Someone bought a ticket!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">once</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"This is only called once"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نحفظ الملف ونخرج منه وننفذ البرنامج ونلاحظ الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_57" style="">
<span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span></pre>

<p>
	لا نلاحظ أي فرق هذه المرة عن الخرج السابق، والسبب أننا سجلنا مستمع للحدث بعد الانتهاء من إرسال حدث الشراء <code>‎buy‎</code> وليس قبله، لذا لم يُنفذ التابع لأنه لم يستقبل أي أحداث جديدة، أي لا يمكن الاستماع إلا للأحداث التي سترد لاحقًا بعد عملية تسجيل التابع، أما الأحداث السابقة فلا يمكن الاستماع لها، ولحل المشكلة يمكن استدعاء تابع الشراء <code>‎buy()‎</code> مرتين من جديد بعد تسجيل تابع الاستماع باستخدام <code>‎once()‎</code> لنتأكد أنه لن يُنفذ سوى لمعالجة أول حدث منها فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_59" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">once</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"This is only called once"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه وننفذ البرنامج لنحصل على خرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_61" style="">
<span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span><span class="pln">
</span><span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span><span class="pln">
</span><span class="typ">This</span><span class="pln"> is only called once
</span><span class="typ">Someone</span><span class="pln"> bought a ticket</span><span class="pun">!</span></pre>

<p>
	أول رسالتين ظهرتا نتيجة أول استدعاءين لتابع الشراء <code>‎buy()‎</code> وقبل تسجيل تابع الاستماع باستخدام <code>‎once()‎</code>، ولكن إضافة تابع الاستماع الجديد لا يزيل وجود التوابع المسجلة سابقًا، وستبقى تستمع للأحداث اللاحقة وتطبع تلك الرسائل، وبوجود توابع استماع تم تسجيلها باستخدام <code>‎on()‎</code> قبل تابع الاستماع الجديد الذي سجلناه باستخدام <code>‎once()‎</code>، فسنلاحظ ظهور الرسالة <code>‎Someone bought a ticket!‎</code> قبل الرسالة <code>‎This is only called once‎</code>، وكلا السطرين هما استجابة لحدث الشراء <code>‎buy‎</code> الثاني وما بعده.
</p>

<p>
	وعند آخر استدعاء لتابع الشراء <code>‎buy()‎</code> لم يبقى ضمن مرسل الأحداث سوى التوابع التي تستمع لهذا الحدث والتي سُجلت باستخدام <code>‎on()‎</code>، حيث أن التابع المستمع الذي سجلناه باستخدام <code>‎once()‎</code> أزيل تلقائيًا بعد تنفيذه لمرة واحدة فقط.
</p>

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

<h2>
	استقبال بيانات الحدث
</h2>

<p>
	تعلمنا في الفقرة السابقة طريقة الاستماع للأحداث والاستجابة لها، لكن عادة ما يُرسل مع هذه الأحداث بيانات إضافية توضح الحدث، وسنتعلم في هذه الفقرة كيف يمكننا استقبال البيانات والتعامل معها.
</p>

<p>
	سننشئ وحدتين برمجيتين الأولى لإرسال البريد الإلكتروني والثانية لتسجيل البيانات في قاعدة البيانات، ولن نتطرق لتفاصيل تلك الوحدات بل سنضع مثالًا يُعبّر عن تنفيذ العمليات الخاصة بها لأن تركيزنا هو على طريقة استقبالها لبيانات الأحداث، وبعد إنشاء تلك الوحدتين سنربطهما مع مرسل الأحداث ضمن ملف البرنامج الأساسي ‎index.js‎.
</p>

<p>
	والآن لنبدأ بإنشاء وحدة خدمة إرسال البريد الإلكتروني البرمجية نُنشئ لها ملف جافاسكربت جديد ونفتحه ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_63" style="">
<span class="pln">$ nano emailService</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ستحوي هذه الوحدة على صنف يوفر تابع الإرسال <code>‎send()‎</code> والذي سنمرر له عنوان البريد الإلكتروني المأخوذ من بيانات حدث الشراء <code>‎buy‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_65" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    send</span><span class="pun">(</span><span class="pln">email</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`‎</span><span class="typ">Sending</span><span class="pln"> email to $</span><span class="pun">{</span><span class="pln">email</span><span class="pun">}‎`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">EmailService</span></pre>

<p>
	عرفنا الصنف <code>‎EmailService‎</code> الحاوي على تابع الإرسال <code>‎send()‎</code> والذي بدلًا من إرسال بريد إلكتروني حقيقي سيطبع رسالة توضح تنفيذ هذه العملية مع توضيح عنوان البريد الإلكتروني المرسل إليه.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ثم نُنشئ ملف جافاسكربت جديد بالاسم ‎databaseService.js‎ لوحدة خدمة قاعدة البيانات البرمجية ونفتحه ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_67" style="">
<span class="pln">$ nano databaseService</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سيُحاكي هذا الصنف حفظ بيانات عملية الشراء ضمن قاعدة البيانات عند استدعاء تابع الحفظ <code>‎save()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_69" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    save</span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> timestamp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">$</span><span class="pun">{</span><span class="pln">email</span><span class="pun">},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">price</span><span class="pun">},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">timestamp</span><span class="pun">})`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">DatabaseService</span></pre>

<p>
	عرفنا الصنف <code>‎DatabaseService‎</code> الحاوي على تابع الحفظ <code>‎save()‎</code> حيث سيحاكي عملية حفظ البيانات إلى قاعدة البيانات بطباعة البيانات الممررة له إلى الطرفية أيضًا، حيث سنمرر له البيانات المرفقة مع حدث الشراء <code>‎buy‎</code> وهي عنوان البريد الإلكتروني للمشتري وسعر البطاقة وتوقيت عملية الشراء.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونبدأ بربط مدير البطاقات <code>‎TicketManager‎</code> مع كل من خدمتي البريد الإلكتروني <code>‎EmailService‎</code> وخدمة قاعدة البيانات <code>‎DatabaseService‎</code>، حيث سنسجل تابع استماع لحدث الشراء <code>‎buy‎</code> سيستدعي داخله تابع إرسال البريد الإلكتروني <code>‎send()‎</code> وتابع حفظ البيانات في قاعدة البيانات <code>‎save()‎</code>، لذا ننشئ ملف جافاسكربت الرئيسي للبرنامج ‎index.js‎ ونفتحه ضمن محرر النصوص ونبدأ باستيراد الوحدات البرمجية اللازمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_71" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./emailService"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./databaseService"</span><span class="pun">);</span></pre>

<p>
	ثم ننشئ كائنات جديدة من الأصناف السابقة، وفي هذه الخطوة سنحدد كمية قليلة للبطاقات المتاحة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_73" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./emailService"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./databaseService"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ticketManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> emailService </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> databaseService </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pun">();</span></pre>

<p>
	بعدها نبدأ بتسجيل تابع الاستماع لحدث الشراء باستخدام الكائنات السابقة، حيث نريد بعد كل عملية شراء لبطاقة جديدة إرسال بريد إلكتروني للمشتري وحفظ بيانات تلك العملية في قاعدة البيانات، لذلك نضيف ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_75" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./emailService"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./databaseService"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ticketManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> emailService </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> databaseService </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pun">();</span><span class="pln">


ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> timestamp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    emailService</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">email</span><span class="pun">);</span><span class="pln">
    databaseService</span><span class="pun">.</span><span class="pln">save</span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> timestamp</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	أضفنا كما تعلمنا سابقًا تابع استماع للحدث باستخدام التابع <code>‎on()‎</code>، والفرق هذه المرة أننا نقبل ثلاث معاملات ضمن تابع رد النداء تمثل البيانات المرفقة مع الحدث، ولمعرفة البيانات التي سترسل نعاين طريقة إرسال حدث الشراء داخل التابع <code>‎buy()‎</code> من صنف مدير البطاقات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_77" style="">
<span class="kwd">this</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">());</span></pre>

<p>
	حيث سيقابل كل معامل نقبله ضمن تابع رد النداء معاملًا من البيانات التي نمررها لتابع إرسال الحدث السابق، فأول معامل هو البريد الإلكتروني <code>‎email‎</code> ثم السعر <code>‎price‎</code> ثم توقيت الشراء وهو التوقيت الحالي <code>‎Date.now()‎</code> والذي يقابل المعامل الأخير المسمى <code>‎timestamp‎</code> في تابع رد النداء.
</p>

<p>
	وفي تابع الاستماع للحدث وعند كل إرسال لحدث الشراء <code>‎buy‎</code> سيُستدعى تابع إرسال البريد الإلكتروني <code>‎send()‎</code> من كائن الخدمة <code>‎emailService‎</code>، ثم تابع حفظ البيانات ضمن قاعدة البيانات من كائن الخدمة الخاصة به <code>‎databaseService‎</code>.
</p>

<p>
	والآن لنختبر عملية الربط تلك كاملة باستدعاء تابع الشراء <code>‎buy()‎</code> في نهاية الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_79" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه، ننفذ البرنامج بتنفيذ الأمر <code>‎node‎</code> ونعاين النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_81" style="">
<span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ ظهور النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_84" style="">
<span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588720081832</span><span class="pun">)</span></pre>

<p>
	نلاحظ استقبال تابع الاستماع للبيانات المرفقة بالحدث بنجاح، والآن بعد أن تعلمنا طرق إضافة توابع الاستماع لمختلف الأحداث بأسماء وبيانات مختلفة ماذا عن الأخطاء التي قد تحدث خلال عملية الشراء؟ وكيف يمكننا معالجتها والاستجابة لها؟ هذا ما سنتعرف عليه في الفقرة التالية، حيث سنتعرف أيضًا على المعايير الواجب اتباعها عند معالجة الأخطاء.
</p>

<h2>
	معالجة أخطاء الأحداث
</h2>

<p>
	عند فشل تنفيذ عملية ما يجب أن يُعلم مرسل الأحداث المشتركين بذلك، والطريقة المتبعة عادةً في نود تكون بإرسال حدث مخصص بالاسم<code>‎error‎</code> يُعبّر عن حدوث خطأ ما أثناء التنفيذ، ويرفق به كائن الخطأ <code>‎Error‎</code> لتوضيح المشكلة.
</p>

<p>
	وحاليًا في صنف مدير البطاقات لدينا الكمية المتاحة تتناقص بمقدار واحد في كل مرة ننفذ تابع الشراء <code>‎buy()‎</code>، ويمكن حاليًا تجاوز الكمية المتاحة وشراء عدد غير محدود من البطاقات، لنحل هذه المشكلة بتعديل تابع الشراء <code>‎buy()‎</code> ليرسل حدث يعبر عن خطأ في حال نفاذ الكمية المتاحة من البطاقات ومحاولة أحدهم شراء بطاقة جديدة، لذا نعود لملف مدير البطاقات ‎ticketManager.js‎ ونعدل تابع الشراء <code>‎buy()‎</code> ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_86" style="">
<span class="pun">...</span><span class="pln">

buy</span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">supply </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">supply</span><span class="pun">—;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">());</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"There are no more tickets left to purchase"</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	أضفنا العبارة الشرطية <code>‎if‎</code> لحصر عملية شراء البطاقات فقط في حال توفر كمية منها، عبر التحقق من أن الكمية الحالية أكبر من الصفر، أما في حال نفاذ الكمية سنرسل حدث الخطأ <code>‎error‎</code> ونرفق به كائن خطأ <code>‎Error‎</code> جديد يحوي وصفًا حول سبب الخطأ.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونحاول الوصول لتلك الحالة من الملف الرئيسي ‎index.js‎، فحاليًا نشتري بطاقة واحدة فقط والكمية المتاحة ضمن كائن مدير البطاقات <code>‎ticketManager‎</code> هي ثلاث بطاقات فقط، لذا للوصول لحالة الخطأ يجب أن نشتري أربعة بطاقات لتجاوز الكمية المتاحة، لهذا نعود للملف ‎index.js‎ لنعدل عليه ونضيف الأسطر التالية في نهاية الملف لشراء أربعة بطاقات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_88" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه وننفذ البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_90" style="">
<span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على خرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_92" style="">
<span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588724932796</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588724932812</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588724932812</span><span class="pun">)</span><span class="pln">
events</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">196</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> er</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Unhandled 'error' event</span><span class="pln">
      </span><span class="pun">^</span><span class="pln">

</span><span class="typ">Error</span><span class="pun">:</span><span class="pln"> </span><span class="typ">There</span><span class="pln"> are no more tickets left to purchase
    at </span><span class="typ">TicketManager</span><span class="pun">.</span><span class="pln">buy </span><span class="pun">(</span><span class="str">/home/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">event</span><span class="pun">-</span><span class="pln">emitters</span><span class="pun">/</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">28</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Object</span><span class="pun">.&lt;</span><span class="pln">anonymous</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">/home/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">event</span><span class="pun">-</span><span class="pln">emitters</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">17</span><span class="pun">:</span><span class="lit">15</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Module</span><span class="pun">.</span><span class="pln">_compile </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">1128</span><span class="pun">:</span><span class="lit">30</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Object</span><span class="pun">.</span><span class="typ">Module</span><span class="pun">.</span><span class="pln">_extensions</span><span class="pun">..</span><span class="pln">js </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">1167</span><span class="pun">:</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Module</span><span class="pun">.</span><span class="pln">load </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">983</span><span class="pun">:</span><span class="lit">32</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Function</span><span class="pun">.</span><span class="typ">Module</span><span class="pun">.</span><span class="pln">_load </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">cjs</span><span class="pun">/</span><span class="pln">loader</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">891</span><span class="pun">:</span><span class="lit">14</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Function</span><span class="pun">.</span><span class="pln">executeUserEntryPoint </span><span class="pun">[</span><span class="pln">as runMain</span><span class="pun">]</span><span class="pln"> </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">modules</span><span class="pun">/</span><span class="pln">run_main</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">71</span><span class="pun">:</span><span class="lit">12</span><span class="pun">)</span><span class="pln">
    at internal</span><span class="pun">/</span><span class="pln">main</span><span class="pun">/</span><span class="pln">run_main_module</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">17</span><span class="pun">:</span><span class="lit">47</span><span class="pln">
</span><span class="typ">Emitted</span><span class="pln"> </span><span class="str">'error'</span><span class="pln"> event on </span><span class="typ">TicketManager</span><span class="pln"> instance at</span><span class="pun">:</span><span class="pln">
    at </span><span class="typ">TicketManager</span><span class="pun">.</span><span class="pln">buy </span><span class="pun">(</span><span class="str">/home/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">event</span><span class="pun">-</span><span class="pln">emitters</span><span class="pun">/</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">14</span><span class="pun">)</span><span class="pln">
    at </span><span class="typ">Object</span><span class="pun">.&lt;</span><span class="pln">anonymous</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="str">/home/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">event</span><span class="pun">-</span><span class="pln">emitters</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">17</span><span class="pun">:</span><span class="lit">15</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">[...</span><span class="pln"> lines matching original stack trace </span><span class="pun">...]</span><span class="pln">
    at internal</span><span class="pun">/</span><span class="pln">main</span><span class="pun">/</span><span class="pln">run_main_module</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">17</span><span class="pun">:</span><span class="lit">47</span></pre>

<p>
	جرى معالجة أول ثلاث أحداث شراء <code>‎buy‎</code> بنجاح بينما سبب الحدث الرابع بعد نفاذ الكمية ذلك الخطأ، لنعاين رسالة الخطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_94" style="">
<span class="pun">...</span><span class="pln">
events</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">196</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> er</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Unhandled 'error' event</span><span class="pln">
      </span><span class="pun">^</span><span class="pln">

</span><span class="typ">Error</span><span class="pun">:</span><span class="pln"> </span><span class="typ">There</span><span class="pln"> are no more tickets left to purchase
    at </span><span class="typ">TicketManager</span><span class="pun">.</span><span class="pln">buy </span><span class="pun">(</span><span class="str">/home/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">event</span><span class="pun">-</span><span class="pln">emitters</span><span class="pun">/</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">16</span><span class="pun">:</span><span class="lit">28</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	توضح رسالة الخطأ نتيجة التنفيذ ونلاحظ تحديدًا رسالة الخطأ التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_96" style="">
<span class="pun">‎</span><span class="str">"Unhandled 'error' event"</span><span class="pun">‎</span></pre>

<p>
	والتي تعني أن خطأ الحدث لم يتم معالجته، ما يعني أنه في حال أرسل مرسل الأحداث حدث الخطأ ولم نسجل أي مستمع لمعالجة هذا الحدث سيتم رمي الخطأ كما بالشكل السابق، ما يؤدي كما رأينا لتوقف تنفيذ البرنامج، لذا يفضل دومًا الاستماع لحدث الخطأ <code>‎error‎</code> من مرسل الأحداث لحل هذه المشكلة ومعالجة هذا الحدث لمنع توقف عمل البرنامج.
</p>

<p>
	والآن لنطبق ذلك ونضيف تابع لمعالجة حدث الخطأ ضمن الملف ‎index.js‎ حيث نضيف تابعًا مستمعًا لحدث الخطأ قبل تنفيذ عملية شراء البطاقات، وذلك لأنه وكما ذكرنا سابقًا لا يمكن سوى معالجة الأحداث التي سترد منذ لحظة تسجيل تابع الاستماع، لذا نضيف تابع معالجة الخطأ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_98" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`‎</span><span class="typ">Gracefully</span><span class="pln"> handling our error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}‎`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email.com"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">);</span></pre>

<p>
	نطبع داخل ذلك التابع رسالة إلى الطرفية تدل على معالجة الخطأ المُرسل باستخدام <code>‎console.error()‎</code>، والآن نحفظ الملف ونخرج منه ثم نعيد تنفيذ البرنامج لنرى ما إذا كانت معالجة الخطأ ستتم بنجاح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_100" style="">
<span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	لنحصل على الخرج التالي هذه المرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_102" style="">
<span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588726293332</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588726293348</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Sending</span><span class="pln"> email to test@email</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">.</span><span class="pln">com</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588726293348</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Gracefully</span><span class="pln"> handling our error</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">:</span><span class="pln"> </span><span class="typ">There</span><span class="pln"> are no more tickets left to purchase</span></pre>

<p>
	نلاحظ في آخر سطر ظهور رسالة معالجة الخطأ من قبل تابع الاستماع الذي سجلناه ولم يفشل تنفيذ البرنامج كما حدث سابقًا.
</p>

<p>
	والآن وبعد أن تعلمنا طرق إرسال والاستماع للأحداث بمختلف أنواعها سنتعرف في الفقرة التالية على طرق مفيدة لإدارة توابع الاستماع للأحداث.
</p>

<h2>
	إدارة توابع الاستماع للأحداث
</h2>

<p>
	يوفر صنف مرسل الأحداث طرقًا لمراقبة عدد توابع الاستماع المشتركة بحدث ما والتحكم بها، حيث يمكن مثلًا الاستفادة من التابع <code>‎listenerCount()‎</code> لمعرفة عدد توابع الاستماع المسجلة لحدث معين ضمن الكائن، حيث يقبل ذلك التابع معامل يدل على الحدث الذي نريد معرفة عدد المستمعين له.
</p>

<p>
	والآن لنعود للملف الأساسي ‎index.js‎ ونطبق ذلك حيث نزيل بدايةً استدعاءات تابع الشراء <code>‎buy()‎</code> الأربعة السابقة ثم نضيف السطرين التاليين لتصبح الشيفرة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_104" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./ticketManager"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./emailService"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./databaseService"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ticketManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TicketManager</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> emailService </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EmailService</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> databaseService </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DatabaseService</span><span class="pun">();</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> timestamp</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    emailService</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">email</span><span class="pun">);</span><span class="pln">
    databaseService</span><span class="pun">.</span><span class="pln">save</span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> timestamp</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Gracefully</span><span class="pln"> handling our error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">We</span><span class="pln"> have $</span><span class="pun">{</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">)}</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the buy event</span><span class="pun">`);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">We</span><span class="pln"> have $</span><span class="pun">{</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">)}</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the error event</span><span class="pun">`);</span></pre>

<p>
	بدلًا من استدعاء تابع الشراء <code>‎buy()‎</code> نطبع إلى الطرفية سطران الأول لطباعة عدد التوابع المُستمعة لحدث الشراء <code>‎buy‎</code> باستخدام التابع <code>‎listenerCount()‎</code>، والثاني لطباعة عدد التوابع المستمعة لتابع الخطأ <code>‎error‎</code>، والآن نحفظ الملف ونخرج منه ثم ننفذ البرنامج مجددًا باستخدام الأمر <code>‎node‎</code> لنحصل على الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_106" style="">
<span class="typ">We</span><span class="pln"> have </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the buy event
</span><span class="typ">We</span><span class="pln"> have </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the error event</span></pre>

<p>
	بما أننا استدعينا تابع التسجيل <code>‎on()‎</code> مرة واحدة لحدث الشراء <code>‎buy‎</code> ومرة واحدة أيضًا لحدث الخطأ <code>‎error‎</code> فالخرج السابق صحيح.
</p>

<p>
	سنستفيد من التابع <code>‎listenerCount()‎</code> عندما نتعلم طريقة إزالة توابع الاستماع من مرسل الأحداث لنتأكد من عدم وجود مشتركين بحدث ما، فقد نحتاج أحيانًا لتسجيل تابع استماع لفترة مؤقتة فقط ثم نزيله بعد ذلك.
</p>

<p>
	يمكن الاستفادة من التابع <code>‎off()‎</code> لإزالة تابع استماع من كائن مرسل الأحداث، ويقبل معاملين هما اسم الحدث وتابع الاستماع الذي نرغب بإزالته.
</p>

<p>
	<strong>ملاحظة</strong>: التابع <code>‎off()‎</code> هو اسم بديل عن التابع <code>‎removeListener()‎</code> وكلاهما ينفذ نفس العملية ويقبل نفس المعاملات، وسنستخدم في أمثلتنا التابع <code>‎off()‎</code> دومًا.
</p>

<p>
	وبما أنه يجب تمرير تابع الاستماع الذي نرغب بإزالته كمعامل ثانِ للتابع <code>‎off()‎</code> فيجب حفظ ذلك التابع أولًا ضمن متغير أو ثابت كي نشير إليه لاحقًا ونمرره للإزالة، فلا تصلح طريقة استخدام التوابع التي سجلناها سابقًا للأحداث <code>‎buy‎</code> و <code>‎error‎</code> للإزالة باستخدام <code>‎off()‎</code>.
</p>

<p>
	ولنتعرف على طريقة عمل تابع الإزالة <code>‎off()‎</code> سنضيف تابع استماع جديد ونختبر إزالته، ونبدأ بتعريف تابع رد النداء وحفظه ضمن متغير سنمرره لاحقًا لتابع الإزالة <code>‎off()‎</code>، والآن نعود للملف الأساسي ‎index.js‎ ونفتحه ضمن محرر النصوص ونضيف التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_108" style="">
<span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> onBuy </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"I will be removed soon"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	بعدها نسجل هذا التابع للاستماع إلى الحدث <code>‎buy‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_110" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> onBuy</span><span class="pun">);</span></pre>

<p>
	وللتأكد من تسجيل التابع بشكل سليم سنطبع عدد التوابع المستمعة للحدث <code>‎buy‎</code> ثم نستدعي تابع الشراء <code>‎buy()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_112" style="">
<span class="pun">...</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">We</span><span class="pln"> added a </span><span class="kwd">new</span><span class="pln"> event listener bringing our total count </span><span class="kwd">for</span><span class="pln"> the buy event to</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">)}`);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه ونشغل البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_114" style="">
<span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سيظر لنا الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_116" style="">
<span class="typ">We</span><span class="pln"> have </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the buy event
</span><span class="typ">We</span><span class="pln"> have </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the error event
</span><span class="typ">We</span><span class="pln"> added a </span><span class="kwd">new</span><span class="pln"> event listener bringing our total count </span><span class="kwd">for</span><span class="pln"> the buy event to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="typ">Sending</span><span class="pln"> email to test@email
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588814306693</span><span class="pun">)</span><span class="pln">
I will be removed soon</span></pre>

<p>
	نلاحظ ظهور الرسالة التي توضح عدد التوابع المستمعة لذلك الحدث، ثم استدعينا بعدها التابع <code>‎buy()‎</code> ونلاحظ تنفيذ تابعي الاستماع لذلك الحدث، حيث نفذ المستمع الأول عمليتي إرسال البريد الإلكتروني وحفظ البيانات ضمن <a href="https://academy.hsoub.com/programming/sql/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%B9%D9%86-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r584/" rel="">قاعدة البيانات</a>، ثم طبع المستمع الثاني الرسالة <code>‎I will be removed soon‎</code>.
</p>

<p>
	والآن لنختبر إزالة تابع الاستماع الثاني باستخدام <code>‎off()‎</code>، لذا نعود للملف مجددًا ونضيف عملية الإزالة بواسطة <code>‎off()‎</code> في نهاية الملف وبعدها نطبع عدد توابع الاستماع الحالية المسجلة لنتأكد من الإزالة، ثم نختبر استدعاء تابع الشراء <code>‎buy()‎</code> مجددًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_118" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">off</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">,</span><span class="pln"> onBuy</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">We</span><span class="pln"> now have</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">)}</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the buy event</span><span class="pun">`);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span></pre>

<p>
	نلاحظ كيف مررنا المتغير <code>‎onBuy‎</code> كمعامل ثاني لتابع الإزالة <code>‎off()‎</code> لنحدد تابع الاستماع الذي نرغب بإزالته، نحفظ الملف ونخرج منه وننفذ البرنامج لمعاينة النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_122" style="">
<span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ أن الخرج بقي كما كان سابقًا، وظهر سطر جديد يؤكد عملية الإزالة ويوضح عدد التوابع المسجلة، ثم بعد استدعاء تابع الشراء <code>‎buy()‎</code> نلاحظ ظهور خرج تابع الاستماع الأول فقط، بينما أُزيل تابع الاستماع الثاني:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_124" style="">
<span class="typ">We</span><span class="pln"> have </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the buy event
</span><span class="typ">We</span><span class="pln"> have </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the error event
</span><span class="typ">We</span><span class="pln"> added a </span><span class="kwd">new</span><span class="pln"> event listener bringing our total count </span><span class="kwd">for</span><span class="pln"> the buy event to</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="typ">Sending</span><span class="pln"> email to test@email
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588816352178</span><span class="pun">)</span><span class="pln">
I will be removed soon
</span><span class="typ">We</span><span class="pln"> now have</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> listener</span><span class="pun">(</span><span class="pln">s</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> the buy event
</span><span class="typ">Sending</span><span class="pln"> email to test@email
</span><span class="typ">Running</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> INSERT INTO orders VALUES </span><span class="pun">(</span><span class="pln">email</span><span class="pun">,</span><span class="pln"> price</span><span class="pun">,</span><span class="pln"> created</span><span class="pun">)</span><span class="pln"> VALUES </span><span class="pun">(</span><span class="pln">test@email</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1588816352178</span><span class="pun">)</span></pre>

<p>
	يمكن أيضًا إزالة كل توابع الاستماع لحدث ما دفعة واحدة باستدعاء التابع <code>‎removeAllListeners()‎</code>، ونمرر له اسم الحدث الذي نرغب بإزالة التوابع التي تستمع إليه، وسنستفيد من هذا التابع لإزالة تابع الاستماع الأول للحدث <code>‎buy‎</code> الذي لم نتمكن من إزالته سابقًا بسبب طريقة تعريفه
</p>

<p>
	الآن نعود للملف ‎index.js‎ ونزيل كافة توابع الاستماع باستخدام <code>‎removeAllListeners()‎</code> ثم نطبع عدد التوابع المسجلة باستخدام <code>‎listenerCount()‎</code> للتأكد من نجاح العملية، ونتحقق من ذلك أيضًا بتنفيذ عملية شراء جديدة بعد الإزالة، ونلاحظ أن لا شيء سيحدث بعد إرسال ذلك الحدث، لذا نضيف الشيفرة التالية في نهاية الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_126" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">removeAllListeners</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">We</span><span class="pln"> have $</span><span class="pun">{</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">)}</span><span class="pln"> listeners </span><span class="kwd">for</span><span class="pln"> the buy event</span><span class="pun">`);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"The last ticket was bought"</span><span class="pun">);</span></pre>

<p>
	نحفظ الملف ونخرج منه ونشغل البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_128" style="">
<span class="pln">$ node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6523_130" style="">
<span class="pun">...</span><span class="pln">

ticketManager</span><span class="pun">.</span><span class="pln">removeAllListeners</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">We</span><span class="pln"> have $</span><span class="pun">{</span><span class="pln">ticketManager</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">"buy"</span><span class="pun">)}</span><span class="pln"> listeners </span><span class="kwd">for</span><span class="pln"> the buy event</span><span class="pun">`);</span><span class="pln">
ticketManager</span><span class="pun">.</span><span class="pln">buy</span><span class="pun">(</span><span class="str">"test@email"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"The last ticket was bought"</span><span class="pun">);</span></pre>

<p>
	نلاحظ بعد إزالة كل توابع الاستماع لم يُرسل أي بريد إلكتروني ولم تُحفظ أي بيانات في قاعدة البيانات.
</p>

<h2>
	ختامًا
</h2>

<p>
	تعلمنا في هذا المقال وظيفة مرسل الأحداث وطريقة إرسال الأحداث منه باستخدام التابع <code>‎emit()‎</code> الذي يوفره الصنف <code>‎EventEmitter‎</code>، ثم تعلمنا طرق الاستماع لتلك الأحداث باستخدام التابعين <code>‎on()‎</code> و <code>‎once()‎</code> لتنفيذ التعليمات البرمجية استجابة لإرسال حدث ما، وتعلمنا كيف يمكن معالجة أحداث الأخطاء، وكيفية مراقبة توابع الاستماع المسجلة باستخدام <code>‎listenerCount()‎</code>، وإدارتها باستخدام التابعين <code>‎off()‎</code> و <code>‎removeAllListeners()‎</code>.
</p>

<p>
	لو استخدمنا توابع رد النداء callbacks والوعود promises للاستجابة للأحداث ضمن نظام مدير البطاقات لكنا سنحتاج لربطه مع الوحدات البرمجية للخدمات الأخرى كخدمة البريد الإلكتروني وخدمة قاعدة البيانات، لكن بالاستفادة من مرسل الأحداث تمكنا من فصل تلك الوحدات البرمجية عن بعضها، ويمكن لأي وحدة برمجية جديدة قد نضيفها لاحقًا وتستطيع الوصول لمدير البطاقات أن تُربط معه وتستجيب للأحداث التي سيرسلها، وهي الفائدة التي يوفرها التعامل مع مرسل الأحداث فعند تطوير وحدة برمجية نرغب بربطها لاحقًا مع عدة وحدات برمجية أخرى أو مراقبتها يمكن أن نجعلها ترث صنف مرسل الأحداث الأساسي لتسهيل عملية الربط تلك.
</p>

<p>
	ويمكنك الرجوع إلى توثيق نود الرسمي العربي <a href="https://wiki.hsoub.com/Node.js/events" rel="external">لمرسل الأحداث</a> للتعرف عليه أكثر.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/using-event-emitters-in-node-js" rel="external nofollow">Using Event Emitters in Node.js</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA%D8%A9-buffers-%D9%81%D9%8A-nodejs-r1805/" rel="">استخدام المخازن المؤقتة Buffers في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1806</guid><pubDate>Sat, 03 Dec 2022 06:21:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x645;&#x62E;&#x627;&#x632;&#x646; &#x627;&#x644;&#x645;&#x624;&#x642;&#x62A;&#x629; Buffers &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA%D8%A9-buffers-%D9%81%D9%8A-nodejs-r1805/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_12/638ad7ededdff_----Node.png.bb05f94662a4e823e0844337f0bbcd08.png" /></p>

<p>
	يمثل المخزن المؤقت buffer مساحة ما في الذاكرة RAM تحتوي على البيانات بالصيغة الثنائية binary، ويمكن لنود <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">Node.js</a> أن تتعامل مع هذه الذاكرة باستخدام الصنف <a href="https://wiki.hsoub.com/Node.js/buffer" rel="external"><code>‎Buffer‎</code></a>، حيث يمثل البيانات كسلسلة من الأعداد بطريقة مشابهة لعمل المصفوفات في جافاسكربت، إلا أن الفرق أن هذه البيانات لا يمكن التعديل على حجمها بعد إنشاء المخزن، وكثيرًا ما نتعامل مع المخازن المؤقتة عند تطوير البرامج ضمن بيئة نود دون أن نشعر، فمثلًا عند قراءة ملف ما باستخدام التابع <a href="https://wiki.hsoub.com/Node.js/fs#fs.readFile.28path.5B.2C_options.5D.2C_callback.29" rel="external"><code>‎fs.readFile()‎</code></a> فسيمرر كائن من نوع مخزن مؤقت يحوي بيانات الملف الذي نحاول قراءته إلى تابع رد النداء callback أو كنتيجة للوعد Promise، وحتى عند إنشاء طلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> فالنتيجة هي مجرى stream من البيانات المخزنة مؤقتًا في مخزن مؤقت داخلي يساعد المستخدم على معالجة بيانات جواب الطلب على دفعات بدلًا من دفعة واحدة.
</p>

<p>
	ونستفيد من المخازن المؤقتة أيضًا عند التعامل مع البيانات الثنائية عند كتابة البرامج منخفضة المستوى مثل التي تتعامل مع إرسال واستقبال البيانات عبر الشبكة، كما توفر القدرة على التعامل مع البيانات على أخفض مستوى ممكن والتعديل عليها في الحالات التي نحتاج بها لذلك.
</p>

<p>
	سنتعرف في هذا الفصل على المخازن المؤقتة وطريقة إنشائها والقراءة والنسخ منها والكتابة إليها، وحتى تحويل البيانات الثنائية ضمنها إلى صيغ ترميز أخرى.
</p>

<h2>
	المستلزمات
</h2>

<p>
	هذا الفصل جزء من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">دليل تعلم Node.js</a> لذا يجب قبل قراءته:
</p>

<ul>
<li>
		تثبيت بيئة Node.js على الجهاز، حيث استخدمنا في هذا المقال الإصدار رقم 10.19.0.
	</li>
	<li>
		معرفة التعامل مع حلقة REPL في نود، يمكنك الاطلاع على <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B6%D8%B9-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D9%8A-repl-%D9%81%D9%8A-nodejs-r1712/" rel="">المقال الثاني</a> من هذه السلسلة للتعرف أكثر على طريقة استخدام هذا الوضع.
	</li>
	<li>
		معرفة <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">بأساسيات جافاسكربت</a> وأنواع البيانات المتوفرة ضمن اللغة.
	</li>
</ul>
<h2>
	إنشاء المخزن المؤقت
</h2>

<p>
	سنتعرف في هذه الفقرة على طريقتين لإنشاء كائن <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D9%84%D9%84%D9%88%D9%8A%D8%A8-web-caching-%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r204/" rel="">التخزين المؤقت</a> في نود، حيث يجب يجب أن نسأل أنفسنا دومًا في ما إذا كنا نريد إنشاء مخزن مؤقت جديد، أو استخراج مخزن مؤقت من بيانات موجودة مسبقًا، وعلى أساس ذلك سنحدد الطريقة المستخدمة لإنشائه، ففي حال أردنا تخزين بيانات غير موجودة ونتوقع أن تصل لاحقًا ففي تلك الحالة يجب إنشاء مخزن مؤقت جديد باستدعاء بالتابع <code>‎alloc()‎</code> من الصنف <code>‎Buffer‎</code>، ولنوضح هذه الطريقة نبدأ بفتح جلسة جديدة من وضع حلقة REPL بتنفيذ الأمر <code>‎node‎</code> في سطر الأوامر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_12" style="">
<span class="pln">$ node</span></pre>

<p>
	يظهر الرمز <code>‎&gt;‎</code> في بداية السطر، ما يدل على استعداد هذا الوضع لتلقي التعليمات البرمجية وتنفيذها، حيث يقبل التابع <code>‎alloc()‎</code> تمرير عدد كمعامل أول إجباري يشير إلى حجم المخزن المؤقت الذي نود إنشاءه، أي يمثل هذا المعامل عدد البايتات التي ستُحجز في الذاكرة للمخزن المؤقت الجديد، فمثلًا لإنشاء مخزن مؤقت بسعة 1 كيلوبايت أي ما يعادل 1024 بايت يمكننا استخدام التابع السابق كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_16" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> firstBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">alloc</span><span class="pun">(</span><span class="lit">1024</span><span class="pun">);</span></pre>

<p>
	نلاحظ أن الصنف <code>‎Buffer‎</code> متاح بشكل عام في بيئة نود، ومنه يمكننا الوصول مباشرة إلى التابع <code>‎alloc()‎</code> لاستخدامه، ونلاحظ كيف مررنا القيمة <code>‎1024‎</code> كمعامل أول له لينتج لدينا مخزن مؤقت بسعة 1 كيلوبايت، حيث ستحوي المساحة المحجوزة للمخزن المؤقت الجديد مؤقتًا على أصفار افتراضيًا، وذلك ريثما نكتب البيانات ضمنه لاحقًا، وبإمكاننا تخصيص ذلك فإذا أردنا أن تحتوي تلك المساحة على واحدات بدلًا من الأصفار يمكننا تمرير هذه القيمة كمعامل ثاني للتابع <code>‎alloc()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_18" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> filledBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">alloc</span><span class="pun">(</span><span class="lit">1024</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	ينتج لدينا مخزنًا مؤقتًا بمساحة 1 كيلوبايت من الذاكرة المملوءة بالواحدات، ويجب التأكيد أن البيانات التي يمثلها المخزن المؤقت ستكون بيانات ثنائية binary مهما كانت القيمة التي نحددها له كقيمة أولية، حيث يمكن تمثيل العديد من صيغ البيانات بواسطة البيانات الثنائية، فمثلًا البيانات الثنائية التالية تمثل حجم 1 بايت: <code>‎01110110‎</code>، ويمكن تفسيرها كنص بترميز ASCII باللغة الإنكليزية وبالتالي ستُعبّر عن الحرف <code>‎v‎</code>، ويمكن أيضًا تفسير هذه البيانات بسياق آخر وترميز مختلف على أنها لون لبكسل واحد من صورة ما، حيث يمكن للحاسوب التعامل مع هذه البيانات ومعالجتها بعد معرفة صيغة ترميزها.
</p>

<p>
	ويستخدم المخزن المؤقت في نود افتراضيًا ترميز <a href="https://ar.wikipedia.org/wiki/%D8%B5%D9%8A%D8%BA%D8%A9_%D8%A7%D9%84%D8%AA%D8%AD%D9%88%D9%8A%D9%84_%D8%A7%D9%84%D9%85%D9%88%D8%AD%D8%AF-8" rel="external nofollow">UTF-8</a> في حال كانت القيمة الأولية المخزنة ضمنه عند إنشاءه هي سلسلة نصية، حيث يمكن للبايت الواحد في ترميز UTF-8 أن يمثل حرفًا من أي لغة أو عددًا أو رمزًا ما، ويعتبر هذا الترميز توسعة لمعيار الترميز الأمريكي لتبادل البيانات أو <a href="https://ar.wikipedia.org/wiki/%D8%A3%D8%B3%D9%83%D9%8A" rel="external nofollow">ASCII</a> والذي يقتصر على ترميز الأحرف الإنكليزية الكبيرة والصغيرة والأعداد وبعض الرموز القليلة الأخرى فقط، كعلامة التعجب "!" وعلامة الضم "&amp;"، ويمكننا تحديد الترميز المستخدم من قبل المخزن المؤقت عبر تمريره كمعامل ثالث للتابع <code>‎alloc()‎</code>، فمثلًا لو اقتصرت حاجة برنامج ما على التعامل مع محارف بترميز ASCII يمكننا تحديده كترميز للبيانات ضمن المخزن المؤقت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_21" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> asciiBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">alloc</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ascii'</span><span class="pun">);</span></pre>

<p>
	نلاحظ تمرير المحرف <code>‎a‎</code> كمعامل ثانِ وبذلك سيتم تخزينه ضمن المساحة الأولية التي ستُحجز للمخزن المؤقت الجديد، ويدعم نود افتراضيًا صيغ ترميز المحارف التالية:
</p>

<ul>
<li>
		ترميز ASCII ويُمثّل بالسلسلة النصية <code>‎ascii‎</code>.
	</li>
	<li>
		ترميز UTF-8 ويُمثّل بالسلسلة النصية <code>‎utf-8‎</code> أو <code>‎utf8‎</code>.
	</li>
	<li>
		ترميز <a href="https://ar.wikipedia.org/wiki/%D9%8A%D9%88_%D8%AA%D9%8A_%D8%A7%D9%81-16" rel="external nofollow">UTF-16</a> ويُمثّل بالسلسلة النصية <code>‎utf-16le‎</code> أو <code>‎utf16le‎</code>.
	</li>
	<li>
		ترميز UCS-2 ويُمثّل بالسلسلة النصية <code>‎ucs-2‎</code> أو <code>‎ucs2‎</code>.
	</li>
	<li>
		ترميز <a href="https://ar.wikipedia.org/wiki/%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3_64" rel="external nofollow">Base64</a> ويُمثّل بالسلسلة النصية <code>‎base64‎</code>.
	</li>
	<li>
		الترميز الست عشري <a href="https://ar.wikipedia.org/wiki/%D9%86%D8%B8%D8%A7%D9%85_%D8%B9%D8%AF_%D8%B3%D8%AA%D8%A9_%D8%B9%D8%B4%D8%B1%D9%8A" rel="external nofollow">Hexadecimal</a> ويُمثّل بالسلسلة النصية <code>‎hex‎</code>.
	</li>
	<li>
		الترميز ISO/IEC 8859-1 ويُمثّل بالسلسلة النصية <code>‎latin1‎</code> أو <code>‎binary‎</code>.
	</li>
</ul>
<p>
	حيث يمكن استخدام أي من أنواع الترميز السابقة مع أي تابع من الصنف <code>‎Buffer‎</code> يقبل ضمن معاملاته معاملًا بالاسم <code>‎encoding‎</code> لتحديد صيغة الترميز، ومن ضمنها التابع <code>‎alloc()‎</code> الذي تعرفنا عليه.
</p>

<p>
	قد نحتاج أحيانًا لإنشاء مخزن مؤقت يُعبر عن بيانات جاهزة موجودة مسبقًا، كقيمة متغير أو سلسلة نصية أو مصفوفة، حيث يمكننا ذلك باستخدام التابع <code>‎from()‎</code> الذي يدعم إنشاء مخزن مؤقت جديد من عدة أنواع من البيانات وهي:
</p>

<ul>
<li>
		مصفوفة من الأعداد التي تتراوح قيمها بين <code>‎0‎</code> و <code>‎255‎</code>،حيث يمثل كل عدد منها قيمة بايت واحد.
	</li>
	<li>
		كائن من نوع <code>‎ArrayBuffer‎</code> والذي يخزن داخله حجمًا ثابتًا من البايتات.
	</li>
	<li>
		سلسلة نصية.
	</li>
	<li>
		مخزن مؤقت آخر.
	</li>
	<li>
		أي كائن جافاسكربت يملك الخاصية <code>‎Symbol.toPrimitive‎</code> التي تُعبر عن طريقة تحويل هذا الكائن إلى بيانات أولية، مثل القيم المنطقية <code>‎boolean‎</code> أو <code>‎null‎</code> أو <code>‎undefined‎</code> أو الأعداد <code>‎number‎</code> أو <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-strings-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r817/" rel="">السلاسل النصية ‎string‎</a> أو الرموز <code>‎symbol‎</code>.
	</li>
</ul>
<p>
	لنختبر الآن طريقة إنشاء مخزن مؤقت جديد من سلسلة نصية باستخدام التابع <code>from</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_24" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> stringBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="str">'My name is Hassan'</span><span class="pun">);</span></pre>

<p>
	ينتج بذلك لدينا كائن مخزن مؤقت جديد يحتوي على قيمة السلسلة النصية <code>‎My name is Hassan‎</code>، ويمكننا كما ذكرنا إنشاء مخزن مؤقت جديد من مخزن مؤقت آخر مثلًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_26" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> asciiCopy </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">asciiBuf</span><span class="pun">);</span></pre>

<p>
	ينتج بذلك لدينا المخزن المؤقت <code>‎asciiCopy‎</code> والذي هو نسخة مطابقة من المخزن الأول <code>‎asciiBuf‎</code>، وبذلك نكون قد تعرفنا على طرق إنشاء المخازن المؤقتة، وفي الفقرة التالية سنتعلم طرق قراءة البيانات منها.
</p>

<h2>
	القراءة من المخزن المؤقت
</h2>

<p>
	يوجد عدة طرق تمكننا من قراءة بيانات المخزن المؤقت، حيث يمكنن قراءة بايت واحد محدد فقط منه إذا أردنا، أو قراءة كل البيانات دفعة واحدة، ولقراءة بايت واحد فقط يمكن الوصول إليه عبر رقم ترتيب مكان هذا البايت ضمن المخزن المؤقت، حيث تُخزِّن المخازن المؤقتة البيانات بترتيب متتابع تمامًا كالمصفوفات، ويبدأ ترتيب أول مكان للبيانات داخلها من الصفر <code>‎0‎</code> تمامًا كالمصفوفات، ويمكن استخدام نفس صيغة الوصول إلى عناصر المصفوفة لقراءة البايتات بشكل مفرد من المخزن مؤقت.
</p>

<p>
	لنختبر ذلك نبدأ بإنشاء مخزن مؤقت جديد من سلسلة نصية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_29" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> hiBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="str">'Hi!'</span><span class="pun">);</span></pre>

<p>
	ونحاول قراءة أول بايت من هذا المخزن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_31" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span></pre>

<p>
	بعد الضغط على زر الإدخال ‎ENTER‎ وتنفيذ التعليمة السابقة سيظهر لنا النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_34" style="">
<span class="lit">72</span></pre>

<p>
	حيث يرمز العدد <code>‎72‎</code> ضمن ترميز UTF-8 للحرف <code>‎H‎</code> وهو أول حرف من السلسلة النصية المُخزنة، حيث تقع قيمة أي بايت ضمن المجال من صفر <code>‎0‎</code> إلى <code>‎255‎</code>، وذلك لأن البايت يتألف من 8 بتات أو bits، وكل بت بدوره يمثل إما صفر <code>‎0‎</code> أو واحد <code>‎1‎</code>، فأقصى قيمة يمكن تمثيلها بسلسلة من ثمانية بتات تساوي 2<sup>⁸</sup> وهو الحجم الأقصى للبايت الواحد، أي يمكن للبايت تمثيل قيمة من 256 قيمة ممكنة، وبما أن أول قيمة هي الصفر فأكبر عدد يمكن تمثيله في البايت الواحد هو 255، والآن لنحاول قراءة قيمة البايت الثاني ضمن المخزن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_36" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span></pre>

<p>
	سنلاحظ ظهور القيمة <code>‎105‎</code> والتي ترمز للحرف الصغير <code>‎i‎</code>، والآن نحاول قراءة آخر بايت من هذا المخزن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_38" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span></pre>

<p>
	نلاحظ ظهور القيمة <code>‎33‎</code> والتي ترمز إلى إشارة التعجب <code>‎!‎</code>، ولكن ماذا سيحدث لو حاولنا قراءة بايت غير موجود بتمرير قيمة لمكان خاطئ ضمن المخزن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_40" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">3</span><span class="pun">];</span></pre>

<p>
	سنلاحظ ظهور القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_42" style="">
<span class="kwd">undefined</span></pre>

<p>
	وهو نفس ما سيحدث لو حاولنا الوصول إلى عنصر غير موجود ضمن مصفوفة ما.
</p>

<p>
	والآن بعد أن تعرفنا على طريقة قراءة بايت واحد من البيانات ضمن المخزن مؤقت، سنتعرف على طريقة لقراءة كل البيانات المخزنة ضمنه دفعة واحدة.
</p>

<p>
	يوفر كائن المخزن مؤقت التابعين <code>‎toString()‎</code> و <code>‎toJSON()‎</code> والذي يعيد كل منهما البيانات الموجودة ضمن المخزن دفعة واحدة كل منهما بصيغة مختلفة، ونبدأ بالتابع <code>‎toString()‎</code> والذي يحول البايتات ضمن المخزن المؤقت إلى قيمة سلسلة نصية ويعيدها، لنختبر ذلك باستدعائه على المخزن المؤقت السابق <code>‎hiBuf‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_44" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	سنلاحظ ظهور القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_47" style="">
<span class="str">'Hi!'</span></pre>

<p>
	وهي قيمة السلسلة النصية التي خزناها ضمن المخزن المؤقت عند إنشاءه، ولكن ماذا سيحدث لو استدعينا التابع <code>‎toString()‎</code> على مخزن مؤقت تم إنشاءه من بيانات من نوع مختلف؟ لنختبر ذلك بإنشاء مخزن مؤقت جديد فارغ بحجم <code>‎10‎</code> بايت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_49" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> tenZeroes </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">alloc</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span></pre>

<p>
	ونستدعي التابع <code>‎toString()‎</code> ونلاحظ النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_51" style="">
<span class="pun">&gt;</span><span class="pln"> tenZeroes</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	سيظهر ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_53" style="">
<span class="str">'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'</span></pre>

<p>
	حيث تقابل السلسلة النصية <code>‎\u0000‎</code> المحرف في ترميز Unicode المقابل للقيمة <code>‎NULL‎</code>، وهو ما يقابل قيمة الصفر <code>‎0‎</code>، حيث يعيد التابع <code>‎toString()‎</code> ترميز UTF-8 للبايتات المخزنة في حال كانت البيانات ضمن المخزن المؤقت ليست من نوع سلسلة نصية، ويقبل التابع <code>‎toString()‎</code> معامل اختياري بالاسم <code>‎encoding‎</code> لتحديد ترميز البيانات المطلوب، حيث يمكن باستخدامه تعديل ترميز قيمة السلسلة النصية التي يعيدها التابع، فيمكن مثلًا قراءة نفس البيانات للمخزن <code>‎hiBuf‎</code> السابق لكن بالترميز الست عشري كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_55" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">(</span><span class="str">'hex'</span><span class="pun">);</span></pre>

<p>
	سنلاحظ ظهور النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_57" style="">
<span class="str">'486921'</span></pre>

<p>
	حيث تُعبر تلك القيمة عن الترميز الست عشري للبايتات التي تتألف منها السلسلة النصية <code>‎Hi!‎</code>.
</p>

<p>
	ويُستفاد في نود من تلك الطريقة لتحويل ترميز بيانات ما من شكل لآخر، بإنشاء مخزن مؤقت جديد يحوي قيمة السلسلة النصية المراد تحويلها ثم استدعاء التابع <code>‎toString()‎</code> مع تمرير الترميز الجديد المرغوب به.
</p>

<p>
	أما وفي المقابل يعيد التابع <code>‎toJSON()‎</code> البيانات ضمن المخزن المؤقت كأعداد تمثل قيم البايتات المخزنة مهما كان نوعها، والآن لنختبر ذلك على كل من المخزنين السابقين <code>‎hiBuf‎</code> و <code>‎tenZeroes‎</code> ونبدأ بإدخال التعلمية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_59" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toJSON</span><span class="pun">();</span></pre>

<p>
	سنلاحظ ظهور القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_61" style="">
<span class="pun">{</span><span class="pln"> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Buffer'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="lit">72</span><span class="pun">,</span><span class="pln"> </span><span class="lit">105</span><span class="pun">,</span><span class="pln"> </span><span class="lit">33</span><span class="pln"> </span><span class="pun">]</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	يحوي الكائن الناتج من استدعاء التابع <code>‎toJSON()‎</code> على خاصية النوع <code>‎type‎</code> بالقيمة نفسها دومًا وهي <code>‎Buffer‎</code>، حيث يُستفاد من هذه القيمة لتمييز نوع <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">كائن JSON</a> هذا عن الكائنات الأخرى، ويحتوي على خاصية البيانات <code>‎data‎</code> وهي مصفوفة من الأعداد التي تمثل البايتات المخزنة، ونلاحظ أنها تحتوي على القيم <code>‎72‎</code> و <code>‎105‎</code> و <code>‎33‎</code> بالترتيب وهي نفس القيم التي ظهرت لنا سابقًا عند محاولة قراءة البايتات المخزنة بشكل مفرد.
</p>

<p>
	والآن لنختبر استدعاء التابع <code>‎toJSON()‎</code> على المخزن الفارغ <code>‎tenZeroes‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_67" style="">
<span class="pun">&gt;</span><span class="pln"> tenZeroes</span><span class="pun">.</span><span class="pln">toJSON</span><span class="pun">();</span></pre>

<p>
	سنلاحظ ظهور النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_69" style="">
<span class="pun">{</span><span class="pln"> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Buffer'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
  </span><span class="pun">]</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	الخاصية <code>‎type‎</code> تحوي نفس القيمة السابقة، بينما البيانات في المصفوفة هي عشرة أصفار تمثل البايتات العشرة الفارغة التي يحويها المخزن المؤقت، وبذلك نكون قد تعلمنا طرق قراءة البيانات من المخازن المؤقتة، وفي الفقرة التالية سنتعلم طريقة التعديل على تلك البيانات ضمن المخزن المؤقت.
</p>

<h2>
	التعديل على المخزن المؤقت
</h2>

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

<p>
	ولنبدأ بالتعرف على الطريقة الأولى لذلك سنستخدم المخزن السابق <code>‎hiBuf‎</code> الذي يحتوي على قيمة السلسلة النصية <code>‎Hi!‎</code> داخله، ولنحاول تعديل محتوى كل بايت منه على حدى إلى أن تصبح القيمة الجديدة هي <code>‎Hey‎</code>، حيث نبدأ بتعديل الحرف الثاني من المخزن <code>‎hiBuf‎</code> إلى الحرف <code>‎e‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_71" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'e'</span><span class="pun">;</span></pre>

<p>
	نتأكد من صحة التعديل السابق بقراءة محتوى المخزن المؤقت الجديد باستدعاء التابع <code>‎toString()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_73" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نلاحظ ظهور القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_75" style="">
<span class="str">'H\u0000!'</span></pre>

<p>
	القيمة الغريبة التي ظهرت تدل على أن المخزن مؤقت يقبل فقط القيم العددية عند تخزينها داخله، لذا لا يمكن تمرير الحرف <code>‎e‎</code> كسلسلة نصية مباشرةً، بل يجب تمرير القيمة الثنائية المقابلة له كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_77" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">101</span><span class="pun">;</span></pre>

<p>
	الآن يمكننا معاينة القيمة الجديدة والتأكد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_79" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نحصل على القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_81" style="">
<span class="str">'He!'</span></pre>

<p>
	نعدل الحرف الأخير من هذه القيمة وهو العنصر الثالث ونضع القيمة الثنائية المقابلة للحرف <code>‎y‎</code> كالتالي:
</p>

<pre class="ipsCode">
&gt; hiBuf[2] = 121;
</pre>

<p>
	نتأكد من المحتوى بعد التعديل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_83" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نحصل على القيمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_85" style="">
<span class="str">'Hey'</span></pre>

<p>
	ماذا سيحدث لو حاولنا تعديل قيمة بايت يقع خارج مجال بيانات المخزن المؤقت؟ سنلاحظ تجاهل المخزن لتلك العملية وتبقى القيمة المخزنة ضمنه كما هي، لنختبر ذلك بكتابة الحرف <code>‎o‎</code> إلى المحرف الرابع الغير موجود ضمن المخزن السابق كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_87" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">[</span><span class="lit">3</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">111</span><span class="pun">;</span></pre>

<p>
	نعاين قيمة المخزن بعد ذلك التعديل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_90" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	ونلاحظ أن القيمة بقيت كما هي دون تعديل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_92" style="">
<span class="str">'Hey'</span></pre>

<p>
	الطريقة الأخرى للتعديل على محتوى المخزن تكون بكتابة عدة بايتات معًا باستخدام التابع <code>‎write()‎</code> الذي يقبل سلسلة نصية كمعامل له تعبر عن المحتوى الجديد للبيانات، لنختبر ذلك عبر تعديل محتوى المخزن <code>‎hiBuf‎</code> إلى محتواه السابق <code>‎Hi!‎</code> كالتالي:
</p>

<pre class="ipsCode">
&gt; hiBuf.write('Hi!');
</pre>

<p>
	نلاحظ أن تنفيذ التعليمة السابقة يعيد القيمة <code>‎3‎</code> وهي عدد البايتات التي تم تعديلها ضمن المخزن في تلك العملية، حيث يعبر كل بايت عن محرف واحد لأننا نستخدم الترميز UTF-8، وفي حال كان المخزن يستخدم ترميز آخر مثل UTF-16 ففيه يُمثَّل كل محرف على 2 بايت، عندها سيعيد تنفيذ تابع الكتابة <code>‎write()‎</code> بنفس الطريقة القيمة <code>‎6‎</code> للدلالة على عدد البايتات التي تمثل المحارف الثلاث المكتوبة.
</p>

<p>
	والآن لنتأكد من المحتوى الجديد بعد التعديل نستدعي<code>‎toString()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_94" style="">
<span class="pun">&gt;</span><span class="pln"> hiBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نحصل على القيمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_96" style="">
<span class="str">'Hi!'</span></pre>

<p>
	هذه الطريقة أسرع من طريقة تعديل كل بايت على حدى، ولكن ماذا سيحدث لو كتبنا بيانات بحجم أكبر من حجم المخزن الكلي؟ سيقبل المخزن البيانات المقابلة لحجمه فقط ويهمل البقية، لنختبر ذلك بإنشاء مخزن مؤقت بحجم 3 بايت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_98" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> petBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">alloc</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	ونحاول كتابة سلسلة نصية بأربعة محارف مثلًا <code>‎Cats‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_100" style="">
<span class="pun">&gt;</span><span class="pln"> petBuf</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Cats'</span><span class="pun">);</span></pre>

<p>
	نلاحظ أن ناتج التعليمة السابقة هي القيمة <code>‎3‎</code> أي تم تعديل قيمة ثلاث بايتات فقط وتجاهل باقي القيمة المُمررة، لنتأكد من القيمة الجديدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_104" style="">
<span class="pun">&gt;</span><span class="pln"> petBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نلاحظ القيمة الجديدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_102" style="">
<span class="str">'Cat'</span></pre>

<p>
	حيث يُعدل التابع <code>‎write()‎</code> البايتات بالترتيب فعدّل أول ثلاث بايتات فقط ضمن المخزن وتجاهل البقية.
</p>

<p>
	والآن لنختبر ماذا سيحدث لو كتبنا قيمة بحجم أقل من حجم المخزن الكلي، لهذا نُنشئ مخزن مؤقت جديد بحجم 4 بايت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_110" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> petBuf2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">alloc</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span></pre>

<p>
	ونكتب القيمة الأولية داخله كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_112" style="">
<span class="pun">&gt;</span><span class="pln"> petBuf2</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Cats'</span><span class="pun">);</span></pre>

<p>
	ثم نكتب قيمة جديدة حجمها أقل من حجم المخزن الكلي كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_114" style="">
<span class="pun">&gt;</span><span class="pln"> petBuf2</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Hi'</span><span class="pun">);</span></pre>

<p>
	وبما أن البيانات ستكتب بالترتيب بدئًا من أول بايت سنلاحظ نتيجة ذلك عند معاينة القيمة الجديدة للمخزن:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_116" style="">
<span class="pun">&gt;</span><span class="pln"> petBuf2</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	ليظهر القيمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_118" style="">
<span class="str">'Hits'</span></pre>

<p>
	تم تعديل قيمة أول بايتين فقط، وبقيت البايتات الأخرى كما هي دون تعديل.
</p>

<p>
	تكون البيانات التي نود كتابتها موجودة أحيانًا ضمن مخزن مؤقت آخر، حيث يمكننا في تلك الحالة نسخ محتوى ذلك المخزن باستدعاء التابع <code>‎copy()‎</code>، لنختبر ذلك بداية بإنشاء مخزنين جديدين كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_120" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> wordsBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="str">'Banana Nananana'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> catchphraseBuf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="str">'Not sure Turtle!'</span><span class="pun">);</span></pre>

<p>
	يحوي كل من المخزنين <code>‎wordsBuf‎</code> و <code>‎catchphraseBuf‎</code> على بيانات من نوع سلسلة نصية، فإذا أردنا تعديل قيمة المخزن <code>‎catchphraseBuf‎</code> ليحوي على القيمة <code>‎Nananana Turtle!‎</code> بدلًا من <code>‎Not sure Turtle!‎</code> يمكننا استدعاء تابع النسخ <code>‎copy()‎</code> لنسخ القيمة <code>‎Nananana‎</code> من المخزن <code>‎wordsBuf‎</code> إلى <code>‎catchphraseBuf‎</code>، حيث نستدعي التابع <code>‎copy()‎</code> على المخزن الحاوي على المعلومات المصدر لنسخها إلى مخزن آخر، ففي مثالنا النص الذي نريد نسخه موجود ضمن المخزن <code>‎wordsBuf‎</code>، لذا نستدعي تابع النسخ منه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_122" style="">
<span class="pun">&gt;</span><span class="pln"> wordsBuf</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">catchphraseBuf</span><span class="pun">);</span></pre>

<p>
	حيث يُعبّر معامل الوجهة <code>‎target‎</code> المُمرر له عن المخزن المؤقت الذي ستُنسخ البيانات إليه، ونلاحظ ظهور القيمة <code>‎15‎</code> كنتيجة لتنفيذ التعليمة السابقة وهي تعبر عن عدد البايتات التي تم كتابتها، ولكن بما أن النص <code>‎Nananana‎</code> مكوّن من ثمانية محارف فقط فهذا يدل على عمل مختلف نفذه تابع النسخ، لنحاول معرفة ماذا حدث ونعاين القيمة الجديدة باستخدام التابع <code>‎toString()‎</code> ونلاحظ النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_124" style="">
<span class="pun">&gt;</span><span class="pln"> catchphraseBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نلاحظ القيمة الجديدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_126" style="">
<span class="str">'Banana Nananana!'</span></pre>

<p>
	نلاحظ أن تابع النسخ <code>‎copy()‎</code> قد نسخ كامل المحتوى من المخزن <code>‎wordsBuf‎</code> وخزنه ضمن <code>‎catchphraseBuf‎</code>، ولكن ما نريده هو نسخ قسم من تلك البيانات فقط وهي القيمة <code>‎Nananana‎</code>، لنعيد القيمة السابقة للمخزن <code>‎catchphraseBuf‎</code> أولًا ثم نحاول تنفيذ المطلوب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_128" style="">
<span class="pun">&gt;</span><span class="pln"> catchphraseBuf</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Not sure Turtle!'</span><span class="pun">);</span></pre>

<p>
	يقبل التابع <code>‎copy()‎</code> عدة معاملات تمكننا من تحديد البيانات التي نرغب بنسخها إلى المخزن المؤقت الوجهة وهي:
</p>

<ul>
<li>
		الوجهة <code>‎target‎</code> وهو المعامل الإجباري الوحيد، ويعبر عن المخزن المؤقت الوجهة لنسخ البيانات.
	</li>
	<li>
		<code>‎targetStart‎</code> وهو ترتيب أول بايت ستبدأ كتابة البيانات إليه ضمن المخزن الوجهة، وقيمته الافتراضية هي الصفر <code>‎0‎</code>، أي بدء عملية الكتابة من أول بايت ضمن المخزن الوجهة.
	</li>
	<li>
		<code>‎sourceStart‎</code> وهو ترتيب أول بايت من البيانات التي نرغب بنسخها من المخزن المصدر.
	</li>
	<li>
		<code>‎sourceEnd‎</code> وهو ترتيب آخر بايت من البيانات الذي ستتوقف عملية النسخ عنده في المخزن المصدر، وقيمته الافتراضية هي الطول الكلي للبيانات ضمن المخزن المصدر.
	</li>
</ul>
<p>
	باستخدام تلك المعاملات يمكننا تحديد الجزء <code>‎Nananana‎</code> من المخزن <code>‎wordsBuf‎</code> ليُنسخ إلى المخزن <code>‎catchphraseBuf‎</code>، حيث نمرر المخزن <code>‎catchphraseBuf‎</code> كمعامل الوجهة <code>‎target‎</code> كما فعلنا سابقًا، ونمرر القيمة <code>‎0‎</code> للمعامل <code>‎targetStart‎</code> لكتابة القيمة <code>‎Nananana‎</code> في بداية المخزن <code>‎catchphraseBuf‎</code>، أما للقيمة <code>‎sourceStart‎</code> سنمرر <code>‎7‎</code> وهو ترتيب بداية أول محرف من القيمة <code>‎Nananana‎</code> ضمن المخزن <code>‎wordsBuf‎</code>، وللقيمة <code>‎sourceEnd‎</code> نمرر الحجم الكلي للمخزن المصدر، ليكون الشكل النهائي لاستدعاء تابع النسخ بعد تخصيص المعاملات السابقة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_130" style="">
<span class="pun">&gt;</span><span class="pln"> wordsBuf</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="pln">catchphraseBuf</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> wordsBuf</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span></pre>

<p>
	سيظهر هذه المرة القيمة <code>‎8‎</code> كنتيجة لتلك العملية ما يعني أن القيمة التي حددناها فقط هي ما تم نسخه، ونلاحظ كيف استخدمنا الخاصية <code>‎wordsBuf.length‎</code> لتمرير حجم المخزن كقيمة للمعامل <code>‎sourceEnd‎</code>، وهي نفس الخاصية <code>‎length‎</code> الموجودة ضمن المصفوفات، والآن لنعاين القيمة الجديدة للمخزن <code>‎catchphraseBuf‎</code> ونتأكد من النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_133" style="">
<span class="pun">&gt;</span><span class="pln"> catchphraseBuf</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">();</span></pre>

<p>
	نلاحظ القيمة الجديدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_135" style="">
<span class="str">'Nananana Turtle!'</span></pre>

<p>
	بذلك نكون قد عدلنا البيانات ضمن المخزن <code>‎catchphraseBuf‎</code> عن طريق نسخ جزء محدد من بيانات المخزن <code>‎wordsBuf‎</code> إليه.
</p>

<p>
	والآن بعد أن انتهينا من تنفيذ الأمثلة في هذا الفصل يمكنك الخروج من جلسة REPL حيث ستُحذف كل المتغيرات السابقة التي عرفناها بعد عملية الخروج هذه، ولذلك ننفذ أمر الخروج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1962_137" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">exit</span></pre>

<h2>
	ختامًا
</h2>

<p>
	تعرفنا في هذا المقال على المخازن المؤقتة والتي تمثل مساحة محددة من الذاكرة محجوزة لتخزين البيانات بالصيغة الثنائية، وتعلمنا طرق إنشاء المخازن المؤقتة، سواء الجديدة أو التي تحتوي على بيانات موجودة مسبقًا، وتعرفنا بعدها على طرق قراءة تلك البيانات من المخزن سواء بقراءة كل بايت منه على حدى أو قراءة المحتوى كاملًا باستخدام التابعين <code>‎toString()‎</code> و <code>‎toJSON()‎</code>، ثم تعرفنا على طرق الكتابة إلى المخازن لتعديل البيانات المخزنة ضمنها، سواء بكتابة كل بايت على حدى أو باستخدام التابعين <code>‎write()‎</code> و <code>‎copy()‎</code>.
</p>

<p>
	يفتح التعامل مع المخازن المؤقتة في نود Node.js الباب للتعامل مع البيانات الثنائية مباشرة، فيمكن مثلًا دراسة تأثير صيغ الترميز المختلفة للمحارف على البيانات المخزنة، كمقارنة صيغ الترميز المختلفة مع الصيغتين UTF-8 و ASCII وملاحظة فرق الحجم بينها، كما يمكن مثلًا تحويل البيانات المخزنة من صيغة UTF-8 إلى صيغ الترميز الأخرى، ويمكنك الرجوع إلى التوثيق الرسمي العربي من نود للكائن <a href="https://wiki.hsoub.com/Node.js/buffer" rel="external"><code>‎Buffer‎</code></a> للتعرف عليه أكثر.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/using-buffers-in-node-js" rel="external nofollow">Using Buffers in Node.js</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-http-r1745/" rel="">إنشاء خادم ويب في Node.js باستخدام الوحدة HTTP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D9%84%D9%84%D9%88%D9%8A%D8%A8-web-caching-%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r204/" rel="">أساسيات التخزين المؤقت للويب Web Caching: المصطلحات الأساسية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D9%84%D9%84%D9%88%D9%8A%D8%A8-web-caching-%D8%AA%D8%B1%D9%88%D9%8A%D8%B3%D8%A7%D8%AA-http-%D9%88%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-r205/" rel="">أساسيات التخزين المؤقت للويب Web Caching: ترويسات HTTP واستراتيجيات التخزين المؤقت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1805</guid><pubDate>Fri, 02 Dec 2022 16:00:00 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x62E;&#x627;&#x62F;&#x645; &#x648;&#x64A;&#x628; &#x641;&#x64A; Node.js &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x629; HTTP</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-http-r1745/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/63411f593a866_----Node--js---HTTP.png.8bb92cbc2ae02dee6e93799f3c822d7c.png" /></p>

<p>
	يرسل المتصفح عند استعراضك لصفحة ويب ما طلبًا إلى جهاز حاسوب آخر عبر الإنترنت وهو بدوره يرسل الصفحة المطلوبة كجواب لذلك الطلب، حيث ندعو جهاز الحاسوب الذي أُرسل إليه ذلك الطلب بخادم الويب web server، ووظيفته تلقي <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">طلبات HTTP</a> القادمة من العملاء كمتصفحات الويب، ويرسل بالمقابل رد HTTP يحتوي على صفحة <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">HTML</a> أو بيانات بصيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> في حال كان دور الخادم تمثيل <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a>، ولإرسال هذه البيانات ومعالجة الطلبات يحتاج خادم الويب لعدة برمجيات تقسم إلى صنفين أساسيين هما شيفرات الواجهات الأمامية Front-end code وهدفها عرض المحتوى المرئي للعميل مثل المحتوى وتنسيق الصفحة من ألوان مستخدمة أو خطوط، والواجهات الخلفية Back-end code وهدفها تحديد طرق تبادل البيانات ومعالجة الطلبات القادمة من المتصفح وتخزينها بالاتصال بقاعدة البيانات، والعديد من العمليات الأخرى.
</p>

<p>
	تتيح لنا بيئة <a href="https://wiki.hsoub.com/Node.js/synopsis" rel="external">نود Node.js</a> كتابة شيفرات الواجهات الخلفية باستخدام لغة جافاسكربت، والتي كان سابقًا استخدامها محصورًا على تطوير الواجهات الأمامية فقط، وسهل استعمال بيئة نود استخدام لغة جافاسكربت لتطوير الواجهات الأمامية والخلفية معًا عملية تطوير خوادم الويب بدلًا من استعمال لغات أخرى لتطوير الواجهات الخلفية مثل <a href="https://wiki.hsoub.com/PHP" rel="external">لغة PHP</a>، وهو السبب الأساسي في شهرة نود واستخدامها الواسع لتطوير شيفرات الواجهات الخلفية.
</p>

<p>
	سنتعلم في هذا المقال كيف نبني خادم ويب بالاستعانة بالوحدة البرمجية <a href="https://wiki.hsoub.com/Node.js/http" rel="external">http</a> التي توفرها نود يمكنه إعادة صفحات الويب <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">بلغة HTML</a> والبيانات بصيغة JSON وحتى ملفات البيانات بصيغة CSV.
</p>

<h2>
	المستلزمات
</h2>

<ul>
<li>
		بيئة Node.js مثبتة على الجهاز حيث استخدمنا في هذا المقال الإصدار رقم 10.19.0.
	</li>
	<li>
		معرفة بأساسيات البرمجة ضمن بيئة نود، ويمكنك التعرف على ذلك أكثر بمراجعة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%88%D9%84-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D9%88%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87-r1711/" rel="">المقال الأول</a> من هذه السلسلة.
	</li>
	<li>
		سنستخدم البرمجة اللامتزامنة في إحدى الفقرات من هذا المقال، لذلك يمكنك التعرف على هذه الطريقة أكثر وعلى طريقة استعمال الوحدة <code>fs</code> للتعامل مع الملفات بمراجعة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%BA%D9%8A%D8%B1-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-nodejs-r1743/" rel="">المقال الخامس</a> من هذه السلسلة.
	</li>
</ul>
<h2>
	إنشاء خادم HTTP بسيط في Node.js
</h2>

<p>
	سنبدأ بإنشاء خادم ويب يعيد للمستخدم نصًا بسيطًا، لنتعلم بذلك أساسيات إعداد الخادم والتي سنعتمد عليها لتطوير خوادم أخرى ستعيد البيانات بصيغ متقدمة مثل صيغة JSON.
</p>

<p>
	نبدأ بإعداد البيئة البرمجية لتنفيذ التمارين ضمن هذا المقال فنُنشئ مجلدًا جديدًا بالاسم first-servers ثم ننتقل إليه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_14" style="">
<span class="pln">mkdir first</span><span class="pun">-</span><span class="pln">servers
cd first</span><span class="pun">-</span><span class="pln">servers</span></pre>

<p>
	ونُنشئ الملف الرئيسي لشيفرة الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_16" style="">
<span class="pln">touch hello</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نفتح الملف ضمن أي محرر نصوص سنستخدم في هذا المقال محرر نانو nano:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_18" style="">
<span class="pln">nano hello</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نضيف إلى الملف السطر التالي لاستيراد الوحدة البرمجية <code>http</code> التي يوفرها نود افتراضيًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_20" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span></pre>

<p>
	تحوي وحدة <code>http</code> توابع لإنشاء <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">الخادم</a> سنستخدمها لاحقًا، ويمكنك التعرف أكثر على الوحدات البرمجية بمراجعة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-modules-%D9%81%D9%8A-nodejs-r1742/" rel="">المقال الرابع</a> من هذه السلسلة.
</p>

<p>
	والآن لنعرف ثابتين الأول هو اسم المضيف والثاني هو رقم المنفذ الذي سيستمع إليه الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_23" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> host </span><span class="pun">=</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">;</span></pre>

<p>
	كما ذكرنا سابقًا يستقبل الخادم الطلبات المرسلة إليه من متصفح العميل، ويمكن الوصول للخادم عبر عنوانه بإدخال اسم النطاق له والذي سيترجم لاحقًا إلى عنوان IP من قبل <a href="https://academy.hsoub.com/devops/servers/%d9%85%d9%82%d8%af%d9%91%d9%85%d8%a9-%d8%a5%d9%84%d9%89-%d9%85%d9%8f%d8%b5%d8%b7%d9%8e%d9%84%d8%ad%d8%a7%d8%aa-%d9%88%d8%b9%d9%86%d8%a7%d8%b5%d8%b1-%d9%88%d9%85%d9%81%d8%a7%d9%87%d9%8a%d9%85-%d9%86%d8%b8%d8%a7%d9%85-%d8%a3%d8%b3%d9%85%d8%a7%d8%a1-%d8%a7%d9%84%d9%86%d8%b7%d8%a7%d9%82%d8%a7%d8%aa-r5/" rel="">خادم DNS</a>، ويتألف هذا العنوان من عدة أرقام متتالية مميزة لكل جهاز ضمن الشبكة مثل شبكة الإنترنت، واسم النطاق <code>localhost</code> هو عنوان خاص يشير به جهاز حاسوب إلى نفسه ويقابله عنوان IP التالي <code>127.0.0.1</code>، وهو متاح فقط ضمن جهاز الحاسوب المحلي وليس متاحًا على أي شبكة موصول بها بما فيها <a href="https://academy.hsoub.com/devops/networking/%D8%A2%D9%84%D9%8A%D8%A9-%D8%B9%D9%85%D9%84-%D8%B4%D8%A8%D9%83%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r571/" rel="">شبكة الإنترنت</a>.
</p>

<p>
	ويعبر رقم المنفذ port عن بوابة مميزة على الجهاز صاحب عنوان IP المحدد، حيث سنستخدم في حالتنا المنفذ رقم <code>8000</code> على الجهاز المحلي لخادم الويب، ويمكن استخدام أي رقم منفذ آخر غير محجوز، ولكن عادة ما نعتمد المنفذ رقم <code>8080</code> أو <code>8000</code> خلال مرحلة التطوير لخوادم HTTP، وبعد ربط الخادم على اسم المضيف ورقم المنفذ المحددين سنتمكن من الوصول إليه من المتصفح المحلي عبر العنوان http://localhost:8000.
</p>

<p>
	والآن لنضيف دالة مهمتها معالجة طلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> الواردة وإرسال رد HTTP المناسب لها، حيث تستقبل الدالة معاملين الأول <code>req</code> وهو كائن يمثل الطلب الوارد ويحوي البيانات الواردة ضمن طلب HTTP، والثاني <code>res</code> وهو كائن يحوي توابع مفيدة لبناء الرد المراد إرساله للعميل، حيث نستخدمه لإرسال رد HTTP من الخادم، وسنعيد بدايةً الرسالة "My first server!‎" لكل الطلبات الواردة إلى الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_32" style="">
<span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="str">"My first server!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يفضل إعطاء الدوال اسمًا واضحًا يدل على وظيفتها، فمثلًا إذا كان تابع الاستماع للطلب يعيد قائمة من الكتب المتوفرة فيفضل تسميته <code>listBooks()‎</code>، لكن في حالتنا وبما أننا نختبر ونتعلم فيمكننا تسميته بالاسم <code>requestListener</code> أي المستمع للطلب.
</p>

<p>
	تستقبل توابع الاستماع للطلبات request listener functions كائنين كمعاملات لها نسميهما عادةً <code>req</code> و <code>res</code>، حيث يُغلَّف طلب HTTP الوارد من المستخدم ضمن كائن الطلب في أول معامل <code>req</code>، ونبني الرد على ذلك الطلب بالاستعانة بكائن الرد في المعامل الثاني <code>res</code>.
</p>

<p>
	يعيِّن السطر الأول من تابع الاستماع السابق <code>res.writeHead(200);‎</code> رمز الحالة لرد HTTP الذي سنرسله، والذي يحدد حالة معالجة الطلب من قبل الخادم، ففي حالتنا وبما أن الطلب سينجح ويكون صحيح دومًا نعين للرد رمز الحالة <code>200</code> والذي يعني إتمام الطلب بنجاح أو <code>"OK"</code>، وانظر مقال <a href="https://academy.hsoub.com/programming/general/%D8%B1%D9%85%D9%88%D8%B2-%D8%A7%D9%84%D8%A5%D8%AC%D8%A7%D8%A8%D8%A9-%D9%81%D9%8A-http-r75/" rel="">رموز الإجابة في HTTP</a> للتعرف على أهم رموز الإجابة في طلبات HTTP.
</p>

<p>
	أما السطر الثاني من التابع <code>res.end("My first server!");‎</code> فيرسل الرد للعميل الذي أرسل الطلب، ويمكن باستخدام ذلك التابع إرسال البيانات التي يجب أن يرسلها الخادم ضمن الرد وفي حالتنا هي إرسال نص بسيط.
</p>

<p>
	والآن أصبحنا جاهزين لإنشاء الخادم والاستفادة من تابع الاستماع السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_34" style="">
<span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">requestListener</span><span class="pun">);</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> host</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//${host}:${port}`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نحفظ الملف ونخرج منه، وفي حال كنت تستخدم محرر النصوص <code>nano</code> يمكنك الخروج بالضغط على الاختصار CTRL+X.
</p>

<p>
	في الشيفرة السابقة، أنشأنا في أول سطر كائن الخادم <code>server</code> باستخدام التابع <code>createServer()‎</code> من الوحدة <code>http</code>، وظيفته استقبال طلبات HTTP وتمريرها إلى تابع الاستماع <code>requestListener()‎</code>، وبعدها نربط الخادم إلى عنوان الشبكة الذي سيستمع إليه باستخدام التابع <code>server.listen()‎</code> ويمكن أن نمرر له رقم المنفذ <code>port</code> كمعامل أول، وعنوان الشبكة <code>host</code> كمعامل ثانِ، وفي النهاية دالة رد نداء callback تُستدعى عند بدء الاستماع من قبل الخادم للطلبات الواردة، وكل تلك المعاملات اختيارية لكن يفضل تمريرها وتحديد قيمها ليتضح عند قراءة الشيفرة على أي منفذ وعنوان سيستمع الخادم، ومن الضروري معرفة هذه الإعدادات للخادم عند نشر خادم الويب في بعض البيئات، خاصة التي تحتاج لإعداد <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D8%AD%D9%90%D9%85%D9%84-load-balancing-r319/" rel="">موزع الحمل load balancing</a> وإعداد الأسماء في خدمة DNS، ومهمة دالة رد النداء التي مررناها هناك طباعة رسالة إلى الطرفية تبين أن الخادم بدأ الاستماع مع توضيح عنوان الوصول إليه.
</p>

<p>
	يجب الملاحظة أنه حتى ولو لم نكن بحاجة لاستخدام كائن الطلب <code>req</code> ضمن تابع الاستماع، فمن الضروري تمريره كمعامل أول حتى نتمكن من الوصول لكائن الرد <code>res</code> كمعامل ثانِ بشكل صحيح.
</p>

<p>
	رأينا مما سبق سهولة إنشاء خادم ويب في نود حيث استطعنا بأقل من 15 سطرًا تجهيز خادم الويب، والآن لنشغله ونرى كيف يعمل بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_36" style="">
<span class="pln">node hello</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سيظهر لنا الخرج التالي ضمن الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_38" style="">
<span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	نلاحظ أن سطر الأوامر خرج من وضع الإدخال الافتراضي، لأن خادم الويب يعمل ضمن إجرائية طويلة لا تنتهي ليتمكن من الاستماع إلى الطلبات الواردة إليه في أي وقت، أما عند حدوث خطأ ما أو في حال أوقفنا الخادم يدويًا سيتم بذلك الخروج من تلك الإجرائية، لهذا السبب يجب اختبار الخادم من طرفية أخرى جديدة عبر التواصل معه باستخدام أداة تتيح إرسال واستقبال البيانات عبر الشبكة مثل <a href="https://github.com/curl/curl" rel="external nofollow">cURL</a>، وباستخدامها ننفذ الأمر التالي لإرسال طلب HTTP من نوع GET لخادم الويب السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_40" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	بعد تنفيذ الأمر سيظهر لنا رد الخادم ضمن الخرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_42" style="">
<span class="typ">My</span><span class="pln"> first server</span><span class="pun">!</span></pre>

<p>
	نلاحظ ظهور الرد من طرف الخادم، ونكون بذلك قد أعددنا خادم الويب واختبرنا إرسال طلب إليه واستقبال الرد منه بنجاح، لكن لنفصّل أكثر في تلك عملية ونفهم ما حدث.
</p>

<p>
	عند إرسال طلب الاختبار إلى الخادم أرسلت الأداة cURL طلب HTTP من النوع GET إلى الخادم على العنوان http://localhost:8000، ثم استقبل خادم الويب الذي أنشأناه ذلك الطلب من العنوان الذي يستمع عليه ومرره إلى تابع الاستماع ومعالجة الطلبات المحدد <code>requestListener()‎</code>، وهو بدوره عيّن رمز الحالة بالرقم <code>200</code> وأرسل البيانات النصية ضمن الرد، ثم أرسل الخادم بعدها الرد إلى صاحب الطلب وهو الأداة cURL، والتي بدورها عرضت محتواه على الطرفية.
</p>

<p>
	نوقف الخادم الآن بالضغط على الاختصار CTRL+C ضمن الطرفية الخاصة به لإيقاف الإجرائية التي يعمل ضمنها ونعود بذلك إلى سطر الأوامر بحالته الافتراضية لاستقبال كتابة الأوامر وتنفيذها، ولكن ما طورناه يختلف عن خوادم الويب للمواقع التي نزورها عادة أو الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> التي نتعامل معها، فهي لا ترسل نصًا بسيطًا فحسب بل إما ترسل لنا صفحات مكتوبة بلغة HTML أو بيانات بصيغة JSON، لذلك في سنتعلم الفقرة التالية كيف يمكننا الرد ببيانات مكتوبة بتلك الصيغ الشائع استخدامها على شبكة الويب.
</p>

<h2>
	الرد بعدة أنواع من البيانات
</h2>

<p>
	يمكن لخادم الويب إرسال البيانات للعميل ضمن الرد بعدة صيغ منها HTML و JSON وحتى XML وصيغة CSV، كما يمكن للخوادم إرسال بيانات غير نصية مثل مستندات PDF أو الملفات المضغوطة وحتى الصوت أو الفيديو.
</p>

<p>
	سنتعلم في هذه الفقرة كيف نرسل بعض الأنواع من تلك البيانات وهي JSON و CSV وصفحات HTML وهي صيغ البيانات النصية الشائع استخدامها في الويب، حيث توفر العديد من الأدوات ولغات البرمجة دعمًا واسعًا لإرسال تلك الأنواع من البيانات ضمن ردود HTTP، فمثلًا يمكن إرسالها في نود باتباع الخطوات التالية:
</p>

<ol>
<li>
		تعيين قيمة لترويسة <code>Content-Type</code> للرد في HTTP بقيمة تناسب نوع المحتوى المُرسل.
	</li>
	<li>
		تمرير البيانات بالصيغة الصحيحة للتابع <code>res.end()‎</code> لإرسالها.
	</li>
</ol>
<p>
	سنطبق ذلك في عدة أمثلة لاحقة، حيث ستتشارك كل تلك الأمثلة في نفس طريقة إعداد الخادم كما فعلنا في الفقرة السابقة، والاختلاف بينها سيكون ضمن تابع معالجة الطلب فقط <code>requestListener()‎</code>، لذلك سنحضر ملفات تلك الأمثلة باستخدام قالب موحد لها جميعًا سنكتبه في البداية، لهذا نبدأ بإنشاء ملف جافاسكربت جديد بالاسم html.js سيحتوي على مثال إرسال الخادم لبيانات بصيغة HTML.
</p>

<p>
	نبدأ بكتابة الشيفرات المشتركة بين جميع الأمثلة ضمنه ثم ننسخ الملف إلى عدة نسخ لتجهيز ملفات الأمثلة الباقية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_44" style="">
<span class="pln">touch html</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نفتح الملف ضمن أي محرر نصوص:
</p>

<pre class="ipsCode">
nano html.js
</pre>

<p>
	ونضع داخله محتوى القالب لجميع الأمثلة اللاحقة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_47" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> host </span><span class="pun">=</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">8000</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">requestListener</span><span class="pun">);</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> host</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//${host}:${port}`);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نحفظ الملف ونخرج منه وننسخه إلى ملفين جديدين الأول لمثال إرسال البيانات بصيغة CSV ضمن الرد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_49" style="">
<span class="pln">cp html</span><span class="pun">.</span><span class="pln">js csv</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	والآخر لإرسال البيانات بصيغة JSON:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_51" style="">
<span class="pln">cp html</span><span class="pun">.</span><span class="pln">js json</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونحضر الملفات التالية أيضًا والتي سنستخدمها للأمثلة في الفقرة اللاحقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_53" style="">
<span class="pln">cp html</span><span class="pun">.</span><span class="pln">js htmlFile</span><span class="pun">.</span><span class="pln">js
cp html</span><span class="pun">.</span><span class="pln">js routes</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	بذلك نكون قد جهزنا جميع ملفات الأمثلة وبإمكاننا البدء بتضمينها، وسنبدأ في أول مثال بالتعرف على طريقة إرسال البيانات بصيغة JSON.
</p>

<h3>
	إرسال البيانات بصيغة JSON
</h3>

<p>
	صيغة ترميز كائنات جافاسكربت objects أو ما يعرف بصيغة JSON هي صيغة نصية لتبادل البيانات، وكما يشير اسمها فهي مشتقة من كائنات جافاسكربت ولكن يمكن التعامل معها من أي لغة برمجة أخرى تدعمها وقادرة على تحليل صيغتها، وهي تستخدم عادة في عمليات إرسال واستقبال البيانات من <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهات البرمجية للتطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a>، ومن أسباب انتشارها صغر حجم البيانات عند إرسالها بهذه الصيغة مقارنة بالصيغ الأخرى مثل XML مثلًا، ومما يساعد في التعامل معها بكل سهولة هو توفر الأدوات لقراءة وتحليل هذه الصيغة.
</p>

<p>
	والآن نفتح ملف المثال json.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_56" style="">
<span class="pln">nano json</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	وبما أننا نريد إرسال البيانات بصيغة JSON لنعدل تابع معالجة الطلب <code>requestListener()‎</code> ليعين قيمة الترويسة المناسبة لردود JSON كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_58" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	يضيف التابع <code>res.setHeader()‎</code> ترويسة HTTP إلى الرد توفر معلومات إضافية عن الطلب أو الرد المرسل، حيث يمرر له معاملين هما اسم الترويسة وقيمتها، حيث تصف قيمة الترويسة <code>Content-Type</code> صيغة البيانات أو نوع الوسائط media type المرفقة ضمن جسم الطلب، وفي حالتنا يجب تعيين قيمة الترويسة إلى <code>application/json</code>، ثم نعيد بعدها البيانات بصيغة JSON إلى المستخدم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_60" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(`{</span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"This is a JSON response"</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ضبطنا كما المثال السابق رمز الرد إلى القيمة <code>200</code> للدلالة على نجاح العملية، والفرق هنا أننا مررنا لتابع إرسال البيانات ضمن الرد <code>response.end()‎</code> سلسلة نصية تحوي بيانات بصيغة JSON.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونشغل الخادم بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_62" style="">
<span class="pln">node json</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونفتح طرفية أخرى لتجربة إرسال طلب إلى الخادم باستخدام الأداة cURL كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_64" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	بعد إرسال الطلب والضغط على زر الإدخال ENTER نحصل على النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_66" style="">
<span class="pun">{</span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"This is a JSON response"</span><span class="pun">}</span></pre>

<p>
	نكون بذلك قد تعلمنا كيف يمكن إرسال رد يحوي بيانات بصيغة JSON مثل ما تفعل الواجهات البرمجية للتطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> تمامًا.
</p>

<p>
	وبعد الاختبار نوقف الخادم بالضغط على الاختصار CTRL+C لنعود إلى سطر الأوامر مجددًا، حيث سنتعلم في الفقرة التالية كيف يمكن إرسال البيانات بصيغة CSV هذه المرة.
</p>

<h3>
	إرسال البيانات بصيغة CSV
</h3>

<p>
	شاع استخدام صيغة القيم المفصولة بفاصلة أو CSV عند التعامل مع البيانات المجدولة ضمن جداول، حيث يُفصل بين السجلات ضمن الجدول سطر جديد، وبين القيم على نفس السطر بفاصلة.
</p>

<p>
	والآن نفتح ملف المثال csv.js ضمن محرر النصوص ونعدل طريقة إرسال الطلب ضمن التابع <code>requestListener()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_68" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/csv"</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Disposition"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"attachment;filename=oceanpals.csv"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نلاحظ كيف حددنا قيمة الترويسة <code>Content-Type</code> هذه المرة بالقيمة <code>text/csv</code> والتي تدل على أن البيانات المرسلة مكتوبة بصيغة CSV، وأضفنا هذه المرة ترويسة جديدة بالاسم <code>Content-Disposition</code> لتدل المتصفح على طريقة عرض البيانات المرسلة إليه، فإما أن تبقى ضمن المتصفح نفسه أو يتم حفظها في ملف خارجي، وحتى لو لم نعين قيمة للترويسة <code>Content-Disposition</code> فمعظم المتصفحات الحديثة ستُنزِّل البيانات وتحفظها ضمن ملف تلقائيًا في حال كانت بصيغة CSV، ويسمح تعيين قيمة لهذه الترويسة بتحديد اسم للملف الذي سيتم حفظه، والقيمة التي عيناها تخبر المتصفح أن البيانات المرسلة هي ملف مرفق بصيغة CSV يجب تنزيله وحفظه بالاسم oceanpals.csv.
</p>

<p>
	والآن لنرسل بيانات CSV ضمن الرد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_70" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/csv"</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Disposition"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"attachment;filename=oceanpals.csv"</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(`</span><span class="pln">id</span><span class="pun">,</span><span class="pln">name</span><span class="pun">,</span><span class="pln">email\n</span><span class="lit">1</span><span class="pun">,</span><span class="typ">Hassan</span><span class="pln"> </span><span class="typ">Shark</span><span class="pun">,</span><span class="pln">shark@ocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	حددنا كما العادة رمز الحالة <code>200</code> ضمن الرد للدلالة على نجاح العملية، ومررنا سلسلة نصية تحوي على بيانات بصيغة CSV إلى تابع إرسال البيانات <code>res.end()‎</code>، ونلاحظ كيف يفصل بين تلك القيم فواصل، وبين أسطر الجدول محرف <code>‎\n</code> الذي يدل على سطر جديد، والبيانات التي أرسلناها تحوي سطران الأول فيه ترويسات الجدول والثاني يحوي البيانات الموافقة لها.
</p>

<p>
	والآن لنختبر عمل الخادم لذا نحفظ الملف ونخرج منه وننفذ أمر تشغيل الخادم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_72" style="">
<span class="pln">node csv</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونفتح طرفية أخرى لتجربة إرسال طلب إلى الخادم باستخدام الأداة cURL كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_74" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	يظهر لنا الرد التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_76" style="">
<span class="pln">id</span><span class="pun">,</span><span class="pln">name</span><span class="pun">,</span><span class="pln">email
</span><span class="lit">1</span><span class="pun">,</span><span class="typ">Hassan</span><span class="pln"> </span><span class="typ">Shark</span><span class="pun">,</span><span class="pln">shark@ocean</span><span class="pun">.</span><span class="pln">com</span></pre>

<p>
	إذا حاولنا الوصول للخادم من المتصفح عن طريق العنوان <code><a href="http://localhost:8000" ipsnoembed="false" rel="external nofollow">http://localhost:8000</a></code> نلاحظ كيف سيتم تنزيل ملف CSV المرسل وسيحدد تلقائيًا الاسم oceanpals.csv له.
</p>

<p>
	نوقف الخادم الآن لنعود إلى سطر الأوامر مجددًا.
</p>

<p>
	والآن بعد أن تعرفنا على طريقة إرسال البيانات بالصيغ JSON و CSV وهي أشيع الصيغ المستخدمة عند تطوير الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>، سنتعرف في الفقرة التالية على طريقة إرسال البيانات بحيث يمكن للمستخدم استعراضها ضمن المتصفح مباشرة.
</p>

<h3>
	إرسال البيانات بصيغة HTML
</h3>

<p>
	تعد لغة ترميز النصوص الفائقة HTML صيغة لترميز صفحات الويب والتي تتيح للمستخدم التفاعل مع الخادم مباشرةً من داخل المتصفح، ووظيفتها توصيف بنية محتوى الويب حيث تعتمد المتصفحات في عرضها لصفحات الويب على <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">لغة HTML</a> وعلى تنسيقها باستخدام CSS وهي تقنية أخرى من تقنيات الويب وظيفتها تجميل الصفحات وضبط طريقة عرضها.
</p>

<p>
	والآن نفتح ملف المثال لهذه الفقرة html.js ضمن محرر النصوص ونعدل طريقة إرسال الرد ضمن التابع <code>requestListener()‎</code> بداية بتعيين قيمة مناسبة للترويسة <code>Content-Type</code> لتدل على صيغة HTML كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_78" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ونعيد بعدها البيانات بصيغة HTML إلى المستخدم بإضافة التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_80" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(`</span><span class="typ">This</span><span class="pln"> is HTML</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	كما العادة ضبطنا بداية رمز الحالة لرد HTTP، ثم أرسلنا بيانات بصيغة HTML بتمريرها كسلسلة نصية للتابع <code>response.end()‎</code>، وإذا اختبرنا الاتصال بالخادم عبر المتصفح ستظهر لنا صفحة HTML تحتوي على ترويسة بالنص "This is HTML".
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونشغل الخادم لاختبار ذلك بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_82" style="">
<span class="pln">node html</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نطلب بعد تشغيل الخادم عنوانه من المتصفح <a href="http://localhost:8000" ipsnoembed="false" rel="external nofollow">http://localhost:8000</a> لتظهر لنا الصفحة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="109321" href="https://academy.hsoub.com/uploads/monthly_2022_10/html-response.png.841c15eb114aeb0afced034b9e138d1c.png" rel=""><img alt="html-response" class="ipsImage ipsImage_thumbnailed" data-fileid="109321" data-unique="keq7sqz6d" src="https://academy.hsoub.com/uploads/monthly_2022_10/html-response.thumb.png.590a27d629ccac5c051125c497e3d823.png" style="width: 780px; height: auto;"></a>
</p>

<p>
	نوقف الخادم لنعود إلى سطر الأوامر مجددًا، وبذلك نكون تعلمنا طريقة إرسال صفحة HTML عبر كتابة محتواها يدويًا ضمن سلسلة نصية، ولكن عادة نخزن محتوى تلك الصفحات ضمن ملفات HTML منفصلة عن شيفرة الخادم، لذا سنتعرف في الفقرة التالية على طريقة تنفيذ ذلك.
</p>

<h2>
	إرسال ملف صفحة HTML
</h2>

<p>
	يمكن إرسال محتوى صفحات HTML عبر تمريرها مباشرة على شكل سلسلة نصية لتابع الإرسال كما فعلنا في الفقرة السابقة، ولكن يفضل تخزين محتوى صفحات HTML ضمن ملفات منفصلة وتخديم محتواها من قبل الخادم، حيث يمكن بذلك التعديل على محتواها بسهولة أكبر، ونكون قد فصلنا بذلك محتوى صفحات الويب عن شيفرات الخادم، وعملية الفصل هذه شائعة في معظم أطر العمل المشهورة لذا سيفيدنا معرفة الطريقة التي يتم بها تحميل وإرسال ملفات HTML.
</p>

<p>
	ولتخديم ملفات HTML من الخادم، يجب تحميل ملفاتها أولًا باستخدام الوحدة <a href="https://wiki.hsoub.com/Node.js/fs" rel="external"><code>fs</code></a> وكتابة محتوى الملف ضمن رد HTTP، لذا نُنشئ بداية ملف HTML الذي سيرسله الخادم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8256_85" style="">
<span class="pln">touch index.html</span></pre>

<p>
	نفتح ملف الصفحة index.html ضمن محرر النصوص ونكتب صفحة HTML بسيطة تحتوي على خلفية باللون البرتقالي وعبارة ترحيب في المنتصف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8256_87" style="">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">My Website</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;style&gt;</span><span class="pln">
        </span><span class="pun">*,</span><span class="pln">
        html </span><span class="pun">{</span><span class="pln">
            margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
            padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
            border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        html </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        body </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
            position</span><span class="pun">:</span><span class="pln"> relative</span><span class="pun">;</span><span class="pln">
            background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> rgb</span><span class="pun">(</span><span class="lit">236</span><span class="pun">,</span><span class="pln"> </span><span class="lit">152</span><span class="pun">,</span><span class="pln"> </span><span class="lit">42</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="pun">.</span><span class="pln">center </span><span class="pun">{</span><span class="pln">
            width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
            height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pun">%;</span><span class="pln">
            margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
            position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
            top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pun">%;</span><span class="pln">
            left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50</span><span class="pun">%;</span><span class="pln">
            transform</span><span class="pun">:</span><span class="pln"> translate</span><span class="pun">(-</span><span class="lit">50</span><span class="pun">%,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">50</span><span class="pun">%);</span><span class="pln">
            color</span><span class="pun">:</span><span class="pln"> white</span><span class="pun">;</span><span class="pln">
            font</span><span class="pun">-</span><span class="pln">family</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Trebuchet MS"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Helvetica</span><span class="pun">,</span><span class="pln"> sans</span><span class="pun">-</span><span class="pln">serif</span><span class="pun">;</span><span class="pln">
            text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        h1 </span><span class="pun">{</span><span class="pln">
            font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">144px</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        p </span><span class="pun">{</span><span class="pln">
            font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">64px</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="tag">&lt;/style&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"center"</span><span class="tag">&gt;</span><span class="pln">
        </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello Again!</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
        </span><span class="tag">&lt;p&gt;</span><span class="pln">This is served from a file</span><span class="tag">&lt;/p&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln">

</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	ستعرض الصفحة السابقة سطران هما "Hello Again!‎" و "This is served from a file"، في منتصف الصفحة فوق بعضهما بعضًا، والسطر الأول منها سيظهر بحجم خط أكبر من السطر الآخر، وستظهر النصوص باللون الأبيض وخلفية الصفحة باللون البرتقالي.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونعود إلى شيفرة الخادم حيث في هذا المثال سنستخدم الملف htmlFile.js الذي أعددناه سابقًا لتطوير الخادم، لذا نفتحه ضمن محرر النصوص ونبدأ أولًا باستيراد الوحدة <code>fs</code> بما أننا ننوي قراءة الملف السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_89" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"http"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	سنستفيد من التابع <code>readFile()‎</code> لتحميل محتوى ملف HTML، ونلاحظ كيف استوردنا نسخة التوابع التي تستعمل الوعود وذلك لتبسيط كتابة الشيفرات، حيث أنها أسهل بالقراءة من استخدام توابع رد النداء، والتي سيتم استيرادها افتراضيًا في حال استوردنا الوحدة <code>fs</code> فقط كالتالي <code>require('fs')‎</code>، ويمكنك الرجوع إلى المقال الخامس من هذه السلسلة للتعرف أكثر على البرمجة اللامتزامنة في جافاسكربت.
</p>

<p>
	والآن نبدأ بقراءة ملف HTML السابق عند وصول طلب من المستخدم، لهذا نعدل تابع معالجة الطلب <code>requestListener()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_91" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">"/index.html"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	استدعينا التابع <code>fs.readFile()‎</code> لتحميل الملف، ومررنا له القيمة <code>‎__dirname + "/index.html"‎</code> والتي يدل فيها المتغير الخاص <code>‎__dirname</code> على المسار المطلق للمجلد الحاوي على ملف جافاسكربت الحالي، ونضيف إليه القيمة <code>‎/index.html</code> للحصول على المسار المطلق الكامل لملف HTML الذي نريد إرساله، وبعد اكتمال تحميل الملف نضيف التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_93" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">"/index.html"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">contents </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	سنرسل المحتوى الناتج عن نجاح تنفيذ الوعد الذي يعيده التابع <code>fs.readFile()‎</code> أي قراءة الملف بنجاح كما فعلنا سابقًا وذلك ضمن التابع <code>then()‎</code>، حيث سيحتوي العامل <code>contents</code> على بيانات الملف بعد نجاح قراءته.
</p>

<p>
	وكما فعلنا سابقًا ضبطنا بدايةً قيمة الترويسة <code>Content-Type</code> إلى <code>text/html</code> للدلالة على إرسال محتوى بصيغة HTML، ثم ضبطنا رمز الحالة إلى <code>200</code> للدلالة على نجاح الطلب، ثم أرسلنا صفحة HTML التي حملناها إلى المستخدم وتحديدًا محتوى المتغير <code>contents</code>، لكن أحيانًا قد يفشل التابع <code>fs.readFile()‎</code> في قراءة الملف لأي سبب كان، لذا يجب معالجة حالة الخطأ تلك بإضافة الشيفرة التالية ضمن التابع <code>requestListener()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_95" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">"/index.html"</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">contents </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
        </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">500</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نحفظ الملف ونخرج من محرر النصوص، ونلاحظ عندما يحدث خطأ ما أثناء تنفيذ الوعد سيتم رفضه، حيث يمكننا معالجة الخطأ باستخدام التابع <code>catch()‎</code> والذي يُمرر إليه كائن الخطأ الذي يرميه استدعاء تابع قراءة الملف <code>fs.readFile()‎</code>، ونحدِّد فيه رمز حالة الرد بالقيمة <code>500</code> للدلالة على حدوث خطأ داخلي من طرف الخادم ونعيد الخطأ للمستخدم.
</p>

<p>
	والآن نشغل الخادم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_97" style="">
<span class="pln">node htmlFile</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونزور عنوانه <a href="http://localhost:8000" ipsnoembed="false" rel="external nofollow">http://localhost:8000</a> باستخدام المتصفح ستظهر لنا صفحة الويب كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="109320" href="https://academy.hsoub.com/uploads/monthly_2022_10/html-file.png.89e63c03393bb3e9a08fa62625634f85.png" rel=""><img alt="html-file" class="ipsImage ipsImage_thumbnailed" data-fileid="109320" data-unique="b6yvp0hv0" src="https://academy.hsoub.com/uploads/monthly_2022_10/html-file.thumb.png.6cd7a77e496b8cb528b80412fbea1673.png" style="width: 780px; height: auto;"></a>
</p>

<p>
	وبذلك نكون قد أرسلنا صفحة HTML مُخزَّنة من ملف إلى المستخدم، والآن نوقف الخادم ونعود إلى الطرفية مجددًا.
</p>

<p>
	انتبه إلى أنَّ تحميل صفحة HTML بهذه الطريقة عند كل طلب HTTP يصل إلى الخادم يؤثر على الأداء، ومع أن الصفحة التي استخدمناها في مثالنا حجمها صغير وهو حوالي 800 بايت فقط، إلا أنه عند بناء التطبيقات قد يصل أحيانًا حجم الصفحات المستخدمة إلى رتبة الميجابايت، مما يؤدي لبطء في تحميلها وتخديمها للعميل، خصوصًا إذا كان من المتوقع ورود طلبات كثيرة إلى الخادم، لذا ولرفع الأداء يمكن تحميل محتوى الملفات مرة واحدة عند إقلاع الخادم وإرسال محتواها للطلبات الواردة، وبعد انتهاء عملية التحميل نخبر الخادم ببدء الاستماع للطلبات على العنوان المحدد له، وهذا ما سنتعلمه في الفقرة التالية حيث سنطور هذه الميزة في الخادم لرفع أداءه.
</p>

<h3>
	رفع كفاءة تخديم صفحات HTML
</h3>

<p>
	بدلًا من تحميل ملفات HTML عند كل طلب يرد إلى الخادم يمكننا تحميلها لمرة واحدة فقط في البداية، وبعدها نعيد تلك البيانات المخزنة مسبقًا لكل طلب سيرد لاحقًا إلى الخادم، لذلك نعود لملف المثال السابق htmlFile.js ونفتحه ضمن محرر النصوص ونضيف فيه متغيرًا جديدًا قبل إنشاء تابع معالجة الطلب <code>requestListener()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_100" style="">
<span class="pun">...</span><span class="pln">
let indexFile</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	سيحتوي هذا المتغير على محتويات ملف HTML عند تشغيل الخادم، والآن نعدل على التابع <code>requestListener()‎</code> وبدلًا من تحميل الملف داخله نعيد مباشرة محتوى المتغير <code>indexFile</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_102" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/html"</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">indexFile</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ونبدل مكان شيفرة تحميل الملف من داخل التابع <code>requestListener()‎</code> إلى أعلى الملف في مكان إعداد الخادم ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_104" style="">
<span class="pun">...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">requestListener</span><span class="pun">);</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">"/index.html"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">contents </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        indexFile </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">;</span><span class="pln">
        server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> host</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Server</span><span class="pln"> is running on http</span><span class="pun">:</span><span class="com">//${host}:${port}`);</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not read index</span><span class="pun">.</span><span class="pln">html file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">}`);</span><span class="pln">
        process</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span></pre>

<p>
	نلاحظ أن عملية قراءة الملف شبيهة جدًا بما نفذنا سابقًا، ولكن الفرق هنا أننا نحفظ بعد نجاح عملية قراءة الملف محتوياته ضمن المتغير العام <code>indexFile</code>، وبعد ذلك نشغل الخادم باستدعاء التابع <code>listen()‎</code>، حيث أن الخطوة الأساسية هي تحميل الملف لمرة واحدة قبل تشغيل الخادم، لنضمن بذلك أن التابع <code>requestListener()‎</code> سيعيد محتوى الملف المخزن ضمن المتغير <code>indexFile</code> وأن قيمته ليست فارغة.
</p>

<p>
	وعدلنا أيضًا تابع معالجة الخطأ بحيث عند حدوث أي خطأ في عملية تحميل الملف سنطبع رسالة ضمن الطرفية توضح السبب ونخرج مباشرة من الخادم عبر استدعاء التابع <code>exit()‎</code>، وبذلك نستطيع معاينة سبب الخطأ الذي يمنع تحميل الملف ونعالج المشكلة أولًا ثم نعيد تشغيل الخادم بنجاح، فما الفائدة من تشغيل الخادم دون تحميل الملف المراد تخديمه.
</p>

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

<h2>
	إدارة الوجهات Routes في الخادم
</h2>

<p>
	معظم المواقع التي نزورها أو الواجهات البرمجية التي نتعامل معها تحوي عدة مسارات أو وجهات تسمح لنا بالوصول إلى عدد من الموارد على نفس الخادم، فمثلًا في نظام لإدارة الكتب في المكتبات على النظام أن يدير بيانات الكتب وبيانات أخرى مثل المؤلفين لهذه الكتب، وسيوفر خدمات أخرى مثل البحث والتصنيف، ومع أن بيانات الكتب والمؤلفين لها مرتبطة ببعضها لكن يمكن معاملتها كمَوردين مختلفين، وفي هذه الحالة يمكن أن نطور النظام ليخدّم كل نوع من تلك الموارد ضمن مسار محدد له، ليميز المستخدم الذي يتعامل مع الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> للنظام نوع المورد الذي يتعامل معه.
</p>

<p>
	لنطبق المثال ذاك ببناء خادم بسيط لنظام إدارة مكتبة سيحتوي على نوعين من البيانات، فعند طلب المستخدم المورد من المسار <code>‎/books</code> سنرسل له قائمة بالكتب المتوفرة بصيغة JSON، أما عند طلب المسار <code>‎/authors</code> سنرسل له قائمة بمعلومات حول المؤلفين بصيغة JSON أيضًا، ففي كل أمثلة خوادم الويب السابقة في هذا المقال كنا نرسل نفس الرد دومًا لكل الطلبات التي تصل إلى الخادم.
</p>

<p>
	لنختبر ذلك، علينا أولًا إرسال طلبات مختلفة للخادم ونلاحظ الرد المرسل على كل منها، لذا نعيد تشغيل خادم JSON الذي طورناه سابقًا بتنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_106" style="">
<span class="pln">node json</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	وكالعادة في طرفية أخرى نرسل طلب HTTP باستخدام cURL كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_108" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000</span></pre>

<p>
	يعيد لنا الخادم الرد التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_114" style="">
<span class="pun">{</span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"This is a JSON response"</span><span class="pun">}</span></pre>

<p>
	لنختبر الآن إرسال طلب على مسار مختلف للخادم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_118" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000/todos</span></pre>

<p>
	سنلاحظ ظهور نفس الرد السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_120" style="">
<span class="pun">{</span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"This is a JSON response"</span><span class="pun">}</span></pre>

<p>
	ذلك لأن الخادم لا يعير اهتمامًا أبدًا عند معالجة الطلب داخل التابع <code>requestListener()‎</code> للمسار الذي يطلبه المستخدم ضمن URL، لذا عندما أرسلنا طلبًا إلى المسار <code>‎/todos</code> أعاد لنا الخادم نفس محتوى JSON الذي يعيده افتراضيًا، ولكن لبناء خادم نظام إدارة المكتبة يجب أن نفصل ونحدد نوع البيانات التي سنعيدها للمستخدم بناءً على المسار الذي يطلب الوصول إليه.
</p>

<p>
	والآن نوقف الخادم ونفتح الملف routes.js ونبدأ بتخزين بيانات JSON التي سيوفرها الخادم ضمن متغيرات قبل تعريف تابع معالجة الطلب <code>requestListener()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_122" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> books </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">([</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"The Alchemist"</span><span class="pun">,</span><span class="pln"> author</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Paulo Coelho"</span><span class="pun">,</span><span class="pln"> year</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1988</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"The Prophet"</span><span class="pun">,</span><span class="pln"> author</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Kahlil Gibran"</span><span class="pun">,</span><span class="pln"> year</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1923</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">]);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> authors </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">([</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Paulo Coelho"</span><span class="pun">,</span><span class="pln"> countryOfBirth</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Brazil"</span><span class="pun">,</span><span class="pln"> yearOfBirth</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1947</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Kahlil Gibran"</span><span class="pun">,</span><span class="pln"> countryOfBirth</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Lebanon"</span><span class="pun">,</span><span class="pln"> yearOfBirth</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1883</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	يحتوي المتغير <code>books</code> على سلسلة نصية بصيغة JSON فيها مصفوفة من الكائنات التي تمثل الكتب المتوفرة، ويحتوي كل كتاب منها على خاصية العنوان أو الاسم والمؤلف وسنة النشر، بينما يحتوي المتغير <code>authors</code> على سلسلة نصية بصيغة JSON أيضًا فيها مصفوفة من الكائنات التي تمثل المؤلفين ويملك كل مؤلف منها خاصية اسمه وبلد وسنة الولادة.
</p>

<p>
	وبعد أن جهزنا البيانات التي سنعيدها للمستخدم نبدأ بتعديل تابع معالجة الطلب <code>requestListener()‎</code> ليعيد البيانات المناسبة منها بحسب المسار المطلوب، لذا نبدأ بتعيين قيمة الترويسة <code>Content-Type</code> لكل الطلبات التي سنرسلها، وبما أن جميع البيانات هي بصيغة JSON يمكننا تحديد قيمة الترويسة مباشرةً في البداية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_124" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	والآن سنعيد بيانات JSON بحسب المسار المقابل ضمن عنوان URL الذي يحاول المستخدم طلبه، لذا نكتب تعليمة تبديل <code>switch</code> بحسب عنوان URL للطلب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_126" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نلاحظ كيف يمكننا الوصول للمسار الذي يطلبه المستخدم من الخاصية <code>url</code> من كائن الطلب <code>req</code>، ونضيف بعدها حالات التوجيه للمسارات أو الوجهات المحددة ضمن تعليمة <code>switch</code> ونعيد بيانات JSON المناسبة لها، حيث توفر التعليمة <code>switch</code> في جافاسكربت طريقة للتحكم بالشيفرات التي ستنفَّذ بحسب القيمة أو التعبير البرمجي الممرر لها بين القوسين.
</p>

<p>
	والآن نضيف الحالة التي يطلب بها المستخدم قائمة الكتب باستخدام الكلمة <code>case</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_128" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"/books"</span><span class="pun">:</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">books</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نعين عندها رمز الحالة للطلب بالقيمة <code>200</code> للدلالة على نجاح الطلب ونعيد قيمة JSON الحاوية على قائمة الكتب المتاحة، ونضيف بعدها حالة <code>case</code> أخرى للرد على مسار طلب المؤلفين كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_130" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"/books"</span><span class="pun">:</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">books</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"/authors"</span><span class="pun">:</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">authors</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	كما في الحالة السابقة نضبط أولًا رمز الحالة للرد بالقيمة <code>200</code> للدلالة على صحة الطلب، ونعيد قيمة JSON الحاوية على قائمة المؤلفين، وفي حال طلب المستخدم أي مسار آخر غير مدعوم سنرسل له خطأ، ولهذه الحالة يمكن إضافة الحالة الافتراضية <code>default</code> لالتقاط كل الحالات التي لا تطابق أي من الحالات المُعرّفة حيث نضبط فيها رمز الحالة إلى القيمة <code>404</code> للدلالة على أن المورد الذي يحاول المستخدم الوصول إليه غير موجود ونعيد رسالة خطأ للمستخدم ضمن كائن بصيغة JSON السابقة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_132" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> requestListener </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">"Content-Type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"application/json"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span class="pln">url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"/books"</span><span class="pun">:</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">books</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"/authors"</span><span class="pun">:</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">authors</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">break</span><span class="pln">
        </span><span class="kwd">default</span><span class="pun">:</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span class="lit">404</span><span class="pun">);</span><span class="pln">
            res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">error</span><span class="pun">:</span><span class="str">"Resource not found"</span><span class="pun">}));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	والآن لنشغل الخادم ونختبره من طرفية أخرى بإرسال طلب وصول إلى مسار الكتب المتاحة ونعاين الرد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_134" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000/books</span></pre>

<p>
	لنحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_136" style="">
<span class="pun">[{</span><span class="str">"title"</span><span class="pun">:</span><span class="str">"The Alchemist"</span><span class="pun">,</span><span class="str">"author"</span><span class="pun">:</span><span class="str">"Paulo Coelho"</span><span class="pun">,</span><span class="str">"year"</span><span class="pun">:</span><span class="lit">1988</span><span class="pun">},{</span><span class="str">"title"</span><span class="pun">:</span><span class="str">"The Prophet"</span><span class="pun">,</span><span class="str">"author"</span><span class="pun">:</span><span class="str">"Kahlil Gibran"</span><span class="pun">,</span><span class="str">"year"</span><span class="pun">:</span><span class="lit">1923</span><span class="pun">}]</span></pre>

<p>
	حصلنا على قائمة الكتب كما هو متوقع، وبالمثل نختبر مسار طلب المؤلفين <code>‎/authors</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_138" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000/authors</span></pre>

<p>
	لنحصل على الخرج التالي:
</p>

<pre class="ipsCode">
[{"name":"Paulo Coelho","countryOfBirth":"Brazil","yearOfBirth":1947},{"name":"Kahlil Gibran","countryOfBirth":"Lebanon","yearOfBirth":1883}]
</pre>

<p>
	وأخيرًا نختبر الوصول إلى مسار غير مدعوم ونتأكد من أن تابع معالجة الطلب <code>requestListener()‎</code> سيعيد لنا رسالة خطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_140" style="">
<span class="pln">curl http</span><span class="pun">:</span><span class="com">//localhost:8000/notreal</span></pre>

<p>
	سيعيد لنا الخادم رسالة الخطأ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8256_142" style="">
<span class="pun">{</span><span class="str">"error"</span><span class="pun">:</span><span class="str">"Resource not found"</span><span class="pun">}</span></pre>

<p>
	نوقف الخادم وبذلك نكون قد طورنا خادمًا يمكنه توجيه الطلب ضمن عدة مسارات مدعومة والرد عليها ببيانات مختلفة، وأضفنا إليه أيضًا ميزة إرسال رسالة خطأ عندما يحاول المستخدم الوصول لمسار غير مدعوم.
</p>

<h2>
	ختامًا
</h2>

<p>
	طورنا في هذا المقال عددًا من خوادم HTTP في بيئة نود، حيث بدأنا بإعادة نص بسيط ضمن الرد مرورًا بعدة أنواع من صيغ البيانات مثل JSON و CSV وصفحات HTML، وطورنا الخادم لتحميل صفحات HTML من ملفات خارجية مخصصة لها وتخديمها وإرسال محتواها إلى العميل، وأخيرًا طورنا واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> يمكنها الرد على طلب المستخدم بعدة أنواع من المعلومات بحسب معلومات من الطلب المُرسل للخادم.
</p>

<p>
	وبذلك تكون قد تعلمت طريقة إنشاء خوادم ويب يمكنها معالجة عدة أنواع من الطلبات والردود، والآن حاول مما تعلمت بناء خادم ويب يُخدّم عدة صفحات HTML للمستخدم بحسب المسارات المختلفة التي يطلبها، ويمكنك أيضًا بناء واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> الخاصة بك، ويمكنك الرجوع إلى <a href="https://wiki.hsoub.com/Node.js/http" rel="external">التوثيق الرسمي للوحدة <code>http</code></a> من نود لتعلم المزيد عن خوادم الويب.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-web-server-in-node-js-with-the-http-module" rel="external nofollow">How To Create a Web Server in Node.js with the HTTP Module</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">مدخل إلى خادم الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">مدخل إلى HTTP</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1745</guid><pubDate>Sat, 08 Oct 2022 07:30:22 +0000</pubDate></item><item><title>&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Mocha &#x648; Assert &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-mocha-%D9%88-assert-%D9%81%D9%8A-nodejs-r1744/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/6341186c4ea6a_----Mocha--Assert.png.1c3f1e4837bd863ac99f26ea5a4dc755.png" /></p>

<p>
	الاختبارات البرمجية Tests جزء مهم جدًا من عملية تطوير البرمجيات، وهي عبارة عن شيفرات برمجية مهمتها اختبار أجزاء التطبيق خلال مرحلة تطويره للتحقق من أدائها السليم خصوصًا بعد إضافة التطويرات والتعديلات عليه، ولتوفير الوقت عادة ما نؤتمت هذه الاختبارات إذ تمكننا سهولة هذه العملية من تنفيذ تلك الاختبارات باستمرار بعد كل إضافة لشيفرات جديدة على لتطبيق للتأكد من صحة تلك التغييرات وأن أي إضافة أو تعديل على جزء من الشيفرة لا تعطل عمل أي مزايا أخرى موجودة سابقًا، ما يمنح مطور التطبيق الثقة الكافية باعتماد التغييرات خصوصًا قبل مرحلة نشر التطبيق وإتاحته للاستخدام.
</p>

<p>
	ويأتي إطار عمل الاختبارات test framework لينظم طريقة إنشاء وتشغيل حالات الاختبار، ومن أشهر أطر عمل الاختبار تلك في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> هو موكا <a href="https://mochajs.org/" rel="external nofollow">Mocha</a>، حيث تقتصر مهمته على إنشاء وتنظيم الاختبارات وليس تطبيق اختبارات التوكيد assertion testing على عمل الشيفرات، لذا لمطابقة القيم وتطبيق العديد من التوكيدات ضمن الاختبارات نستخدم وحدة برمجية أخرى يوفرها نود لنا افتراضيًا وهي <a href="https://wiki.hsoub.com/Node.js/assert" rel="external">assert</a>.
</p>

<p>
	سنكتب في هذا المقال اختبارات لتطبيق قائمة مهام سنطوره ضمن بيئة نود، وسنُعد إطار عمل الاختبارات موكا Mocha له ونستخدمه لتنظيم الاختبارات، ثم سنستخدم الوحدة <code>assert</code> لكتابة تلك الاختبارات، أي سنستخدم Mocha لتخطيط الاختبارات والوحدة <code>assert</code> لتنفيذ التوكيدات ضمن الاختبار، وسيلزمك لتطبيق الأمثلة في هذا المقال تثبيت بيئة Node.js على جهازك، حيث سنستعمل في هذا المقال الإصدار رقم 10.16.0، وأيضًا معرفة بأساسيات <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">لغة جافاسكربت</a>.
</p>

<h2>
	كتابة الوحدة البرمجية في نود
</h2>

<p>
	نبدأ بكتابة وحدة برمجية سنختبرها لاحقًا وظيفتها إدارة قائمة من المهام وتوفر طريقة لاستعراض قائمة المهام التي نعمل عليها وإضافة مهام جديدة وتحديد المهام المكتملة منها، وستتيح أيضًا ميزة تصدير قائمة المهام هذه إلى ملف بصيغة CSV، وللتعرف أكثر على طرق كتابة وحدة برمجية باستخدام نود يمكنك مراجعة مقال <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-modules-%D9%81%D9%8A-nodejs-r1742/" rel="">إنشاء وحدات برمجية Modules في Node.js</a> من هذه السلسلة.
</p>

<p>
	والآن نبدأ بتحضير بيئة العمل وننشئ مجلد باسم المشروع <code>todos</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_10" style="">
<span class="pln">mkdir todos</span></pre>

<p>
	ثم ندخل إلى المجلد:
</p>

<pre class="ipsCode">
cd todos
</pre>

<p>
	ونهيّئ ملف حزمة npm لاستخدامه لاحقًا لتنفيذ أوامر الاختبار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_12" style="">
<span class="pln">npm init </span><span class="pun">-</span><span class="pln">y</span></pre>

<p>
	سنحتاج لاعتمادية واحدة فقط وهي إطار عمل الاختبارات موكا Mocha لتنظيم وتشغيل الاختبارات التي سنكتبها لاحقًا، لذا ننفذ أمر تثبيتها ضمن المشروع كالتالي:
</p>

<pre class="ipsCode">
npm i request --save-dev mocha
</pre>

<p>
	نلاحظ أننا ثبتناها كاعتمادية تطوير لأننا لن نحتاج إليها في مرحلة الإنتاج بل ستستخدم خلال مرحلة التطوير فقط، ويمكنك التعرف أكثر على طرق إدارة الوحدات البرمجية في Node.js بمراجعة <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">المقال الثالث</a> من هذه السلسلة، والآن ننشئ الملف الأساسي لهذه الوحدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_14" style="">
<span class="pln">touch index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونفتحه ضمن أي محرر النصوص وليكن باستخدام <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/" rel="">محرر نانو nano</a>:
</p>

<pre class="ipsCode">
nano index.js
</pre>

<p>
	نبدأ بتعريف الصنف <code>Todos</code> والذي سيحتوي على توابع سنستخدمها لإدارة قائمة المهام، لذا نضيف الأسطر التالية إلى ملف index.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_18" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">;</span></pre>

<p>
	عرّفنا في بداية الملف الصنف <code>Todos</code> والتابع الباني له <code>constructor()‎</code> بدون أي معاملات، حيث يمكننا إنشاء كائن جديد من هذا الصنف دون الحاجة لتمرير أي قيم له، ومهمته حاليًا إنشاء الخاصية <code>todos</code> وتعيين مصفوفة فارغة كقيمة لها، ثم صدّرنا هذا الصنف باستخدام الكائن <code>modules</code> في النهاية، كي تتمكن باقي الوحدات البرمجية من استيراد واستخدام الصنف <code>Todos</code>، فبدون ذلك لا يمكن لملف الاختبار الذي سنُنشئه لاحقًا استيراد واستخدام هذا الصنف، والآن نضيف تابعًا وظيفته إرجاع مصفوفة المهام المخزنة ضمن الكائن كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_20" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    list</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[...</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">;</span></pre>

<p>
	يعيد التابع <code>list()‎</code> نسخة من المصفوفة المخزنة ضمن الصنف باستخدام <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A5%D8%B3%D9%86%D8%A7%D8%AF-%D8%A8%D8%A7%D9%84%D8%AA%D9%81%D9%83%D9%8A%D9%83-destructuring-assignment-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r824/" rel="">صيغة التفكيك في جافاسكربت</a> لأن إعادة المتغير <code>this.todos</code> مباشرة يعني إعادة مؤشر إلى المصفوفة الأصلية ضمن الصنف <code>Todos</code> وبذلك نمنع الوصول إلى المصفوفة الأصلية وإجراء تعديلات عليها عن طريق الخطأ.
</p>

<p>
	<strong>ملاحظة</strong>: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r818/" rel="">المصفوفات في جافاسكربت</a> تُمرَّر بالمرجعية reference (وكذلك الكائنات objects أيضًا)، أي عند إسناد مصفوفة إلى متغير فإنه يحمل إشارة إلى تلك المصفوفة الأصلية وليس المصفوفة نفسها أي عند استعمال هذا المتغير لاحقًا أو تمريره كمعامل لتابع ما، فستشير جافاسكربت إلى المصفوفة الأصلية دومًا وستنعكس التعديلات عليها، فمثلًا إذا عند إنشاء مصفوفة تحوي ثلاث عناصر أسندناها إلى متغير <code>x</code>، ثم أنشأنا المتغير <code>y</code> وأسندنا له قيمة المصفوفة السابقة كالتالي <code>y = x</code>، فسيشير عندها كل من <code>y</code> و <code>x</code> إلى نفس المصفوفة وكل تغيير نقوم به على المصفوفة عن طريق المتغير <code>y</code> سيؤثر على المصفوفة التي يشير إليها المتغير <code>x</code> والعكس صحيح أي كلاهما يشيران إلى المصفوفة نفسها.
</p>

<p>
	والآن لنضيف التابع <code>add()‎</code> ووظيفته إضافة مهمة جديدة إلى قائمة المهام الحالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_22" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    list</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[...</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    add</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todo </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            title</span><span class="pun">:</span><span class="pln"> title</span><span class="pun">,</span><span class="pln">
            completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">todo</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">;</span></pre>

<p>
	يأخذ التابع <code>add()‎</code> معاملًا من نوع سلسلة نصية ويضعها ضمن خاصية العنوان <code>title</code> لكائن المهمة الجديدة، ويعين خاصية اكتمال هذه المهمة <code>completed</code> بالقيمة <code>false</code> افتراضيًا، ثم يضيف ذلك الكائن إلى مصفوفة المهام الحالية ضمن الكائن.
</p>

<p>
	ومن المهام الأخرى التي يجب أن يوفرها صنف مدير المهام هو تعيين مهمة كمهمة مكتملة، حيث سننفذ ذلك بالمرور على عناصر مصفوفة المهام <code>todos</code> والبحث عن عنصر المهمة التي يريد المستخدم تعيينها كمهمة مكتملة، وعند العثور عليها نعينها كمكتملة وإذا لم يُعثر عليها نرمي خطأ كإجراء احترازي، والآن نضيف هذا التابع الجديد <code>complete()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_24" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    list</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[...</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    add</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todo </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            title</span><span class="pun">:</span><span class="pln"> title</span><span class="pun">,</span><span class="pln">
            completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">todo</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    complete</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todoFound </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">todo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">title </span><span class="pun">===</span><span class="pln"> title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                todo</span><span class="pun">.</span><span class="pln">completed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
                todoFound </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">todoFound</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">No</span><span class="pln"> TODO was found </span><span class="kwd">with</span><span class="pln"> the title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"${title}"</span><span class="pun">`);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">;</span></pre>

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

<h2>
	اختبار الشيفرة يدويًا
</h2>

<p>
	في هذه الفقرة سننفذ شيفرات التوابع السابقة لصنف إدارة المهام <code>Todos</code> يدويًا لنعاين ونتفحص خرجها ونتأكد من عملها كما هو متوقع منها أن تعمل، وتدعى هذه الطريقة بالاختبار اليدوي manual testing فهي أشيع طريقة يطبقها المطورون معظم الوقت حتى لو لكن يكن ذلك مقصودًا، وسنؤتمت لاحقًا تلك العملية باستخدام موكا Mocha لكن بدايةً سنختبر الشيفرات يدويًا لنتعرف على هذه الطريقة ونلاحظ ميزة استخدام إطار خاص لأتمتة الاختبارات.
</p>

<p>
	نبدأ بإضافة مهمتين جديدتين ونعيّن إحداهما كمكتملة، لذلك نبدأ <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B6%D8%B9-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D9%8A-repl-%D9%81%D9%8A-nodejs-r1712/" rel="">جلسة REPL جديدة</a> ضمن مجلد المشروع نفسه الحاوي على الملف <code>index.js</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_26" style="">
<span class="pln">node</span></pre>

<p>
	ستلاحظ ظهور الرمز <code>‎&gt;‎</code> في بداية السطر عند الدخول إلى وضع REPL التفاعلي، ويمكننا إدخال شيفرات جافاسكربت لتنفيذها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_30" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./index'</span><span class="pun">);</span></pre>

<p>
	نحمل الوحدة البرمجية لمدير قائمة المهام باستخدام التابع <code>require()‎</code> ونخزن قيمتها ضمن متغير بالاسم <code>Todos</code>، والذي صدرنا منه افتراضيًا الصنف <code>Todos</code>، والآن لنبدأ بإنشاء كائن جديد من ذلك الصنف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_28" style="">
<span class="kwd">const</span><span class="pln"> todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span></pre>

<p>
	يمكننا اختبار الوحدة باستخدام الكائن <code>todos</code> المشتق من الصنف <code>Todos</code> للتأكد من عمله وفق ما هو متوقع، فنبدأ بإضافة مهمة جديدة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_33" style="">
<span class="pln">todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"run code"</span><span class="pun">);</span></pre>

<p>
	لم يظهر إلى الآن مما نفذناه أي خرج ضمن الطرفية، ولنتأكد من تخزين المهمة السابقة بشكل سليم ضمن قائمة المهام نستدعي تابع عرض المهام الموجودة ونعاين النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_35" style="">
<span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">();</span></pre>

<p>
	سيظهر لنا الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_37" style="">
<span class="pun">[</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'run code'</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	وهي النتيجة الصحيحة المتوقعة حيث تحتوي على عنصر وحيد وهو المصفوفة التي أضفناها سابقًا وحالة اكتمالها غير مكتملة، لنضيف الآن مهمة أخرى ونعدل المهمة الأولى لتصبح مكتملة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_39" style="">
<span class="pln">todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"test everything"</span><span class="pun">);</span><span class="pln">
todos</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">(</span><span class="str">"run code"</span><span class="pun">);</span></pre>

<p>
	نتوقع الآن وجود مهمتين ضمن الكائن <code>todos</code>، وهما "run code" و "test everything"، حيث يجب أن تكون المهمة الأولى "run code" مكتملة، ونتأكد من ذلك باستدعاء التابع <code>list()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_41" style="">
<span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">();</span></pre>

<p>
	نحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_43" style="">
<span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'run code'</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">'test everything'</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	الخرج صحيح كما هو متوقع، والآن نخرج من جلسة REPL بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_45" style="">
<span class="pun">.</span><span class="pln">exit</span></pre>

<p>
	بذلك نكون قد تحققنا من عمل الوحدة البرمجية التي طورناها بشكل سليم، ولم نستخدم في ذلك أي ملفات اختبار مخصصة أو مكتبات اختبار، بل اعتمدنا فقط على الاختبار اليدوي، ولكن هذه الطريقة في الاختبار تأخذ وقتًا وجهدًا في كل مرة نضيف فيها تعديلات على الوحدة البرمجية، لذا سنؤتمت في الفقرة التالية عملية الاختبار هذه ونرى ما يمكن لإطار العمل موكا أن يساعدنا في ذلك.
</p>

<h2>
	كتابة اختبارات Node.js باستخدام Mocha و Assert
</h2>

<p>
	اختبرنا في الفقرة السابقة التطبيق يدويًا مع أن ذلك قد يفيد في بعض الحالات إلا أنه ومع تطوير الوحدة البرمجية التي نعمل عليها وزيادة حجمها والشيفرات المستخدمة ضمنها ستزداد تلك الطريقة صعوبة، وبينما نحن نختبر المزايا الجديدة التي أضفناها فيجب أيضًا اختبار المزايا السابقة جميعها مجددًا للتأكد أن التطويرات لم تؤثر على أي مزايا سابقة، وسيجبرنا ذلك اختبار كل ميزة ضمن التطبيق مرارًا وتكرارًا في كل مرة نعدل فيها الشيفرة ما سيأخذ الكثير من الوقت والجهد وقد نخطئ أو ننسى تنفيذ بعض الاختبارات خلال تلك المرحلة، والحل إعداد اختبارات مؤتمتة عبارة عن نصوص اختبار برمجية كأي برنامج عادي آخر، نمرر فيها بيانات محددة إلى التوابع ضمن التطبيق ونتأكد من سلامة عملها ووظيفتها كما هو متوقع، وكلما أضفنا ميزة للتطبيق أضفنا معها اختبارها، حيث عندما نكتب اختبارات مقابلة لكل ميزة في التطبيق سنتحقق بذلك من عمل الوحدة البرمجية كاملةً ودون حاجة لتذكر تنفيذ كل التوابع واختبار كل المزايا في كل عملية اختبار.
</p>

<p>
	وسنستخدم في كتابة الاختبارات إطار عمل مخصص للاختبار يدعى موكا Mocha مع وحدة <code>assert</code> البرمجية التي يوفرها نود كما أشرنا في بداية المقال، والآن نبدأ بإنشاء ملف جديد سنضع داخله شيفرات الاختبار كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_47" style="">
<span class="pln">touch index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نفتحه ضمن أي محرر نصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_51" style="">
<span class="pln">nano index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نبدأ بتحميل الوحدة البرمجية لمدير المهام كما فعلنا ضمن جلسة REPL في الفقرة السابقة، وبعدها نحمل الوحدة البرمجية <code>assert</code> لاستخدامها عند كتابة الاختبارات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_49" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./index'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> assert </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'assert'</span><span class="pun">).</span><span class="pln">strict</span><span class="pun">;</span></pre>

<p>
	تسمح الخاصية <code>strict</code> التي استخرجناها من الوحدة <code>assert</code> باستخدام معامل مساواة خاص منصوح باستخدامه ضمن بيئة نود ويوفر مزايا مفيدة أخرى لن ندخل في تفاصيلها.
</p>

<p>
	والآن قبل كتابة الاختبارات لنتعرف على طريقة موكا Mocha في تنظيم وترتيب شيفرات الاختبار، حيث تُكتب الاختبارات في Mocha بالصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_54" style="">
<span class="pln">describe</span><span class="pun">([</span><span class="typ">Test</span><span class="pln"> </span><span class="typ">Group</span><span class="pln"> </span><span class="typ">Name</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">([</span><span class="typ">Test</span><span class="pln"> </span><span class="typ">Name</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">[</span><span class="typ">Test</span><span class="pln"> </span><span class="typ">Code</span><span class="pun">]</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لاحظ وجود استدعاء لدالتين رئيسيين هما <code>describe()‎</code> و <code>it()‎</code>، حيث تستخدم الدالة <code>describe()‎</code> لتجميع الاختبارات المتشابهة معًا المكتوبة عبر <code>it()‎</code>، ولكن خطوة التجميع هذه غير ضرورية لكنها تسهل قراءة ملفات الاختبار وتزيد تنظيمها ويسهل لاحقًا التعديل على الاختبارات المتشابهة بسهولة أكبر، أما الدالة <code>it()‎</code> فتحتوي على شيفرة الاختبار المراد تنفيذها للوحدة البرمجية المختبرة ونستخدم فيها مكتبة <code>assert</code> للتوكيد والتحقق من المخرجات.
</p>

<p>
	وهدفنا في هذه الفقرة استخدام موكا Mocha والوحدة <code>assert</code> لأتمتة عملية الاختبار أو حتى تنفيذها يدويًا كما فعلنا سابقًا، لذلك سنبدأ أولٌا بتعريف مجموعة اختبارات باستخدام التابع <code>describe()‎</code> بإضافة الأسطر التالية لملف الاختبار بعد استيراد الوحدات البرمجية السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_56" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بهذا نكون قد أنشأنا مجموعة اختبارات -سنكتبها لاحقًا- باسم integration test أي اختبار التكامل ووظيفته التحقق من عمل عدة توابع مع بعضها ضمن الوحدات البرمجية، على عكس اختبار الوحدة unit test الذي يختبر دالة واحدة في كل مرة، وعندما ينفذ موكا عملية اختبار التطبيق فسينفذ كل الاختبارات المعرفة ضمن التابع <code>describe()‎</code> ضمن مجموعة بالاسم "integration test" التي عرفناها.
</p>

<p>
	والآن لنضيف اختبارًا باستخدام التابع <code>it()‎</code> لاختبار جزء من التطبيق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_58" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نلاحظ كيف سمينا الاختبار باسم أجنبي واضح يصف عمله معناه بالعربية "يجب التمكن من إضافة مهمة ToDo وإكمالها"، لذا عند تنفيذ أي شخص للاختبارات سيعرف ما الجزء الذي نجح في الاختبار من تلك التي لم تنجح فيه، حيث يعتبر الاختبار الجيد لأي تطبيق توثيقًا جيدًا لعمله فتعتبر تلك الاختبارات كتوثيق تقني للتطبيق.
</p>

<p>
	والآن نبدأ بأول اختبار وهو إنشاء كائن من الصنف <code>Todos</code> جديد والتأكد بأنه لا يحتوي على أي عناصر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_60" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">notStrictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	أنشأنا في أول سطر من الاختبار كائنًا جديدًا من الصنف <code>Todos</code> كما فعلنا سابقًا ضمن REPL أو كما سنفعل عند استخدام هذا الصنف ضمن أي وحدة برمجية أخرى، واستخدمنا في السطر الثاني الوحدة <code>assert</code> وتحديدًا تابع اختبار عدم المساواة <code>notStrictEqual()‎</code> والذي يأخذ معاملان وهما القيمة التي نريد اختبارها وتدعى القيمة الفعلية <code>actual</code>، والمعامل الثاني وهو القيمة التي نتوقع أن لا تساويها وتدعى القيمة المتوقعة <code>expected</code>، وفي حال تساوي القيمتين سيرمي التابع <code>notStrictEqual()‎</code> خطئًا ويفشل هذا الاختبار.
</p>

<p>
	نحفظ الملف ونخرج منه، ونتوقع في هذه الحالة نجاح هذا الاختبار لأن طول المصفوفة سيكون <code>0</code> وهو غير مساوي للقيمة <code>1</code>، ونتأكد من ذلك بتشغيل الاختبارات باستخدام موكا، لذا نعدل بدايةً على ملف الحزمة package.json ونفتحه ضمن محرر النصوص ونعدل النص البرمجي الخاص بتشغيل الاختبارات ضمن الخاصية <code>scripts</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_62" style="">
<span class="pun">...</span><span class="pln">
</span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"mocha index.test.js"</span><span class="pln">
</span><span class="pun">},</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	بذلك نكون قد عدلنا الأمر <code>test</code> الخاص بالأداة npm، حيث عند تنفيذه كالتالي <code>npm test</code> سيتحقق npm من الأمر الذي أدخلناه ضمن ملف الحزمة package.json وسيبحث عن مكتبة موكا Mocha ضمن مجلد الحزم node_modules وينفذ الأمر <code>mocha</code> مُمرِّرًا له اسم ملف الاختبار للتطبيق.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذ أمر الاختبار السابق ونعاين النتيجة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_64" style="">
<span class="pln">npm test</span></pre>

<p>
	نحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_66" style="">
<span class="pun">&gt;</span><span class="pln"> todos@1</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln"> test your_file_path</span><span class="pun">/</span><span class="pln">todos
</span><span class="pun">&gt;</span><span class="pln"> mocha index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js



integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">


  </span><span class="lit">1</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">16ms</span><span class="pun">)</span></pre>

<p>
	يُظهر لنا الخرج السابق مجموعة الاختبارات التي جرى تنفيذها ويترك فراغًا قبل كل اختبار ضمن مجموعة الاختبار المعرفة، ونلاحظ ظهور اسم الاختبار كما مررناه للتابع <code>it()‎</code> في ملف الاختبار، حيث تشير العلامة الظاهرة على يسار الاختبار أن الاختبار قد نجح، وفي الأسفل يظهر لنا خلاصة فيها معلومات عن كل الاختبارات التي نُفِّذت، وفي حالتنا هناك اختبار واحد ناجح واستغرقت عملية الاختبار كاملة 16 ميلي ثانية لتنفيذها، حيث يعتمد هذا التوقيت على أداء الجهاز الذي يُنفذ تلك الاختبارات.
</p>

<p>
	وكما لاحظنا أن الاختبارات التي نفذناها نجحت بالكامل ولكن مع ذلك فإن الاختبار الذي كتبناه قد يشير إلى حالة نجاح مغلوطة، وهي الحالة التي ينجح فيها الاختبار بينما في الحقيقة يجب أن يفشل، حيث أننا في هذا الاختبار نختبر فراغ مصفوفة المهام باختبار أن طولها لا يساوي الواحد، لذا نعدل الاختبار السابق ضمن ملف الاختبارات index.test.js ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_68" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"get up from bed"</span><span class="pun">);</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"make up bed"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">notStrictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نحفظ الملف ونخرج منه ونلاحظ أننا أضفنا مهمتين جديدتين، لننفذ الاختبار ونلاحظ النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_70" style="">
<span class="pln">npm test</span></pre>

<p>
	سيظهر لنا التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_72" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">


  </span><span class="lit">1</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">8ms</span><span class="pun">)</span></pre>

<p>
	نرى أن الاختبار قد نجح لأن طول المصفوفة ليس واحد كما هو متوقع، لكن ذلك يتعارض مع الاختبار السابق الذي أجريناه، حيث مهمته التحقق من أن الكائن الجديد من مدير المهام سيبدأ فارغًا دون أي مهام مخزنة ضمنه، لذا من الأفضل أن يتحقق الاختبار من ذلك في جميع الحالات.
</p>

<p>
	لنعدل الاختبار ونجعله ينجح فقط في حال عدم وجود أي مهام مخزنة ضمن الكائن ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_74" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"get up from bed"</span><span class="pun">);</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"make up bed"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لاحظ استدعينا التابع <code>strictEqual()‎</code> بدلًا من استدعاء التابع <code>notStrictEqual()‎</code> الذي يتحقق من المساواة بين القيمة الحقيقية والمتوقعة الممررة له، بحيث يفشل عند عدم تساوي القيمتين.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونعيد تنفيذ أمر الاختبار لنرى النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_76" style="">
<span class="pln">npm test</span></pre>

<p>
	هذه المرة سيظهر لنا خطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_79" style="">
<span class="pun">...</span><span class="pln">
  integration test
    </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">


  </span><span class="lit">0</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">16ms</span><span class="pun">)</span><span class="pln">
  </span><span class="lit">1</span><span class="pln"> failing

  </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> integration test
       should be able to add and complete </span><span class="typ">TODOs</span><span class="pun">:</span><span class="pln">

      </span><span class="typ">AssertionError</span><span class="pln"> </span><span class="pun">[</span><span class="pln">ERR_ASSERTION</span><span class="pun">]:</span><span class="pln"> </span><span class="typ">Input</span><span class="pln"> A expected to strictly equal input B</span><span class="pun">:</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> expected </span><span class="pun">-</span><span class="pln"> actual

</span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
      </span><span class="pun">+</span><span class="pln"> expected </span><span class="pun">-</span><span class="pln"> actual

      </span><span class="pun">-</span><span class="lit">2</span><span class="pln">
      </span><span class="pun">+</span><span class="lit">0</span><span class="pln">

      at </span><span class="typ">Context</span><span class="pun">.</span><span class="pln"> </span><span class="pun">(</span><span class="pln">index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">9</span><span class="pun">:</span><span class="lit">10</span><span class="pun">)</span><span class="pln">



npm ERR</span><span class="pun">!</span><span class="pln"> </span><span class="typ">Test</span><span class="pln"> failed</span><span class="pun">.</span><span class="pln">  </span><span class="typ">See</span><span class="pln"> above </span><span class="kwd">for</span><span class="pln"> more details</span><span class="pun">.</span></pre>

<p>
	سيفيدنا الخرج الظاهر في معرفة سبب الفشل وتصحيح الخطأ الحاصل، ونلاحظ عدم وجود علامة بجانب اسم الاختبار لأنه فشل، وأيضًا لم تعد خلاصة تنفيذ عملية الاختبار في الأسفل بل في الأعلى بعد قائمة الاختبارات المنفذة وحالتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_81" style="">
<span class="pun">...</span><span class="pln">
</span><span class="lit">0</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">29ms</span><span class="pun">)</span><span class="pln">
  </span><span class="lit">1</span><span class="pln"> failing
</span><span class="pun">...</span></pre>

<p>
	والخرج الباقي يظهر بيانات متعلقة بالاختبارات الفاشلة، حيث يظهر أولًا الاختبارات التي فشلت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_83" style="">
<span class="pun">...</span><span class="pln">
</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> integrated test
       should be able to add and complete </span><span class="typ">TODOs</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ثم سبب فشل تلك الاختبارات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_85" style="">
<span class="pun">...</span><span class="pln">
      </span><span class="typ">AssertionError</span><span class="pln"> </span><span class="pun">[</span><span class="pln">ERR_ASSERTION</span><span class="pun">]:</span><span class="pln"> </span><span class="typ">Input</span><span class="pln"> A expected to strictly equal input B</span><span class="pun">:</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> expected </span><span class="pun">-</span><span class="pln"> actual

</span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
      </span><span class="pun">+</span><span class="pln"> expected </span><span class="pun">-</span><span class="pln"> actual

      </span><span class="pun">-</span><span class="lit">2</span><span class="pln">
      </span><span class="pun">+</span><span class="lit">0</span><span class="pln">

      at </span><span class="typ">Context</span><span class="pun">.</span><span class="pln"> </span><span class="pun">(</span><span class="pln">index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">9</span><span class="pun">:</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	رُمي خطأ من النوع <code>AssertionError</code> عندما فشل اختبار التابع <code>strictEqual()‎</code>، حيث نلاحظ أن القيمة المتوقعة وهي <code>0</code> مختلفة عن القيمة الحقيقية لطول مصفوفة المهام وهي <code>2</code>، ونلاحظ ذكر السطر الذي فشل عنده الاختبار ضمن ملف الاختبار وهو السطر رقم 10، وتفيد هذه المعلومات في حل المشكلة.
</p>

<p>
	نعدل الاختبار ونصحح المشكلة بتوقع القيمة الصحيحة لطول المصفوفة حتى لا يفشل الاختبار، وأولًا نفتح ملف الاختبارات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_87" style="">
<span class="pln">nano index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ثم نزيل أسطر إضافة المهام باستخدام <code>todos.add</code> ليصبح الاختبار كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_89" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نحفظ الملف ونخرج منه ونعيد تنفيذ الاختبار مجددًا ونتأكد من نجاحه في حالة صحيحة هذه المرة وليست مغلوطة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_91" style="">
<span class="pln">npm test</span></pre>

<p>
	نحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_93" style="">
<span class="pun">...</span><span class="pln">
integration test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">


  </span><span class="lit">1</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">15ms</span><span class="pun">)</span></pre>

<p>
	أصبح الاختبار الآن أقرب لما نريد، لنعود إلى اختبار التكامل مجددًا ونحاول اختبار إضافة مهمة جديدة ضمن الملف index.test.js كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_96" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"run code"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">deepStrictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">(),</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"run code"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">}]);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بعد استدعاء التابع <code>add()‎</code> نتحقق من وجود مهمة واحدة ضمن كائن مدير المهام <code>todos</code> باستخدام تابع التوكيد <code>strictEqual()‎</code>، وأما الاختبار التالي فسيتحقق من البيانات الموجودة ضمن قائمة المهام <code>todos</code> بواسطة التابع <code>deepStrictEqual()‎</code> والذي يختبر مساواة القيمة المتوقعة مع القيمة الحقيقية تعاوديًا بالمرور على كل الخصائص ضمن من القيمتين واختبار مساواتهما، ففي حالتنا سيختبر أن المصفوفتين يملك كل منها كائنًا واحدًا داخلها، ويتحقق من امتلاك كلا الكائنين لنفس الخواص وتساويها ففي حالتنا يجب أن يكون هنالك خاصيتين الأولى العنوان <code>title</code> ويجب أن تساوي قيمتها <code>"run code"</code>والثاني اكتمال المهمة <code>completed</code> وقيمتها تساوي <code>false</code>.
</p>

<p>
	نكمل كتابة الاختبار ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_98" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"integration test"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should be able to add and complete TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"run code"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">deepStrictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">(),</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"run code"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">}]);</span><span class="pln">

        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"test everything"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">().</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">deepStrictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">(),</span><span class="pln">
            </span><span class="pun">[</span><span class="pln">
                </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"run code"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"test everything"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">]</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">

        todos</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">(</span><span class="str">"run code"</span><span class="pun">);</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">deepStrictEqual</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">list</span><span class="pun">(),</span><span class="pln">
            </span><span class="pun">[</span><span class="pln">
                </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"run code"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
                </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"test everything"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">]</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	أصبح الاختبار الآن مماثل تمامًا للاختبار اليدوي الذي نفذناها سابقًا، ولم نعد بحاجة للتحقق من الخرج يدويًا في كل مرة، فيكفي تنفيذ هذا الاختبار والتأكد من نجاحه ليدل على صحة عمل الوحدة البرمجية، حيث الهدف من الاختبار في النهاية التأكد من سلامة عمل الوحدة البرمجية كلها.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذ الاختبارات مرة أخرى ونتحقق من النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_100" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">


  </span><span class="lit">1</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">9ms</span><span class="pun">)</span></pre>

<p>
	أعددنا بذلك اختبار تكامل باستخدام إطار الاختبارات موكا Mocha والوحدة <code>assert</code>.
</p>

<p>
	والآن لنتخيل بأننا شاركنا الوحدة البرمجية السابقة مع مطورين آخرين وأخبرنا العديد منهم بأنه يفضل رمي خطأ عند استدعاء التابع <code>complete()‎</code> في حال لم يتم إضافة أي مهام بعد سابقًا، لذا لنضيف تلك الخاصية ضمن التابع <code>complete()‎</code> ضمن الملف index.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_102" style="">
<span class="pun">...</span><span class="pln">
complete</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"You have no TODOs stored. Why don't you add one first?"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    let todoFound </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">todo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">title </span><span class="pun">===</span><span class="pln"> title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            todo</span><span class="pun">.</span><span class="pln">completed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
            todoFound </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">todoFound</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">No</span><span class="pln"> TODO was found </span><span class="kwd">with</span><span class="pln"> the title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"${title}"</span><span class="pun">`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نحفظ الملف ونخرج منه ثم نضيف اختبارًا جديدًا لتلك الميزة في ملف الاختبارات، حيث نريد التحقق من أن استدعاء التابع <code>complete</code> من كائن لا يحوي أي مهام بعد سيعيد الخطأ الخاص بحالتنا، لذا نعود لملف الاختبار ونضيف في نهايته الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_104" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"complete()"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should fail if there are no TODOs"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> expectedError </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"You have no TODOs stored. Why don't you add one first?"</span><span class="pun">);</span><span class="pln">

        assert</span><span class="pun">.</span><span class="pln">throws</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            todos</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">(</span><span class="str">"doesn't exist"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">},</span><span class="pln"> expectedError</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	استخدما التوابع <code>describe()‎</code> و <code>it()‎</code> كما فعلنا سابقًا، وبدأنا الاختبار بإنشاء كائن <code>todos</code> جديد، ثم عرّفنا الخطأ المتوقع عند استدعاء التابع <code>complete()‎</code> واستخدمنا تابع توكيد رمي الأخطاء <code>throws()‎</code> الذي توفره الوحدة <code>assert</code> لاختبار الأخطاء المرمية من قبل الشيفرة عند تنفيذها، حيث نمرر له كمعامل أول تابعًا يحتوي داخله على التابع الذي نتوقع منه رمي الخطأ، والمعامل الثاني هو الخطأ المتوقع رميه، والآن ننفذ أمر الاختبار <code>npm test</code> ونعاين النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_106" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">

  complete</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should fail </span><span class="kwd">if</span><span class="pln"> there are no </span><span class="typ">TODOs</span><span class="pln">


  </span><span class="lit">2</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">25ms</span><span class="pun">)</span></pre>

<p>
	يتضح من الخرج السابق أهمية أتمتة الاختبارات باستخدام موكا والوحدة <code>assert</code>، حيث أنه وعند كل تنفيذ لأمر الاختبار <code>npm test</code> سيتم التحقق من نجاح كل الاختبارات السابقة، ولا حاجة لتكرار التحقق اليدوي أبدًا طالما أن الشيفرات الأخرى لا زالت تعمل وهذا ما تأكدنا منه عند نجاح بقية الاختبارات.
</p>

<p>
	وإلى الآن كل ما اختبرناه كان عبارة عن شيفرات متزامنة، وفي الفقرة التالية سنتعلم طرق اختبار والتعامل مع الشيفرات اللامتزامنة.
</p>

<h2>
	اختبار الشيفرات اللامتزامنة
</h2>

<p>
	سنضيف الآن ميزة تصدير قائمة المهام إلى ملف بصيغة CSV التي ذكرناها سابقًا، حيث سيحوي ذلك الملف كل المهام المخزنة مع تفاصيل حالة اكتمالها، لذا وللتعامل مع نظام الملفات سنحتاج لاستخدام وحدة <code>fs</code> التي توفرها نود لكتابة ذلك الملف.
</p>

<p>
	والجدير بالذكرأن عملية كتابة الملف عملية غير متزامنة ويمكن تنفيذها بعدة طرق كاستخدام دوال رد النداء callbacks مثلًا أو الوعود Promises أو عبر اللاتزامن والانتظار <code>async/await</code> كما رأينا في المقال السابق.
</p>

<p>
	سنتعلم في هذه الفقرة كيف يمكن كتابة الاختبارات للشيفرات اللامتزامنة التي تستخدم أي طريقة من تلك الطرق.
</p>

<h3>
	الاختبار باستخدام دوال رد النداء
</h3>

<p>
	تمُرر دالة رد النداء كمعامل إلى التابع اللامتزامن لتُستدعى عند انتهاء مهمة ذلك التابع، لنبدأ بإضافة التابع <code>saveToFile()‎</code> للصنف <code>Todos</code> والذي سيمر على عناصر المهام ضمن الصنف ويبنى منها سلسلة نصية ويخزنها ضمن ملف بصيغة CSV، لذا نعود إلى ملف index.js ونضيف الشيفرات المكتوبة في نهايته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_108" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    list</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[...</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    add</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todo </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            title</span><span class="pun">:</span><span class="pln"> title</span><span class="pun">,</span><span class="pln">
            completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">todo</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    complete</span><span class="pun">(</span><span class="pln">title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"You have no TODOs stored. Why don't you add one first?"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        let todoFound </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">todo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">title </span><span class="pun">===</span><span class="pln"> title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                todo</span><span class="pun">.</span><span class="pln">completed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
                todoFound </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
                </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">todoFound</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">No</span><span class="pln"> TODO was found </span><span class="kwd">with</span><span class="pln"> the title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"${title}"</span><span class="pun">`);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    saveToFile</span><span class="pun">(</span><span class="pln">callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let fileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Title,Completed\n'</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">todo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            fileContents </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">title</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">\n</span><span class="pun">`</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">,</span><span class="pln"> fileContents</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">;</span></pre>

<p>
	بداية نستورد الوحدة <code>fs</code> ثم نضيف التابع الجديد <code>saveToFile()‎</code> إلى الصنف والذي يقبل كمعامل له دالة رد نداء تُستدعَى عند انتهاء عملية كتابة الملف، ونُنشئ ضمن التابع الجديد محتوى الملف ونخزنه ضمن المتغير <code>fileContents</code>، ونلاحظ كيف عيّنا القيمة الابتدائية له وهي عناوين الأعمدة للجدول في ملف CSV، ومررنا على كل مهمة مخزنة ضمن المصفوفة باستخدام التابع <code>forEach()‎</code> وأضفنا لكل مهمة قيمة خاصية العنوان لها <code>title</code> وحالة الاكتمال <code>completed</code>، ثم استدعينا التابع <code>writeFile()‎</code> من وحدة <code>fs</code> لكتابة الملف النهائي، ومررنا له اسم الملف الناتج todos.csv وكمعامل ثانِ مررنا محتوى ذلك الملف وهو قيمة المتغير <code>fileContents</code> السابق، وآخر معامل هو دالة رد النداء لمعالجة الخطأ الذي قد يحدث عند تنفيذ هذه العملية.
</p>

<p>
	والآن نحفظ الملف ونخرج منه ونكتب اختبارًا للتابع الجديد <code>saveToFile()‎</code> يتحقق من وجود الملف المصدَّر ثم يتحقق من صحة محتواه، لذا نعود لملف الاختبار index.test.js ونبدأ بتحميل الوحدة <code>fs</code> في بداية الملف والتي سنستخدمها في عملية الاختبار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_110" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Todos</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./index'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> assert </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'assert'</span><span class="pun">).</span><span class="pln">strict</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	ونضيف حالة الاختبار الجديدة في نهاية الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_112" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"saveToFile()"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should save a single TODO"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">done</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">((</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
            let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,false\n"</span><span class="pun">;</span><span class="pln">
            let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
            assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
            done</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وبما أن هذا اختبار لميزة جديدة كليًا عن سابقاتها استخدمنا الدالة <code>describe()‎</code> لتعريف مجموعة اختبارات جديدة متعلقة بهذه الميزة، ونلاحظ هذه المرة استخدام الدالة <code>it()‎</code> بطريقة مختلفة، حيث نمرر لها عادةً دالة رد نداء تحوي داخلها الاختبار دون تمرير أي معامل لها، ولكن هذه المرة سنمرر لدالة رد النداء المعامل <code>done</code> والذي نحتاج إليه عند تنفيذ اختبار شيفرات لا متزامنة وخصوصًا التي تستخدم في عملها دوال رد النداء، حيث نستخدم دالة رد النداء <code>done()‎</code> لإعلام موكا عند الانتهاء من اختبار عملية غير متزامنة، لهذا يجب علينا التأكد بعد اختبار دوال رد النداء استدعاء <code>done()‎</code> حيث بدون ذلك الاستدعاء لن يعلم موكا أن الاختبار قد انتهى وسيبقى منتظرًا إشارة الانتهاء تلك.
</p>

<p>
	ونلاحظ أننا أنشأنا كائنًا جديدًا من الصنف <code>Todos</code> وأضفنا مهمة جديدة له بعدها استدعينا التابع الجديد <code>saveToFile()‎</code> ومررنا له دالة لفحص كائن الخطأ الذي سيمرر لها إن وجد، ونلاحظ كيف وضعنا الاختبار لهذا التابع ضمن دالة رد النداء، لأن الاختبار سيفشل إن أجري قبل عملية كتابة الملف.
</p>

<p>
	أول توكيد تحققنا منه هو أن الملف todos.csv موجود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_114" style="">
<span class="pun">...</span><span class="pln">
assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	حيث يعيد التابع <code>fs.existsSync()‎</code> القيمة الصحيحة <code>true</code> إذا كان الملف المحدد بالمسار الممر له موجودًا وإلا سيعيد قيمة خاطئة <code>false</code>.
</p>

<p>
	<strong>ملاحظة</strong>: يرجع العمل أن توابع الوحدة <code>fs</code> غير متزامنة افتراضيًا، ويوجد لبعض التوابع الأساسية منها نسخ متزامنة استخدمناها هنا لتبسيط الاختبار ويمكننا الاستدلال على تلك التوابع المتزامنة من اللاحقة <code>"Sync"</code> في نهاية اسمها، فلو استخدمنا النسخة المتزامنة ومررنا لها دالة رد نداء أيضًا فستصبح الشيفرة متداخلة وصعبة القراءة أو التعديل.
</p>

<p>
	أنشأنا بعد ذلك متغيرًا يحوي القيمة المتوقعة للملف todos.csv:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_116" style="">
<span class="pun">...</span><span class="pln">
let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,false\n"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	واستخدمنا التابع المتزامن <code>readFileSync()‎</code> من الوحدة <code>fs</code> لقراءة محتوى الملف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_118" style="">
<span class="pun">...</span><span class="pln">
let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	حيث مررنا للتابع <code>readFileSync()‎</code> مسار الملف todos.csv الذي جرى تصديره، وسيعيد لنا كائن تخزين مؤقت <code>Buffer</code> سيحوي بيانات الملف بالصيغة الثنائية، لذا نستدعي التابع <code>toString()‎</code> منه للحصول على القيمة النصية المقروءة لتلك البيانات لمقارنتها مع القيمة المتوقعة لمحتوى الملف التي أنشأناها مسبقًا، ونستخدم لمقارنتهما تابع اختبار المساواة <code>strictEqual</code> من الوحدة <code>assert</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_120" style="">
<span class="pun">...</span><span class="pln">
assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	وأخيرًا نستدعي التابع <code>done()‎</code> لإعلام موكا بانتهاء الاختبار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_122" style="">
<span class="pun">...</span><span class="pln">
done</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نلاحظ كيف مررنا كائن الخطأ <code>err</code> عند استدعاء تابع الانتهاء <code>done()‎</code> حيث سيفحص موكا تلك القيمة وسيفشل الاختبار إن احتوت على خطأ.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذ الاختبارات بتنفيذ التابع <code>npm test</code> كما العادة ونلاحظ النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_125" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">

  complete</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should fail </span><span class="kwd">if</span><span class="pln"> there are no </span><span class="typ">TODOs</span><span class="pln">

  saveToFile</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO


  </span><span class="lit">3</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">15ms</span><span class="pun">)</span></pre>

<p>
	بذلك نكون قد اختبرنا تابعًا غير متزامنًا يستخدم دالة رد النداء، وبما أن تلك الطريقة لم تعد مستخدمة كثيرًا في وقتنا الحالي وتم استبدالها باستخدام الوعود كما شرحنا في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%BA%D9%8A%D8%B1-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-nodejs-r1743/" rel="">المقال السابق</a> من هذه السلسلة، سنتعلم في الفقرة القادمة كيف يمكن اختبار الشيفرات التي تستخدم الوعود في تنفيذ عملياتها اللامتزامنة باستخدام موكا.
</p>

<h3>
	الاختبار باستخدام الوعود
</h3>

<p>
	<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/" rel="">الوعد Promise</a> هو كائن توفره جافاسكربت وظيفته إرجاع قيمة ما لاحقًا، وعندما تنفذ عمليته بنجاح نقول <a href="https://wiki.hsoub.com/JavaScript/Promise/resolve" rel="external">تحقق ذلك الوعد resolved</a>، وفي حال حدث خطأ في تنفيذ عمليته نقول أنه قد <a href="https://wiki.hsoub.com/JavaScript/Promise/reject" rel="external">فشل rejected</a>.
</p>

<p>
	لنبدأ بتعديل التابع <code>saveToFile()‎</code> ليستخدم الوعود بدلًا من دوال رد النداء، نفتح ملف index.js ونبدأ بتعديل طريقة استيراد الوحدة <code>fs</code>، حيث نعدل على عبارة الاستيراد باستخدام <code>require()‎</code> لتصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_128" style="">
<span class="pun">...</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	بذلك نكون قد استوردنا وحدة <code>fs</code> التي تستخدم الوعود بدلًا من التي تستخدم دوال رد النداء، ثم نعدل التابع <code>saveToFile()‎</code> ليستخدم الوعود بشكل سليم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_130" style="">
<span class="pun">...</span><span class="pln">
saveToFile</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let fileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Title,Completed\n'</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">todo</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        fileContents </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">title</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">todo</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">\n</span><span class="pun">`</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">,</span><span class="pln"> fileContents</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نلاحظ أن التابع لم يعد يقبل معاملًا له، حيث يغنينا استخدام الوعود عن ذلك، ونلاحظ أيضًا تغيير طريقة كتابة التابع حيث نرجع منه الوعد الذي يرجعه التابع <code>writeFile()‎</code>.
</p>

<p>
	والآن نحفظ التغييرات على ملف index.js ثم نعدل على اختبار هذا التابع ليلائم استخدامه للوعود، لذا نعود لملف الاختبار index.test.js ونبدل اختبار التابع <code>saveToFile()‎</code> ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_132" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"saveToFile()"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should save a single TODO"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
            let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,false\n"</span><span class="pun">;</span><span class="pln">
            let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
            assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	أول تعديل أجريناه هو إزالة معامل تابع الانتهاء <code>done()‎</code> لأن بقاءه يعني انتظار موكا إشارة استدعائه حتى ينهي الاختبار وإلا سيرمي خطأ كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_134" style="">
<span class="lit">1</span><span class="pun">)</span><span class="pln"> saveToFile</span><span class="pun">()</span><span class="pln">
       should save a single TODO</span><span class="pun">:</span><span class="pln">
     </span><span class="typ">Error</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Timeout</span><span class="pln"> of </span><span class="lit">2000ms</span><span class="pln"> exceeded</span><span class="pun">.</span><span class="pln"> </span><span class="typ">For</span><span class="pln"> async tests and hooks</span><span class="pun">,</span><span class="pln"> ensure </span><span class="str">"done()"</span><span class="pln"> is called</span><span class="pun">;</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> returning a </span><span class="typ">Promise</span><span class="pun">,</span><span class="pln"> ensure it resolves</span><span class="pun">.</span><span class="pln"> </span><span class="pun">(</span><span class="str">/home/</span><span class="pln">ubuntu</span><span class="pun">/</span><span class="pln">todos</span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js</span><span class="pun">)</span><span class="pln">
      at listOnTimeout </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">timers</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">536</span><span class="pun">:</span><span class="lit">17</span><span class="pun">)</span><span class="pln">
      at processTimers </span><span class="pun">(</span><span class="pln">internal</span><span class="pun">/</span><span class="pln">timers</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">480</span><span class="pun">:</span><span class="lit">7</span><span class="pun">)</span></pre>

<p>
	لهذا السبب عندما نستخدم الوعود ضمن الاختبار لا نمرر المعامل <code>done()‎</code> إلى دالة رد النداء المُمررة لدالة تعريف الاختبار <code>it()‎</code>.
</p>

<p>
	ولاختبار الوعد نضع اختبارات التوكيدات ضمن استدعاء التابع <code>then()‎</code>، ونلاحظ كيف أننا نرجع الوعد من داخل تابع الاختبار وأننا لا نضيف استدعاء للتابع <code>catch()‎</code> إليه لالتقاط الخطأ الذي قد يُرمى أثناء التنفيذ، وذلك حتى تصل أي أخطاء ترمى من داخل التابع <code>then()‎</code> إلى الدالة الأعلى وتحديدًا إلى <code>it()‎</code>، حتى يعلم موكا بحدوث أخطاء أثناء التنفيذ وإفشال الاختبار الحالي، لذلك ولاختبار الوعود يجب أن نعيد الوعد المراد اختباره باستخدام <code>return</code>، وإلا سيظهر الاختبار على أنه ناجح حتى عند فشله في الحقيقة، ونحصل على نتيجة صحة مغلوطة، وأيضًا نتجاهل إضافة التابع <code>catch()‎</code> لأن موكا يتحقق من الأخطاء المرمية بنفسه للتأكد من حالة فشل الوعد الذي يجب أن يؤدي بالمقابل إلى فشل الاختبار الذي يعطينا فكرة عن وجود مشكلة في عمل وحدة التطبيق.
</p>

<p>
	والآن وبعد أن عدلنا الاختبار نحفظ الملف ونخرج منه، وننفذ الأمر <code>npm test</code> لتنفيذ الاختبارات والتأكد من نجاحها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_137" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">

  complete</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should fail </span><span class="kwd">if</span><span class="pln"> there are no </span><span class="typ">TODOs</span><span class="pln">

  saveToFile</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO


  </span><span class="lit">3</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">18ms</span><span class="pun">)</span></pre>

<p>
	بذلك نكون قد عدلنا على الشيفرة والاختبار المتعلق بها لتستخدم الوعود، وتأكدنا من أن الميزة لا زالت تعمل بشكل صحيح، والآن بدلًا من التعامل مع الوعود بتلك الطريقة سنستخدم في الفقرة التالية اللاتزامن والانتظار <code>async/await</code> لتبسيط العملية وإلغاء الحاجة لاستدعاء التابع <code>then()‎</code> أكثر من مرة لمعالجة حالات نجاح التنفيذ ولتبسيط شيفرة الاختبار وتوضيحها.
</p>

<h3>
	الاختبار باستخدام اللاتزامن والانتظار async/await
</h3>

<p>
	تتيح الكلمتان المفتاحيتان <code>async/await</code> صيغة بديلة للتعامل مع الوعود، فعند تحديد تابع ما كتابع لا متزامن باستخدام الكلمة المفتاحية <code>async</code> يصبح بإمكاننا الحصول داخله مباشرةً على قيمة نتيجة أي وعد ننفذه عند نجاحه باستخدام الكلمة المفتاحية <code>await</code> قبل استدعاء الوعد، وبذلك نلغي الحاجة لاستدعاء التوابع <code>then()‎</code> أو <code>catch()‎</code> نهائيًا، وباستخدامها يمكننا تبسيط اختبار التابع <code>saveToFile()‎</code> الذي يستخدم الوعود، لذا نعدله ضمن ملف الاختبارات index.test.js ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_139" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"saveToFile()"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should save a single TODO"</span><span class="pun">,</span><span class="pln"> async </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        await todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">();</span><span class="pln">

        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
        let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,false\n"</span><span class="pun">;</span><span class="pln">
        let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نلاحظ كيف أضفنا الكلمة <code>async</code> قبل تعريف دالة رد النداء المُمرر إلى <code>it()‎</code>، ما يسمح لنا باستخدام الكلمة <code>await</code> داخلها، ونلاحظ عند استدعاء التابع <code>saveToFile()‎</code> إضافة الكلمة <code>await</code> قبل استدعائه بذلك لن يكمل نود تنفيذ الشيفرات في الأسطر اللاحقة وسينتظر لحين انتهاء تنفيذ هذا التابع، ونلاحظ أيضًا كيف أصبحت شيفرة الاختبار أسهل في القراءة بعد أن نقلنا شيفرات التوكيد من داخل التابع <code>then()‎</code> مباشرة إلى جسم تابع الاختبار المُمرر إلى <code>it()‎</code>.
</p>

<p>
	والآن ننفذ الاختبارات بتنفيذ الأمر <code>npm test</code> لنحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_141" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">

  complete</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should fail </span><span class="kwd">if</span><span class="pln"> there are no </span><span class="typ">TODOs</span><span class="pln">

  saveToFile</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO


  </span><span class="lit">3</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">30ms</span><span class="pun">)</span></pre>

<p>
	بذلك نكون قد تعلمنا كيف يمكن اختبار التوابع اللامتزامنة مهما كان شكلها، سواء كانت تستخدم دوال رد النداء في عملها أو الوعود، وتعلمنا الكثير عن إطار عمل الاختبار موكا Mocha وكيفية استخدامه لاختبار التوابع اللامتزامنة، وسنتعرف في الفقرة التالية على خصائص أخرى يوفرها موكا Mocha ستحسن من طريقة كتابة الاختبارات، وسنتعرف تحديدًا على الخطافات hooks وكيف يمكنها التعديل على بيئة الاختبار.
</p>

<h2>
	تحسين الاختبارات باستخدام الخطافات Hooks
</h2>

<p>
	تسمح الخطافات في موكا Mocha بإعداد بيئة الاختبار قبل وبعد تنفيذ الاختبارات، حيث نستخدمها داخل التابع <code>describe()‎</code> عادةً وتحوي على شيفرات تفيد في عملية الإعداد والتنظيف التي قد تحتاجها بعض الاختبارات، حيث يوفر موكا أربع خطافات رئيسية وهي:
</p>

<ul>
<li>
		<code>before</code>: يُنفذ مرة واحدة قبل أول اختبار فقط.
	</li>
	<li>
		<code>beforeEach</code>: يُنفذ قبل كل اختبار.
	</li>
	<li>
		<code>after</code>: يُنفذ بعد تنفيذ آخر اختبار فقط.
	</li>
	<li>
		<code>afterEach</code>: يُنفذ بعد كل اختبار.
	</li>
</ul>
<p>
	تفيد تلك الخطافات عند اختبار تابع ما ضمن عدة اختبارات، وتسمح بفصل شيفرة الإعداد لها إلى مكان واحد منفصل عن مكان شيفرات التوكيد، كإنشاء الكائن <code>todos</code> في حالتنا مثلًا، ولنختبر فائدتها سنبدأ أولًا بإضافة اختبارات جديدة لمجموعة اختبارات التابع <code>saveToFile()‎</code>، فبعد أن تحققنا في الاختبار الماضي من صحة تصدير ملف المهام إلا أننا اختبرنا وجود مهمة واحدة فقط ضمنه، ولم نختبر الحالة التي تكون فيها المهمة مكتملة وهل سيتم حفظها ضمن الملف بشكل سليم أم لا، لذلك سنضيف اختبارات جديدة للتأكد من تلك الحالات وبالتالي التأكد من صحة عمل الوحدة البرمجية التي نطورها.
</p>

<p>
	لنبدأ بإضافة اختبار ثانِ للتأكد من حفظ المهام المكتملة بشكل سليم، لذا نفتح الملف index.test.js ضمن محرر النصوص ونضيف الاختبار الجديد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_144" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"saveToFile()"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">"should save a single TODO"</span><span class="pun">,</span><span class="pln"> async </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        await todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">();</span><span class="pln">

        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
        let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,false\n"</span><span class="pun">;</span><span class="pln">
        let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    it</span><span class="pun">(</span><span class="str">"should save a single TODO that's completed"</span><span class="pun">,</span><span class="pln"> async </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        todos</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        await todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">();</span><span class="pln">

        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
        let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,true\n"</span><span class="pun">;</span><span class="pln">
        let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يشبه هذا الاختبار ما سبقه، والفرق الوحيد هو استدعاء التابع <code>complete()‎</code> قبل تصدير الملف باستخدام التابع <code>saveToFile()‎</code>، وأيضًا اختلاف محتوى الملف المتوقع ضمن المتغير <code>expectedFileContents</code> حيث يحوي القيمة <code>true</code> بدلًا من <code>false</code> عند حقل حالة الاكتمال للمهمة <code>completed</code>.
</p>

<p>
	والآن نحفظ الملف ونخرج منه وننفذ الاختبارات بتنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_146" style="">
<span class="pln">npm test</span></pre>

<p>
	سيظهر لنا التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_148" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">

  complete</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should fail </span><span class="kwd">if</span><span class="pln"> there are no </span><span class="typ">TODOs</span><span class="pln">

  saveToFile</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO that</span><span class="str">'</span><span class="pln">s completed


  </span><span class="lit">4</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">26ms</span><span class="pun">)</span></pre>

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

<p>
	وقد يظن المستخدم لهذه الوحدة خطأً أن هذا الملف هو ملف مهام حقيقية وليس ملف ناتج عن عملية الاختبار، ولحل تلك المشكلة يمكننا حذف الملفات الناتجة بعد انتهاء الاختبار مباشرةً باستخدام الخطافات تلك، حيث نستفيد من الخطاف <code>beforeEach()‎</code> لإعداد المهام قبل اختبارها، وهنا ضمن هذا الخطاف نُعد ونحضر عادةً أي بيانات سنستخدمها داخل الاختبارات، ففي حالتنا نريد إنشاء الكائن <code>todos</code> وبداخله مهمة جديدة نجهزها ونضيفها مسبقًا، وسنستفيد من الخطاف <code>afterEach()‎</code> لحذف الملفات الناتجة بعد كل اختبار، لذلك نعدل مجموعة اختبارات التابع <code>saveToFile()‎</code> ضمن ملف الاختبارات index.test.js ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_150" style="">
<span class="pun">...</span><span class="pln">
describe</span><span class="pun">(</span><span class="str">"saveToFile()"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    beforeEach</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    afterEach</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            fs</span><span class="pun">.</span><span class="pln">unlinkSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    it</span><span class="pun">(</span><span class="str">"should save a single TODO without error"</span><span class="pun">,</span><span class="pln"> async </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        await </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">();</span><span class="pln">

        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
        let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,false\n"</span><span class="pun">;</span><span class="pln">
        let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    it</span><span class="pun">(</span><span class="str">"should save a single TODO that's completed"</span><span class="pun">,</span><span class="pln"> async </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">complete</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
        await </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">saveToFile</span><span class="pun">();</span><span class="pln">

        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">'todos.csv'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
        let expectedFileContents </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Title,Completed\nsave a CSV,true\n"</span><span class="pun">;</span><span class="pln">
        let content </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
        assert</span><span class="pun">.</span><span class="pln">strictEqual</span><span class="pun">(</span><span class="pln">content</span><span class="pun">,</span><span class="pln"> expectedFileContents</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نلاحظ إضافة الخطاف <code>beforeEach()‎</code> داخل مجموعة الاختبار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_152" style="">
<span class="pun">...</span><span class="pln">
beforeEach</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Todos</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">todos</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"save a CSV"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	حيث أنشأنا كائنًا جديدًا من الصنف <code>Todos</code> سيكون متاحًا لكل الاختبارات ضمن هذه المجموعة، وذلك لأن موكا سيشارك قيمة الكائن <code>this</code> الذي أضفنا له خصائص ضمن الخطاف <code>beforeEach()‎</code> مع جميع الاختبارات في توابع الاختبار <code>it()‎</code>، وقيمته ستكون واحدة ضمن مجموعة الاختبارات داخل <code>describe()‎</code>، حيث بالاستفادة من تلك الميزة يمكننا مشاركة بيانات مُعدة مسبقًا مع جميع الاختبارات.
</p>

<p>
	أما داخل الخطاف <code>afterEach()‎</code>، فقد حذفنا ملف CSV الناتج عن الاختبارات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_154" style="">
<span class="pun">...</span><span class="pln">
afterEach</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        fs</span><span class="pun">.</span><span class="pln">unlinkSync</span><span class="pun">(</span><span class="str">"todos.csv"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	في حال فشلت الاختبارات فلن يُنشأ ذلك الملف، لهذا السبب نختبر أولًا وجوده باستخدام التابع <code>existsSync()‎</code> قبل تنفيذ عملية الحذف باستخدام التابع <code>unlinkSync()‎</code>، ثم بدلنا في باقي الاختبارات الإشارة إلى كائن المهام <code>todos</code> الذي كنا نُنشئه ضمن <code>it()‎</code> مباشرةً، ليشير إلى الكائن الذي أعددناه ضمن الخطاف عن طريق <code>this.todos</code>، وحذفنا أسطر إنشاء الكائن <code>todos</code> ضمن تلك الاختبارات.
</p>

<p>
	والآن لننفذ تلك الاختبارات بعد التعديلات ونتأكد من نتيجتها بتنفيذ الأمر <code>npm test</code> لنحصل على التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1184_156" style="">
<span class="pun">...</span><span class="pln">
integrated test
    </span><span class="pun">✓</span><span class="pln"> should be able to add and complete </span><span class="typ">TODOs</span><span class="pln">

  complete</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should fail </span><span class="kwd">if</span><span class="pln"> there are no </span><span class="typ">TODOs</span><span class="pln">

  saveToFile</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO without error
    </span><span class="pun">✓</span><span class="pln"> should save a single TODO that</span><span class="str">'</span><span class="pln">s completed


  </span><span class="lit">4</span><span class="pln"> passing </span><span class="pun">(</span><span class="lit">20ms</span><span class="pun">)</span></pre>

<p>
	نلاحظ أنه لا تغيير في نتائج الاختبار وجميعها نجحت، وأصبحت اختبارات التابع <code>saveToFile()‎</code> أبسط وأسرع بسبب مشاركة الكائن مع جميع الاختبارات، وحللنا مشكلة ملف CSV الناتج عن تنفيذ الاختبارات.
</p>

<h2>
	خاتمة
</h2>

<p>
	كتبنا في هذا المقال وحدة برمجية لإدارة المهام في نود، واختبرنا عملها يدويًا في البداية داخل حلقة REPL التفاعلية، ثم أنشأنا ملف اختبار واستخدمنا إطار عمل الاختبارات موكا Mocha لأتمتة تنفيذ جميع الاختبارات على تلك الوحدة، واستخدمنا الوحدة <code>assert</code> للتوكيد والتحقق من عمل الوحدة التي طورناها، وتعلمنا كيف يمكن اختبار التوابع المتزامنة واللامتزامنة في موكا Mocha، واستعنا أخيرًا بالخطافات لتبسيط كتابة الاختبارات المرتبطة ببعضها وتسهيل قراءتها وزيادة سهولة التعديل عليها لاحقًا.
</p>

<p>
	والآن حاول عند تطوير برنامجك التالي كتابة الاختبارات لمزاياه، أو يمكنك البدء بكتابة الاختبارات له أولًا من خلال تحديد الدخل والخرج المتوقع من التوابع التي ستكتبها وكتابة اختبار لها على هذا الأساس ثم ابدأ ببنائها.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-test-a-node-js-module-with-mocha-and-assert" rel="external nofollow">How To Test a Node.js Module with Mocha and Assert</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-modules-%D9%81%D9%8A-nodejs-r1742/" rel="">إنشاء وحدات برمجية Modules في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">إدارة الوحدات البرمجية في Node.js باستخدام npm وملف package.json</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1744</guid><pubDate>Sat, 08 Oct 2022 06:54:40 +0000</pubDate></item><item><title>&#x637;&#x631;&#x642; &#x643;&#x62A;&#x627;&#x628;&#x629; &#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; &#x63A;&#x64A;&#x631; &#x645;&#x62A;&#x632;&#x627;&#x645;&#x646;&#x629; &#x627;&#x644;&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%B7%D8%B1%D9%82-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%BA%D9%8A%D8%B1-%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D9%81%D9%8A-nodejs-r1743/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/6341149b5e1f1_---Node--js.png.bf630af3fb6b6d45ad96730944e72181.png" /></p>

<p>
	البرمجة المتزامنة synchronous programming في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> تعني تنفيذ التعليمات في الأسطر البرمجية سطرًا تلو الآخر بحسب ترتيب كتابتها تمامًا، ولكن لا حاجة للالتزام بترتيب التنفيذ هذا دومًا، فمثلًا عند إرسال طلب عبر الشبكة ستضطر الإجرائية التي يُنفذ فيها البرنامج إلى انتظار رد ذلك الطلب ووصول جوابه قبل أن نتمكن من إكمال تنفيذ باقي البرنامج، حيث وقت انتظار إتمام الطلب هذا هو وقت مهدور، هنا يأتي دور البرمجة اللامتزامنة asynchronous programming لتحل هذه المشكلة، حيث تُنفذ فيها الأسطر البرمجية للبرنامج بترتيب مختلف عن ترتيب كتابتها الأصلي، فيصبح بإمكاننا مثلًا في مثالنا السابق تنفيذ تعليمات برمجية أخرى في أثناء انتظار إتمام عملية إرسال الطلب ووصول جوابه المنتظر مع البيانات المطلوبة.
</p>

<p>
	تٌنفَذ <a href="https://academy.hsoub.com/programming/javascript/%D9%86%D9%85%D8%B7-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r785/" rel="">شيفرة جافاسكربت</a> ضمن خيط وحيد thread ضمن الإجرائية، حيث تعالج شيفراتها بشكل متزامن ضمن ذلك الخيط عبر تنفيذ تعليمة واحدة فقط في كل لحظة، ويتوضح أثر البرمجة المتزامنة في هذه الحالة أكثر، فعند تنفيذ المهام التي تحتاج لوقت كبير ضمن ذلك الخيط سيُعيق ذلك تنفيذ كل الشيفرات اللاحقة لحين انتهاء تلك المهمة، لذا وبالاستفادة من مزايا برمجة جافاسكربت اللامتزامنة يمكننا إزاحة المهام التي تأخذ وقتًا طويلًا في التنفيذ إلى خيط آخر في الخلفية وبالتالي حل المشكلة، وبعد انتهاء تلك المهمة الطويلة تُنفذ الشيفرات المتعلقة بمعالجة بياناتها ضمن الخيط الأساسي لشيفرة جافاسكربت مجددًا.
</p>

<p>
	سنتعلم في هذا المقال طرق إدارة المهام اللامتزامنة باستخدام <a href="https://academy.hsoub.com/programming/javascript/%D8%AD%D9%84%D9%82%D8%A9-%D8%A7%EF%BB%B7%D8%AD%D8%AF%D8%A7%D8%AB-event-loop-%D8%B7%D8%B1%D9%8A%D9%82%D9%83-%D9%84%D9%81%D9%87%D9%85-%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1241/" rel="">حلقة الأحداث Event Loop</a> الخاصة بجافاسكربت والتي تُنهي بواسطتها مهامًا جديدة أثناء انتظار انتهاء المهام الأخرى، ولذلك سنطور برنامجًا يستفيد من البرمجة اللامتزامنة لطلب قائمة من الأفلام من <a href="https://ghibliapi.herokuapp.com/" rel="external nofollow">الواجهة البرمجية لاستديو Ghibli</a> وحفظ بياناتها ضمن <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D9%84%D9%81%D8%A7%D8%AA-csv-r1644/" rel="">ملف CSV</a>، حيث سننفذ ذلك بثلاثة طرق وهي دوال رد النداء callback functions و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/" rel="">الوعود promises</a> وأخيرًا باستخدام <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/" rel="">اللاتزامن والانتظار async/await</a> ومع أنه من غير الشائع حاليًا استخدام دوال رد النداء في البرمجة اللامتزامنة في جافاسكربت، إلا أنه من المهم تعلم تلك الطريقة لفهم تاريخ الانتقال لاستخدام الوعود ووجودها أساسًا، ثم تأتي آلية اللاتزامن والانتظار لتسمح باستخدام الوعود بطريقة أبسط، وهي الطريقة المعتمدة حاليًا عند <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/" rel="">كتابة الشيفرات اللامتزامنة في جافاسكربت</a>.
</p>

<h2>
	المستلزمات
</h2>

<ul>
<li>
		تثبيت بيئة Node.js على الجهاز، حيث استخدمنا في هذا المقال الإصدار رقم 10.17.0، ويمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-nodejs-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r419/" rel="">تثبيت Node.js على نظام أبونتو 18.04</a> للتعرف على طريقة تثبيته.
	</li>
	<li>
		معرفة طريقة تثبيت الحزم ضمن المشروع باستخدام <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">npm</a>.
	</li>
	<li>
		معرفة طرق تعريف وتنفيذ الدوال البسيطة في جافاسكربت قبل تعلم تنفيذها بالطريقة اللامتزامنة.
	</li>
</ul>
<h2>
	حلقة الأحداث Event Loop
</h2>

<p>
	لنتعرف بدايةً على الطريقة التي ينفذ بها جافاسكربت الدوال داخليًا، ما سيسمح لنا لاحقًا بفهم أكثر عند كتابة الشيفرات اللامتزامنة وتزيد قدرتنا على استكشاف الأخطاء وتصحيحها حين حدوثها، حيث يضيف مفسر جافاسكربت كل دالة تُنفَّذ إلى مكدس الاستدعاءات call stack، وهو هيكلية بيانات شبيهة بالقائمة بحيث يمكن إضافة أو حذف العناصر منه من الأعلى فقط أي تعتمد مبدأ الداخل آخرًا يخرج أولًا LIFO -اختصارًا إلى Last in, first out- فعند إضافة عنصرين إلى المكدس مثلًا يمكن حذف آخر عنصر تمت إضافته أولًا، فمثلًا عند استدعاء الدالة <code>‎functionA()‎</code> سيُضاف ذلك إلى مكدس الاستدعاء، وإذا استدعت الدالة <code>functionA()‎</code> داخلها دالة أخرى مثلًا <code>functionB()‎</code> فسيضاف الاستدعاء الأخير لأعلى مكدس الاستدعاء، وبعد الانتهاء من تنفيذه سيُزال من أعلى مكدس الاستدعاء، أي ينفذ جافاسكربت أولًا الدالة <code>functionB()‎</code> ثم يزيلها من المكدس عند انتهائها، ثم يُنهي تنفيذ الدالة الأب <code>functionA()‎</code> ثم يزيلها أيضًا من مكدس الاستدعاء، لهذا يتم دومًا تنفيذ الدوال الأبناء أو الداخلية قبل الدوال الآباء أو الخارجية.
</p>

<p>
	عندما يُنفذ <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a> عملية لا متزامنة ككتابة البيانات إلى ملف مثلًا، فسيضيفها إلى جدول خاص ضمن الذاكرة يُخزَّن فيه العملية وشرط اكتمالها والدالة التي ستُستدعى عند اكتمالها، وبعد اكتمال العملية ستضاف تلك الدالة إلى رتل الرسائل message queue، وهو هيكلية بيانات تشبه القائمة أيضًا تُضاف إليها العناصر من الأسفل وتزال من الأعلى فقط أي تعتمد مبدأ الداخل أولًا يخرج أولًا FIFO -اختصارًا إلى First in, First out- وحين انتهاء عمليتين لا متزامنتين والتجهيز لاستدعاء الدوال الخاصة بهما سيتم استدعاء الدالة الخاصة بالعملية التي انتهت أولًا، حيث تنتظر الدوال ضمن رتل الرسائل إضافتها إلى مكدس الاستدعاء، وتبقى حلقة الأحداث في فحص دائم لمكدس الاستدعاء بانتظار فراغه، عندها يُنقل أول عنصر من رتل الرسائل إلى مكدس الاستدعاء، ويعطي جافاسكربت الأولوية للدوال ضمن رتل الرسائل بدلًا من استدعاءات الدوال الجديدة التي يفسرها ضمن الشيفرة، وبذلك تسمح تركيبة عمل مكدس الاستدعاء ورتل الرسائل وحلقة الأحداث بتنفيذ شيفرات جافاسكربت أثناء معالجة المهام اللامتزامنة.
</p>

<p>
	والآن بعد أن ألقينا نظرة عامة على حلقة الأحداث وتعرفنا فيها على طريقة تنفيذ الشيفرات اللامتزامنة في جافاسكربت يمكننا البدء بكتابة شيفرات لا متزامنة باستخدام إحدى الطرق لذلك، إما بدوال رد النداء أو الوعود أو باستخدام اللاتزامن والانتظار async/await.
</p>

<h2>
	البرمجة اللامتزامنة باستخدام دوال رد النداء
</h2>

<p>
	تُمرَّر دالة رد النداء callback function كمعامل للدوال الأخرى وتُنفَذ عند انتهاء تنفيذ الدالة المُمررة لها، وتحوي دالة رد النداء عادة شيفرات لمعالجة نتيجة تلك العملية أو شيفرات لتنفيذها بعد انتهاء تنفيذ العملية اللامتزامنة، حيث استخدمت هذه الطريقة لفترة طويلة وكانت أشيع طريقة مستخدمة لكتابة الشيفرات اللامتزامنة، ولكنها لم تعد مستخدمة حاليًا لأنها تُصعّب قراءة ووضوح الشيفرة، لكن سنستخدمها في هذه الفقرة لكتابة شيفرة جافاسكربت لا متزامنة لنتعرف بذلك على كل الطرق الممكنة ونلاحظ الفروقات بينها وميزة كل منها، حيث نستخدم دوال رد النداء بأكثر من طريقة ضمن الدوال الأخرى، وعادة ما نُمرِّرها كآخر معامل للدالة اللامتزامنة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_14" style="">
<span class="kwd">function</span><span class="pln"> asynchronousFunction</span><span class="pun">([</span><span class="pln"> </span><span class="typ">Function</span><span class="pln"> </span><span class="typ">Arguments</span><span class="pln"> </span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="typ">Callback</span><span class="pln"> </span><span class="typ">Function</span><span class="pln"> </span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">[</span><span class="pln"> </span><span class="typ">Action</span><span class="pln"> </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكننا لسنا ملزومين باتباع هذه البنية عند كتابة الدوال اللامتزامنة، ولكن شاع تمرير دالة رد النداء كآخر معامل للدالة اللامتزامنة ليسهل التعرف عليه بين المبرمجين، وتُمرَّر عادة دالة رد النداء كدالة مجهول الاسم -التي تُعرّف بلا اسم- إذ يُحسِّن تمريرها كآخر معامل قراءة الشيفرة، ولنفهم ذلك أكثر سننشئ وحدة برمجية في نود وظيفتها كتابة قائمة من أفلام <a href="https://ar.wikipedia.org/wiki/%D8%A5%D8%B3%D8%AA%D9%88%D8%AF%D9%8A%D9%88_%D8%BA%D9%8A%D8%A8%D9%84%D9%8A" rel="external nofollow">استوديو Ghibli</a> إلى ملف، فنبدأ بإنشاء مجلد سيحوي ملف جافاسكربت للبرنامج وملف الخرج النهائي كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_16" style="">
<span class="pln">mkdir ghibliMovies</span></pre>

<p>
	ندخل إلى المجلد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_18" style="">
<span class="pln">cd ghibliMovies</span></pre>

<p>
	سنرسل بدايةً طلب HTTP <a href="https://ghibliapi.herokuapp.com/" rel="external nofollow">للواجهة البرمجية لاستديو Ghibli</a> ونطبع داخل دالة رد النداء نتيجة ذلك الطلب، ولنتمكن من ذلك نحتاج إلى مكتبة تساعدنا في إرسال طلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP </a>والوصول إلى البيانات ضمن الرد باستخدام دالة رد نداء، لذا نُهيئ ملف الحزمة للوحدة بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_21" style="">
<span class="pln">npm init </span><span class="pun">-</span><span class="pln">y</span></pre>

<p>
	ونثبت مكتبة <a href="https://www.npmjs.com/package/request" rel="external nofollow">request</a> بتنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_23" style="">
<span class="pln">npm i request </span><span class="pun">--</span><span class="pln">save</span></pre>

<p>
	ننشئ ملفًا جديدًا بالاسم callbackMovies.js ونفتحه باستخدام أي محرر نصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_25" style="">
<span class="pln">nano callbackMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونكتب داخله الشيفرة التالية والتي سترسل طلب HTTP باستخدام مكتبة request السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_27" style="">
<span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'request'</span><span class="pun">);</span><span class="pln">

request</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">);</span></pre>

<p>
	نُحمّل في أول سطر مكتبة request التي ثبتناها، حيث ستعيد المكتبة دالة يمكن استدعاؤها لإنشاء طلبات HTTP نخزنها ضمن الثابت <code>request</code>، ثم نرسل طلب HTTP باستدعاء الدالة <code>request()‎</code> وتمرير عنوان <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> له.
</p>

<p>
	لنطبع الآن البيانات من نتيجة الطلب إلى الطرفية بإضافة الأسطر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_30" style="">
<span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'request'</span><span class="pun">);</span><span class="pln">

request</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">,</span><span class="pln"> body</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not send request to <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Expected</span><span class="pln"> status code </span><span class="lit">200</span><span class="pln"> but received $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}.`);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Processing our list of movies'</span><span class="pun">);</span><span class="pln">
    movies </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">body</span><span class="pun">);</span><span class="pln">
    movies</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}`);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	مررنا للدالة <code>request()‎</code> معاملان هما عنوان URL للواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> لإرسال الطلب إليها، ودالة رد نداء سهمية لمعالجة أي أخطاء قد تحدث أو معالجة نتيجة إرسال الطلب عند نجاحه بعد انتهاء تنفيذه.
</p>

<p>
	لاحظ أن دالة رد النداء أخذت ثلاثة معاملات وهي كائن الخطأ <code>error</code> و كائن الرد <code>response</code> وبيانات جسم الطلب <code>body</code>، فبعد اكتمال الطلب سيُعيَّن قيم لتلك المعاملات بناءً على النتيجة، ففي حال فشل الطلب سيتم تعيين قيمة كائن للمعامل <code>error</code>، وتعيين القيمة <code>null</code> لكل من <code>response</code> و <code>body</code>، وعند نجاح الطلب سيتم تعيين قيمة الرد للمعامل <code>response</code>، وفي حال احتوى الرد على بيانات في جسم الطلب ستُعيَّن كقيمة للمعامل <code>body</code>.
</p>

<p>
	ونستفيد من تلك المعاملات داخل دالة رد النداء التي مَرَّرناها لها للتحقق من وجود الخطأ أولًا، ويفضل التحقق من ذلك دومًا ضمن دوال رد النداء بحيث لا نكمل تنفيذ باقي التعليمات عند حدوث خطأ، وفي حال وجود خطأ سنطبع رسالة الخطأ إلى الطرفية وننهي تنفيذ الدالة، بعدها نتحقق من رمز الحالة للرد المرسل ففي حال عدم توافر الخادم للرد على الطلب أو تغيير الواجهة البرمجية أو إرسال طلبية خاطئة سنلاحظ ذلك من رمز الرد ويمكن التحقق من نجاح العملية وسلامة الرد بالتحقق من أن رمز الحالة يساوي <code>200</code> ويمكننا بذلك متابعة معالجة الطلب، وفي حالتنا عالجنا الطلب بتحليل الرد وتحويله إلى مصفوفة ثم طبع كل عنصر من عناصرها -التي تمثل الأفلام- على شكل اسم الفلم وتاريخ إصداره، والآن نحفظ الملف وتخرج منه وننفذه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_32" style="">
<span class="pln">node callbackMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر الخرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_34" style="">
<span class="typ">Castle</span><span class="pln"> in the </span><span class="typ">Sky</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1986</span><span class="pln">
</span><span class="typ">Grave</span><span class="pln"> of the </span><span class="typ">Fireflies</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbor</span><span class="pln"> </span><span class="typ">Totoro</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">Kiki</span><span class="str">'</span><span class="pln">s </span><span class="typ">Delivery</span><span class="pln"> </span><span class="typ">Service</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1989</span><span class="pln">
</span><span class="typ">Only</span><span class="pln"> </span><span class="typ">Yesterday</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1991</span><span class="pln">
</span><span class="typ">Porco</span><span class="pln"> </span><span class="typ">Rosso</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1992</span><span class="pln">
</span><span class="typ">Pom</span><span class="pln"> </span><span class="typ">Poko</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1994</span><span class="pln">
</span><span class="typ">Whisper</span><span class="pln"> of the </span><span class="typ">Heart</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1995</span><span class="pln">
</span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Mononoke</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1997</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbors</span><span class="pln"> the </span><span class="typ">Yamadas</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1999</span><span class="pln">
</span><span class="typ">Spirited</span><span class="pln"> </span><span class="typ">Away</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2001</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Cat</span><span class="pln"> </span><span class="typ">Returns</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2002</span><span class="pln">
</span><span class="typ">Howl</span><span class="str">'</span><span class="pln">s </span><span class="typ">Moving</span><span class="pln"> </span><span class="typ">Castle</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2004</span><span class="pln">
</span><span class="typ">Tales</span><span class="pln"> from </span><span class="typ">Earthsea</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2006</span><span class="pln">
</span><span class="typ">Ponyo</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2008</span><span class="pln">
</span><span class="typ">Arrietty</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2010</span><span class="pln">
</span><span class="typ">From</span><span class="pln"> </span><span class="typ">Up</span><span class="pln"> on </span><span class="typ">Poppy</span><span class="pln"> </span><span class="typ">Hill</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2011</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Wind</span><span class="pln"> </span><span class="typ">Rises</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Tale</span><span class="pln"> of the </span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Kaguya</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">When</span><span class="pln"> </span><span class="typ">Marnie</span><span class="pln"> </span><span class="typ">Was</span><span class="pln"> </span><span class="typ">There</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2014</span></pre>

<p>
	حصلنا على قائمة بأفلام من إنتاج استوديو Ghibli مع تواريخ إصدارها بنجاح، والآن نريد من البرنامج كتابة القائمة إلى ملف، لذا نعدل الملف callbackMovies.js ضمن محرر النصوص ونضيف الأسطر التالية لإنشاء ملف بصيغة CSV يحوي بيانات الأفلام المجلوبة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_36" style="">
<span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'request'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">);</span><span class="pln">

request</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">,</span><span class="pln"> body</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not send request to <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Expected</span><span class="pln"> status code </span><span class="lit">200</span><span class="pln"> but received $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}.`);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Processing our list of movies'</span><span class="pun">);</span><span class="pln">
    movies </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">body</span><span class="pun">);</span><span class="pln">
    let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
    movies</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'callbackMovies.csv'</span><span class="pun">,</span><span class="pln"> movieList</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not save the </span><span class="typ">Ghibli</span><span class="pln"> movies to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`);</span><span class="pln">
            </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Saved our list of movies to callbackMovies.csv'</span><span class="pun">);;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نلاحظ بدايةً استيراد الوحدة البرمجية <code>fs</code> والتي توفرها بيئة نود للتعامل مع الملفات حيث نريد التابع <code>writeFile()‎</code> منها لكتابة البيانات إلى ملف بطريقة لا متزامنة، وبدلًا من طباعة البيانات إلى الطرفية، يمكننا إضافتها إلى السلسلة النصية للمتغير <code>movieList</code> ثم نستدعي التابع <code>writeFile()‎</code> لحفظ قيمة <code>movieList</code> النهائية إلى ملف جديد بالاسم callbackMovies.csv، ثم نمرر أخيرًا دالة رد نداء للتابع <code>writeFile()‎</code> حيث سيُمرَّر لها معاملًا وحيدًا وهو كائن الخطأ <code>error</code> نعرف بالتحقق منه إذا ما فشلت عملية الكتابة إلى الملف، وذلك مثلًا عندما لا يملك المستخدم الحالي الذي يُنفِّذ إجرائية نود صلاحيات كافية لإنشاء ملف جديد ضمن المسار الحالي.
</p>

<p>
	والآن نحفظ الملف وننفذه مرة أخرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_38" style="">
<span class="pln">node callbackMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنلاحظ ظهور ملف جديد ضمن مجلد المشروع ghibliMovies بالاسم callbackMovies.csv يحوي قائمة أفلام تشبه القائمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_40" style="">
<span class="typ">Castle</span><span class="pln"> in the </span><span class="typ">Sky</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1986</span><span class="pln">
</span><span class="typ">Grave</span><span class="pln"> of the </span><span class="typ">Fireflies</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbor</span><span class="pln"> </span><span class="typ">Totoro</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">Kiki</span><span class="str">'</span><span class="pln">s </span><span class="typ">Delivery</span><span class="pln"> </span><span class="typ">Service</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1989</span><span class="pln">
</span><span class="typ">Only</span><span class="pln"> </span><span class="typ">Yesterday</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1991</span><span class="pln">
</span><span class="typ">Porco</span><span class="pln"> </span><span class="typ">Rosso</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1992</span><span class="pln">
</span><span class="typ">Pom</span><span class="pln"> </span><span class="typ">Poko</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1994</span><span class="pln">
</span><span class="typ">Whisper</span><span class="pln"> of the </span><span class="typ">Heart</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1995</span><span class="pln">
</span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Mononoke</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1997</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbors</span><span class="pln"> the </span><span class="typ">Yamadas</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1999</span><span class="pln">
</span><span class="typ">Spirited</span><span class="pln"> </span><span class="typ">Away</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2001</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Cat</span><span class="pln"> </span><span class="typ">Returns</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2002</span><span class="pln">
</span><span class="typ">Howl</span><span class="str">'</span><span class="pln">s </span><span class="typ">Moving</span><span class="pln"> </span><span class="typ">Castle</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2004</span><span class="pln">
</span><span class="typ">Tales</span><span class="pln"> from </span><span class="typ">Earthsea</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2006</span><span class="pln">
</span><span class="typ">Ponyo</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2008</span><span class="pln">
</span><span class="typ">Arrietty</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2010</span><span class="pln">
</span><span class="typ">From</span><span class="pln"> </span><span class="typ">Up</span><span class="pln"> on </span><span class="typ">Poppy</span><span class="pln"> </span><span class="typ">Hill</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2011</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Wind</span><span class="pln"> </span><span class="typ">Rises</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Tale</span><span class="pln"> of the </span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Kaguya</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">When</span><span class="pln"> </span><span class="typ">Marnie</span><span class="pln"> </span><span class="typ">Was</span><span class="pln"> </span><span class="typ">There</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2014</span></pre>

<p>
	نلاحظ أننا كتبنا ذلك المحتوى إلى ملف CSV ضمن دالة رد النداء لطلب HTTP المرسل، حيث أن الشيفرات ضمن تلك الدالة ستُنفَذ بعد انتهاء عملية إرسال الطلب فقط، وفي حال أردنا الاتصال بقاعدة بيانات بعد كتابة محتوى ملف CSV السابق يجب إنشاء دالة لا متزامنة أخرى تُستدعَى ضمن دالة رد نداء التابع <code>writeFile()‎</code>، وكلما أردنا تنفيذ عمليات لا متزامنة متلاحقة يجب تغليف المزيد من دوال رد النداء داخل بعضها البعض، فإذا أردنا مثلًا تنفيذ خمس عمليات لا متزامنة متتالية بحيث تُنفًّذ كل منها بعد انتهاء العملية التي تسبقها وسنحصل في النهاية على شيفرة بنيتها تشبه التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_42" style="">
<span class="pln">doSomething1</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    doSomething2</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        doSomething3</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            doSomething4</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                doSomething5</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="com">// العملية النهائية</span><span class="pln">
                </span><span class="pun">});</span><span class="pln">
            </span><span class="pun">});</span><span class="pln">
        </span><span class="pun">});</span><span class="pln"> 
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

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

<h2>
	استخدام الوعود لاختصار الشيفرات اللامتزامنة
</h2>

<p>
	الوعد Promise هو كائن توفره جافاسكربت وظيفته إرجاع قيمة ما مستقبلًا ومن هنا جاءت تسمية الوعد من أنه يعدك بإعادة قيمة ما لاحقًا، ويمكن للدوال اللامتزامنة أن تُعيد كائن وعد من هذا النوع بدلًا من إرجاع القيمة النهائية لتنفيذها، وعند تحقق هذا الوعد مستقبلًا fulfilled سنحصل على القيمة النهائية للعملية وإلا سنحصل على خطأ ويكون الوعد قد رُفض rejected، أما خلال التنفيذ يكون الوعد في حالة الانتظار ويتم معالجته.
</p>

<p>
	تستخدم الوعود بالصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_44" style="">
<span class="pln">promiseFunction</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">([</span><span class="pln"> </span><span class="pun">رد</span><span class="pln"> </span><span class="pun">نداء</span><span class="pln"> </span><span class="pun">يُنفَّذ</span><span class="pln"> </span><span class="pun">عند</span><span class="pln"> </span><span class="pun">تحقق</span><span class="pln"> </span><span class="pun">الوعد</span><span class="pln"> </span><span class="pun">])</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">([</span><span class="pln"> </span><span class="pun">رد</span><span class="pln"> </span><span class="pun">نداء</span><span class="pln"> </span><span class="pun">يُنفَّذ</span><span class="pln"> </span><span class="pun">عند</span><span class="pln"> </span><span class="pun">رفض</span><span class="pln"> </span><span class="pun">الوعد</span><span class="pln"> </span><span class="pun">])</span></pre>

<p>
	نلاحظ أن الوعود تستخدم دوال رد النداء هي أيضًا، حيث نُمرِّر للتابع <code>then()‎</code> دالة رد نداء تُستدعى عند نجاح التنفيذ، ونُمرِّر للتابع <code>catch()‎</code> دالة رد نداء أخرى تُستدعى لمعالجة الأخطاء عند حدوثها أثناء عملية تنفيذ ذلك الوعد.
</p>

<p>
	ولنتعرف على الوعود أكثر سنطور برنامجنا السابق لاستخدام طريقة الوعود بدلًا من دوال رد النداء، ونبدأ بتثبيت مكتبة <a href="https://www.npmjs.com/package/axios" rel="external nofollow">Axios</a> التي تعتمد على الوعود في عملياتها لإرسال طلبات HTTP:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_46" style="">
<span class="pln">npm i axios </span><span class="pun">--</span><span class="pln">save</span></pre>

<p>
	نُنشئ ملفًا جديدًا بالاسم promiseMovies.js سيحوي النسخة الجديدة من البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_48" style="">
<span class="pln">nano promiseMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنرسل طلب HTTP باستخدام مكتبة axios هذه المرة، وباستخدام نسخة خاصة من وحدة <code>fs</code> تعتمد في عملها على الوعود سنحفظ النتيجة ضمن ملف CSV كما فعلنا سابقًا، ونبدأ بكتابة الشيفرة التالية ضمن الملف لتحميل مكتبة Axios وإرسال طلب HTTP للواجهة البرمجية للحصول على قائمة الأفلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_50" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">

axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">);</span></pre>

<p>
	حملنا في أول سطر مكتبة axios وحفظنا الناتج ضمن الثابت <code>axios</code> وبعدها استدعينا التابع <code>axios.get()‎</code> لإرسال طلب HTTP إلى الواجهة البرمجية، حيث سيعيد التابع <code>axios.get()‎</code> وعدًا يمكننا ربطه مع دالة لطباعة الأفلام إلى الطرفية عند نجاح الطلب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_52" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">


axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Successfully retrieved our list of movies'</span><span class="pun">);</span><span class="pln">
        response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}`);</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">})</span></pre>

<p>
	بعد إرسال طلب HTTP من نوع GET باستخدام التابع <code>axios.get()‎</code> استخدمنا التابع <code>then()‎</code> والذي سيُنفذ عند نجاح الطلب فقط، وطبعنا داخله الأفلام إلى الطرفية كما فعلنا في الفقرة السابقة، والآن نطور البرنامج لكتابة تلك البيانات إلى ملف جديد باستخدام واجهة للتعامل مع نظام الملفات قائمة على الوعود كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_54" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">


axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Successfully retrieved our list of movies'</span><span class="pun">);</span><span class="pln">
        let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
        response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'promiseMovies.csv'</span><span class="pun">,</span><span class="pln"> movieList</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Saved our list of movies to promiseMovies.csv'</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">})</span></pre>

<p>
	استوردنا الوحدة البرمجية <code>fs</code> مجددًا لكن نلاحظ استخدام الخاصية <code>‎.promises</code> منها، وهي النسخة الخاصة من وحدة <code>fs</code> التي تستخدم الوعود كنتيجة لتنفيذ دوالها بدلًا من طريقة دوال رد النداء، وسبب إتاحتها كنسخة منفصلة هو دعم المشاريع التي لازالت تستخدم الطريقة القديمة.
</p>

<p>
	ونلاحظ كيف أصبح أول استدعاء للتابع <code>then()‎</code> يعالج رد الطلب HTTP الوارد ثم يستدعي التابع <code>fs.writeFile()‎</code> بدلًا من طباعة البيانات إلى الطرفية، وبما أننا نستخدم نسخة الوعود من <code>fs</code> فسيعيد التابع <code>writeFile()‎</code> عند استدعائه وعدًا آخر، يجري معالجته باستدعاء <code>then()‎</code> مرة أخرى والتي بدورها تأخذ دالة رد النداء تُنفَّذ عند نجاح تنفيذ ذلك الوعد -المُعاد من التابع <code>writeFile()‎</code>.
</p>

<p>
	نلاحظ أيضًا مما سبق أنه يمكن إعادة وعد من داخل وعد آخر، ما سيسمح بتنفيذ تلك الوعود الواحد تلو الآخر، ويوفر لنا ذلك طريقة لتنفيذ عدد من العمليات اللامتزامنة خلف بعضها البعض، وندعو هذه العملية باسم <a href="https://academy.hsoub.com/programming/javascript/%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-chaining-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r916/" rel="">سلسلة الوعود promise chaining</a> وهي بديل عن استخدام دوال رد النداء المتداخلة التي تعرفنا عليها في الفقرة السابقة، بحيث يُستدعى التابع <code>then()‎</code> الموالي عند تحقق الوعد المعاد من سابقه وهكذا وعند رفض أحد الوعود يُستدعى التابع <code>catch()‎</code> مباشرةً آنذاك وتتوقع السلسلة عن العمل.
</p>

<p>
	<strong>ملاحظة</strong>: لم نتحقق في هذا المثال من رمز الرد لطلب HTTP الوارد كما فعلنا سابقًا، حيث لن يُلبي axios تلقائيًا الوعد الذي يعيده في حال كان رمز الرد الوارد يمثل أي خطأ، ولذلك لم نعد مضطرين للتحقق منه بأنفسنا.
</p>

<p>
	والآن نضيف التابع <code>catch()‎</code> في نهاية البرنامج لإكماله كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_56" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">


axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Successfully retrieved our list of movies'</span><span class="pun">);</span><span class="pln">
        let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
        response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">

        </span><span class="kwd">return</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'promiseMovies.csv'</span><span class="pun">,</span><span class="pln"> movieList</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Saved our list of movies to promiseMovies.csv'</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">((</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not save the </span><span class="typ">Ghibli</span><span class="pln"> movies to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">});</span></pre>

<p>
	في حال فشل أي وعد من سلسلة الوعود تلك سيُنفذ التابع <code>catch()‎</code> تلقائيًا كما أشرنا متجاوزًا أي دوال تسبقه، لذا يمكننا إضافة استدعاء التابع <code>catch()‎</code> مرة واحدة فقط في النهاية لمعالجة أي خطأ قد يحدث من أي عملية سابقة حتى لو كنا ننفذ عدة عمليات غير متزامنة متتالية.
</p>

<p>
	والآن لنتحقق من صحة عمل البرنامج بتنفيذه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_58" style="">
<span class="pln">node promiseMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ ظهور نفس البيانات السابقة ضمن الملف promiseMovies.csv:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_60" style="">
<span class="typ">Castle</span><span class="pln"> in the </span><span class="typ">Sky</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1986</span><span class="pln">
</span><span class="typ">Grave</span><span class="pln"> of the </span><span class="typ">Fireflies</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbor</span><span class="pln"> </span><span class="typ">Totoro</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">Kiki</span><span class="str">'</span><span class="pln">s </span><span class="typ">Delivery</span><span class="pln"> </span><span class="typ">Service</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1989</span><span class="pln">
</span><span class="typ">Only</span><span class="pln"> </span><span class="typ">Yesterday</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1991</span><span class="pln">
</span><span class="typ">Porco</span><span class="pln"> </span><span class="typ">Rosso</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1992</span><span class="pln">
</span><span class="typ">Pom</span><span class="pln"> </span><span class="typ">Poko</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1994</span><span class="pln">
</span><span class="typ">Whisper</span><span class="pln"> of the </span><span class="typ">Heart</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1995</span><span class="pln">
</span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Mononoke</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1997</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbors</span><span class="pln"> the </span><span class="typ">Yamadas</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1999</span><span class="pln">
</span><span class="typ">Spirited</span><span class="pln"> </span><span class="typ">Away</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2001</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Cat</span><span class="pln"> </span><span class="typ">Returns</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2002</span><span class="pln">
</span><span class="typ">Howl</span><span class="str">'</span><span class="pln">s </span><span class="typ">Moving</span><span class="pln"> </span><span class="typ">Castle</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2004</span><span class="pln">
</span><span class="typ">Tales</span><span class="pln"> from </span><span class="typ">Earthsea</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2006</span><span class="pln">
</span><span class="typ">Ponyo</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2008</span><span class="pln">
</span><span class="typ">Arrietty</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2010</span><span class="pln">
</span><span class="typ">From</span><span class="pln"> </span><span class="typ">Up</span><span class="pln"> on </span><span class="typ">Poppy</span><span class="pln"> </span><span class="typ">Hill</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2011</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Wind</span><span class="pln"> </span><span class="typ">Rises</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Tale</span><span class="pln"> of the </span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Kaguya</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">When</span><span class="pln"> </span><span class="typ">Marnie</span><span class="pln"> </span><span class="typ">Was</span><span class="pln"> </span><span class="typ">There</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2014</span></pre>

<p>
	نلاحظ كيف اختصر استخدام الوعود كتابة الكثير من الشيفرات، وكيف أن عملية سَلسَلة الوعود أسهل وأبسط ومقروءة أكثر من طريقة دوال رد النداء المتداخلة، ولكن حتى مع تلك المزايا الجديدة هنالك صعوبات تحصل في حال أردنا تنفيذ العديد من العمليات اللامتزامنة أي ستزداد صعوبة الشيفرة المكتوبة بازدياد طول سلسلة الوعود.
</p>

<p>
	وتحتاج كلا الطريقتين السابقتين سواء دوال رد النداء أو الوعود لإنشاء دوال رد نداء تُعالج ناتج العملية اللامتزامنة، والطريقة الأفضل من ذلك هي انتظار نتيجة العملية اللامتزامنة وتخزينها ضمن متغير خارج أي دالة، وبذلك يمكننا استخدام النتائج ضمن المتغيرات مباشرةً ودون الحاجة لإنشاء الكثير من الدوال في كل مرة، وهذا تحديدًا ما يميز عملية اللاتزامن والانتظار باستخدام <code>async</code> و <code>await</code> في جافاسكربت وهي ما سنتعرف عليه في الفقرة التالية.
</p>

<p>
	<strong>ملاحظة</strong>: إن أردت تعلم المزيد حول الوعود، فارجع إلى توثيق <a href="https://wiki.hsoub.com/JavaScript/Promise" rel="external">واجهة الوعود Promise</a> في موسوعة حسوب.
</p>

<h2>
	التعامل مع الوعود باستخدام طريقة اللاتزامن والانتظار async/await
</h2>

<p>
	تتيح الكلمة المفتاحية <code>async</code> اللاتزامن والكلمة المفتاحية <code>await</code> الانتظار صيغة بديلة أبسط للتعامل مع الوعود، إذ ستُعاد النتيجة مباشرةً كقيمة بدلًا من تمريرها على شكل وعد إلى التابع <code>then()‎</code> لمعالجتها وكأننا نستدعي تابع متزامن عادي في جافاسكربت.
</p>

<p>
	ولنخبر جافاسكربت أن دالة ما هي دالة لا متزامنة تُعيد وعدًا، نعرفها بوضع الكلمة المفتاحية <code>async</code> قبلها، وبعدها يمكننا استخدام الكلمة المفتاحية <code>await</code> داخلها لإخبار جافاسكربت بإرجاع ناتج الوعد المُعاد عند نجاحه بدلًا من إرجاع الوعد نفسه كقيمة، أي تكون صيغة استخدام <code>async/await</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_62" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    await </span><span class="pun">[عملية</span><span class="pln"> </span><span class="pun">غير</span><span class="pln"> </span><span class="pun">متزامنة]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنطبق استخدامها على برنامجنا ونلاحظ الفرق، لننشئ ملفًا للبرنامج الجديد بالاسم asyncAwaitMovies.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_64" style="">
<span class="pln">nano asyncAwaitMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نستورد داخل ذلك الملف نفس الوحدات التي استخدمناها سابقًا لأن طريقة <code>async/await</code> تعتمد على الوعود في عملها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_66" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span></pre>

<p>
	والآن نعرّف دالة باستخدام الكلمة المفتاحية <code>async</code> للدلالة على أنها دالة لا متزامنة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_68" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> saveMovies</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	عرفنا الدالة <code>saveMovies()‎</code> باستخدام الكلمة المفتاحية <code>async</code>، بهذا نستطيع استخدام الكلمة المفتاحية <code>await</code> داخلها، أي ضمن الدوال اللامتزامنة التي نعرفها بنفس تلك الطريقة، والآن نستخدم الكلمة المفتاحية <code>await</code> لإرسال طلب HTTP إلى الواجهة البرمجية لجلب قائمة الأفلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_70" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> saveMovies</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let response </span><span class="pun">=</span><span class="pln"> await axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">);</span><span class="pln">
    let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
    response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نرسل طلب HTTP باستخدام <code>axios.get()‎</code> من داخل الدالة <code>saveMovies()‎</code> كما فعلنا سابقًا، لكن لاحظ أنه بدلًا من استدعاء التابع <code>then()‎</code> أضفنا الكلمة المفتاحية <code>await</code> قبل الاستدعاء، سينفذ حينها جافاسكربت الشيفرة في الأسطر اللاحقة فقط عند نجاح تنفيذ التابع <code>axios.get()‎</code>، وستُعيَّن القيمة التي يعيدها إلى المتغير <code>response</code>، والآن نضيف الشيفرة المسؤولة عن كتابة البيانات الواردة إلى ملف CSV:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_72" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> saveMovies</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let response </span><span class="pun">=</span><span class="pln"> await axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">);</span><span class="pln">
    let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
    response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
    await fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'asyncAwaitMovies.csv'</span><span class="pun">,</span><span class="pln"> movieList</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ استخدامنا للكلمة المفتاحية <code>await</code> عند استدعاء التابع <code>fs.writeFile()‎</code> أيضًا لكتابة محتويات الملف، والآن ننهي كتابة الدالة بالتقاط ومعالجة أي أخطاء قد ترميها تلك العمليات باستخدام <code>try/catch</code> كما نفعل عادةً في جافاسكربت لالتقاط الأخطاء المرمية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_74" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> saveMovies</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let response </span><span class="pun">=</span><span class="pln"> await axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">);</span><span class="pln">
        let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
        response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
        await fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'asyncAwaitMovies.csv'</span><span class="pun">,</span><span class="pln"> movieList</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not save the </span><span class="typ">Ghibli</span><span class="pln"> movies to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبذلك نضمن <a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r1342/" rel="">معالجة الأخطاء</a> عند حدوثها في العمليات اللامتزامنة داخل جسم <code>try</code>، بما فيها أي أخطاء قد تحدث عند إرسال طلب HTTP أو عند فشل الكتابة إلى الملف.
</p>

<p>
	والآن نستدعي الدالة <code>saveMovies()‎</code> اللامتزامنة لضمان تنفيذها عند تنفيذ البرنامج باستخدام نود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_76" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">).</span><span class="pln">promises</span><span class="pun">;</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> saveMovies</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        let response </span><span class="pun">=</span><span class="pln"> await axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://ghibliapi.herokuapp.com/films'</span><span class="pun">);</span><span class="pln">
        let movieList </span><span class="pun">=</span><span class="pln"> </span><span class="str">''</span><span class="pun">;</span><span class="pln">
        response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">movie </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            movieList </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]},</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">movie</span><span class="pun">[</span><span class="str">'release_date'</span><span class="pun">]}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
        </span><span class="pun">});</span><span class="pln">
        await fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'asyncAwaitMovies.csv'</span><span class="pun">,</span><span class="pln"> movieList</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not save the </span><span class="typ">Ghibli</span><span class="pln"> movies to a file</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

saveMovies</span><span class="pun">();</span></pre>

<p>
	لا يوجد فروقات كبيرة بين هذه الطريقة وبين الصيغة العادية لكتابة واستدعاء شيفرات جافاسكربت المتزامنة، حيث لم نحتاج لتعريف العديد من الدوال -تحديدًا دوال ردود النداء- وتمريرها كما فعلنا سابقًا، وتتوضح بذلك ميزة استخدام <code>async/await</code> تسهيل قراءة واستخدام الشيفرات اللامتزامنة أفضل من الطرق الأخرى.
</p>

<p>
	والآن ننفذ هذا البرنامج ونختبر عمله:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_78" style="">
<span class="pln">node asyncAwaitMovies</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ ظهور ملف جديد بالاسم asyncAwaitMovies.csv ضمن مجلد المشروع ghibliMovies يحوي داخله على التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_934_80" style="">
<span class="typ">Castle</span><span class="pln"> in the </span><span class="typ">Sky</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1986</span><span class="pln">
</span><span class="typ">Grave</span><span class="pln"> of the </span><span class="typ">Fireflies</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbor</span><span class="pln"> </span><span class="typ">Totoro</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1988</span><span class="pln">
</span><span class="typ">Kiki</span><span class="str">'</span><span class="pln">s </span><span class="typ">Delivery</span><span class="pln"> </span><span class="typ">Service</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1989</span><span class="pln">
</span><span class="typ">Only</span><span class="pln"> </span><span class="typ">Yesterday</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1991</span><span class="pln">
</span><span class="typ">Porco</span><span class="pln"> </span><span class="typ">Rosso</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1992</span><span class="pln">
</span><span class="typ">Pom</span><span class="pln"> </span><span class="typ">Poko</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1994</span><span class="pln">
</span><span class="typ">Whisper</span><span class="pln"> of the </span><span class="typ">Heart</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1995</span><span class="pln">
</span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Mononoke</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1997</span><span class="pln">
</span><span class="typ">My</span><span class="pln"> </span><span class="typ">Neighbors</span><span class="pln"> the </span><span class="typ">Yamadas</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1999</span><span class="pln">
</span><span class="typ">Spirited</span><span class="pln"> </span><span class="typ">Away</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2001</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Cat</span><span class="pln"> </span><span class="typ">Returns</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2002</span><span class="pln">
</span><span class="typ">Howl</span><span class="str">'</span><span class="pln">s </span><span class="typ">Moving</span><span class="pln"> </span><span class="typ">Castle</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2004</span><span class="pln">
</span><span class="typ">Tales</span><span class="pln"> from </span><span class="typ">Earthsea</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2006</span><span class="pln">
</span><span class="typ">Ponyo</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2008</span><span class="pln">
</span><span class="typ">Arrietty</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2010</span><span class="pln">
</span><span class="typ">From</span><span class="pln"> </span><span class="typ">Up</span><span class="pln"> on </span><span class="typ">Poppy</span><span class="pln"> </span><span class="typ">Hill</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2011</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Wind</span><span class="pln"> </span><span class="typ">Rises</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">The</span><span class="pln"> </span><span class="typ">Tale</span><span class="pln"> of the </span><span class="typ">Princess</span><span class="pln"> </span><span class="typ">Kaguya</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2013</span><span class="pln">
</span><span class="typ">When</span><span class="pln"> </span><span class="typ">Marnie</span><span class="pln"> </span><span class="typ">Was</span><span class="pln"> </span><span class="typ">There</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2014</span></pre>

<p>
	وبذلك نكون تعرفنا على طريقة عملها استخدام ميزة <code>async/await</code> في جافاسكربت.
</p>

<h2>
	ختامًا
</h2>

<p>
	تعرفنا في هذا المقال على الطريقة التي تعالج بها جافاسكربت الدوال وتدير العمليات اللامتزامنة باستخدام حلقة الأحداث، وكتبنا برنامجًا لإنشاء ملف بصيغة CSV بالاعتماد على بيانات واردة من واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> بإرسال طلب HTTP نطلب فيه بيانات عدد من الأفلام مستخدمين بذلك كل طرق البرمجة اللامتزامنة المتوفرة في جافاسكربت، حيث بدأنا ذلك باستخدام طريقة دوال رد النداء القديمة وبعدها تعرفنا على الوعود وطريقة استخدامها، ثم طورنا ذلك باستخدام طريقة اللاتزامن والانتظار <code>async/await</code> لتصبح الشيفرة أبسط وأوضح.
</p>

<p>
	ويمكنك الآن بعد ما تعلمته ضمن هذا المقال استخدام التقنيات التي تعلمتها لكتابة البرامج التي تستخدم العمليات اللامتزامنة، ويمكنك الاستفادة من <a href="https://github.com/public-apis/public-apis" rel="external nofollow">قائمة الواجهات البرمجية العامة</a> المتاحة لتطوير ما قد يفيدك، وذلك بإرسال طلبات HTTP لا متزامنة إليها كما فعلنا في هذا المقال.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-write-asynchronous-code-in-node-js" rel="external nofollow">How To Write Asynchronous Code in Node.js</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-nodejs-r1467/" rel="">البرمجة غير المتزامنة في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-promise-api-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r918/" rel="">واجهة الوعود البرمجية Promise <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%88%D8%A7%D8%B9%D8%AF%D8%A9-%D8%AA%D8%AD%D9%88%D9%8A%D9%84-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%B9%D9%88%D8%AF-promisification-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r919/" rel="">الدوال الواعدة: تحويل الدوال إلى وعود Promisification في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r917/" rel="">التعامل مع أخطاء الوعود في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1743</guid><pubDate>Sat, 08 Oct 2022 06:25:07 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x648;&#x62D;&#x62F;&#x627;&#x62A; &#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; Modules &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-modules-%D9%81%D9%8A-nodejs-r1742/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/63410ee2ef403_----node.png.678e610bef544d7115925870e73fc068.png" /></p>

<p>
	الوحدة البرمجية module في نود Node.js هي أجزاء من <a href="https://academy.hsoub.com/programming/javascript/%D9%86%D9%85%D8%B7-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r785/" rel="">شيفرات جافاسكربت</a> منعزلة قابلة للاستخدام في أكثر من تطبيق، حيث يعد الغرض من الوحدة البرمجية هو تقسيم منطقي لوظيفة عمل الشيفرة، فأي ملف أو مجموعة ملفات يمكن اعتبارها وحدة برمجية في حال أمكن استخدام البيانات والتوابع فيها من قبل برامج أخرى خارجية.
</p>

<p>
	ويَنشأ عن التقسيم الوظيفي للشيفرات بتلك الطريقة وحدات برمجية أخرى يمكن إعادة استخدامها في عدة مشاريع أكبر أو مع مطورين آخرين أي أن الوحدات تبنى باعتماد بعضها على بعضها الآخر بطريقة هرمية، ما يؤدي لتطوير برمجيات غير مترابطة سهلة التطوير والتوسع وتوفر درجة من التعقيد أعلى من الوحدات المكونة لها، ما يفتح بابًا للمساهمة بمشاركة تلك الوحدات البرمجية والتي توفر بيانات وتوابع مفيدة مع مجتمع نود، وهي الطريقة التي جرى فيها تحزيم ونشر كل الوحدات البرمجية على مستودع npm، لهذا كمبرمج نود من الضروري أن تتعلم طريقة إنشاء الوحدات البرمجية.
</p>

<p>
	أخذنا في المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">إدارة الوحدات البرمجية في Node.js باستخدام npm وملف package.json</a> فكرة أساسية عن ماهية الوحدات في نود وتعرفنا على <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">مدير حزم نود npm</a> وأهمية الملف package.json لإدارة الوحدات التي يعتمد عليها مشروعنا، وسنتعلم في هذا المقال كيفية إنشاء وحدة برمجية وظيفتها اقتراح الألوان على مطور الويب لاستخدامها في التصميم، فسنخزن الألوان المتاحة في مصفوفة داخل الوحدة وسنوفر تابعًا للمستخدمين يختار لهم إحداها عشوائيًا، بعدها سنتعلم عدة طرق يمكننا بها استيراد تلك الوحدة واستخدامها ضمن تطبيقات ومشاريع نود الأخرى.
</p>

<p>
	يلزمك في هذا المقال معرفةً باستخدام حلقة REPL التي يوفرها نود، حيث سنستخدمها لاختبار الوحدة التي سنطورها، لذا يفضل الاطلاع على مقال <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B6%D8%B9-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D9%8A-repl-%D9%81%D9%8A-nodejs-r1712/" rel="">استخدام الوضع التفاعلي REPL في Node.js</a> من هذه السلسلة "<a href="https://academy.hsoub.com/tags/%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20node.js/" rel="">دليل تعلم Node.js</a>" إن لم تطلع عليه مسبقًا.
</p>

<h2>
	إنشاء وحدة برمجية في Node.js
</h2>

<p>
	سنشرح في هذه الفقرة طريقة إنشاء وحدة برمجية جديدة في نود، حيث ستحتوي الوحدة التي سنطورها على مصفوفة من الألوان وتابع يختار إحداها عشوائيًا ويعيده للمستخدم، وسنستخدم في ذلك خاصية التصدير <code>exports</code> في نود لإتاحة التابع والمصفوفة للبرامج الخارجية.
</p>

<p>
	بدايةً، لنعتمد هيكلية معينة للبيانات التي سنخزنها ضمن الوحدة، حيث سنمثل كل لون بكائن سيحوي الخاصية <code>name</code> التي تعبر عن اسم ذلك اللون بصيغة مقروءة، والخاصية <code>code</code> وهي سلسلة نصية تمثل ترميز ذلك اللون لاستخدامه في <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">HTML</a>، والصيغة المعتمدة لتمثيل الألوان في <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">HTML</a> هي ستة أرقام بالترميز الست عشري.
</p>

<p>
	نبدأ باختيار بعض تلك الألوان التي ستوفرها وحدتنا البرمجية ونضعها في مصفوفة بالاسم <code>allColors</code> وليكن عددها ستة ألوان كما ستحتوي وحدتنا على تابع بالاسم <code>getRandomColor()‎</code> لاختيار لون عشوائي من تلك المصفوفة وإعادته.
</p>

<p>
	ننتقل إلى الخطوات العملية، ننشئ مجلدًا جديدًا لاحتواء المشروع نسميه colors وننتقل إليه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1369_11" style="">
<span class="pln">mkdir colors
cd colors</span></pre>

<p>
	نُهيئ ملف الحزمة package.json ضمن مجلد المشروع لتتمكن باقي البرامج من استيراده واستخدامه لاحقًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_13" style="">
<span class="pln">npm init </span><span class="pun">-</span><span class="pln">y</span></pre>

<p>
	يمكن باستخدام الخيار <code>‎-y</code> تخطي الأسئلة التي تظهر عادةً عند تخصيص محتوى ملف الحزمة package.json، وفي حال كنا ننوي نشر تلك الوحدة يجب تخصيص القيم داخل ذلك الملف كما شرحنا في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">المقال السابق</a>.
</p>

<p>
	سنحصل بعد تنفيذ الأمر على الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_16" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"colors"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الآن نُنشئ ملف <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> جديد سيحوي على شيفرة الوحدة البرمجية وسيكون المدخل لها، ونفتحه باستخدام أي محرر نصوص أو شيفرات برمجية، مثلًا باستخدام <code>nano</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_19" style="">
<span class="pln">nano index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نبدأ بتعريف الصنف <code>Color</code> والذي سنمرر له اسم اللون وترميزه الذي سيستخدم ضمن HTML كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_21" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">code </span><span class="pun">=</span><span class="pln"> code</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بعد تعريف هيكلية البيانات التي ستمثل اللون، نُنشئ من ذلك الصنف بعض الكائنات ونخزنها ضمن مصفوفة الألوان كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_23" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">code </span><span class="pun">=</span><span class="pln"> code</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> allColors </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'brightred'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#E74C3C'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'soothingpurple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#9B59B6'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'skyblue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#5DADE2'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'leafygreen'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#48C9B0'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'sunkissedyellow'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#F4D03F'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'groovygray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#D7DBDD'</span><span class="pun">),</span><span class="pln">
</span><span class="pun">];</span></pre>

<p>
	بعدها نُعرّف <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r781/" rel="">الدالة</a> التي ستجلب لنا لونًا عشوائيًا عند استدعاءها، لتصبح الشيفرة بالكامل كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_25" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">code </span><span class="pun">=</span><span class="pln"> code</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> allColors </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'brightred'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#E74C3C'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'soothingpurple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#9B59B6'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'skyblue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#5DADE2'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'leafygreen'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#48C9B0'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'sunkissedyellow'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#F4D03F'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'groovygray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#D7DBDD'</span><span class="pun">),</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">getRandomColor </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> allColors</span><span class="pun">[</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> allColors</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">allColors </span><span class="pun">=</span><span class="pln"> allColors</span><span class="pun">;</span></pre>

<p>
	تشير الكلمة المفتاحية <code>exports</code> إلى كائن عام توفره نود لكل وحدة برمجية، حيث ستكون كل الكائنات والتوابع المُعرّفة كخصائص ضمن ذلك الكائن متاحة عند استيراد هذه الوحدة واستخدامها من قبل الوحدات البرمجية الأخرى، ولذلك لاحظ كيف عرّفنا التابع <code>getRandomColor()‎</code> مباشرةً كخاصية ضمن الكائن <code>exports</code>، وبعدها أضفنا الخاصية <code>allColors</code> ضمن ذلك الكائن التي تشير قيمتها إلى مصفوفة الألوان <code>allColors</code> المُنشئة سابقًا.
</p>

<p>
	بناءً على ما سبق، ستتمكن أي وحدة برمجية أخرى بعد استيرادها لهذه الوحدة من الوصول إلى التابع <code>getRandomColor()‎</code> والمصفوفة <code>allColors</code> واستخدامهما، وبهذا نكون قد أنشأنا وحدة برمجية توفر للوحدات الأخرى مصفوفة من الألوان وتابعًا يختار إحداها عشوائيًا لتتمكن من استخدامهما.
</p>

<p>
	سنستخدم في الفقرة التالية الوحدة التي طورناها ضمن تطبيق آخر لنفهم فائدة الكائن <code>export</code> أكثر.
</p>

<h2>
	اختبار الوحدة البرمجية باستخدام REPL
</h2>

<p>
	يفضل قبل البدء باستخدام هذه الوحدة اختبارها أولًا للتأكد من صحة عملها، فسنستخدم في هذه الفقرة الوضع التفاعلي REPL لتحميل الوحدة colors واستدعاء التابع <code>getRandomColor()‎</code> التي توفره لنختبر صحة عمله.
</p>

<p>
	نبدأ أولًا جلسة REPL جديدة ضمن مجلد المشروع الحاوي على الملف index.js كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_27" style="">
<span class="pln">node</span></pre>

<p>
	نلاحظ ظهور الرمز <code>‎&gt;‎</code> في بداية السطر عند الدخول إلى وضع REPL ويمكن الآن إدخال أوامر وشيفرات جافاسكربت لتنفيذها فورًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_29" style="">
<span class="pln">colors </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./index'</span><span class="pun">);</span></pre>

<p>
	سيُحمّل التابع <code>‎require()‎</code> الوحدة colors وتحديدًا ملف المدخل entry point لها بعد الضغط على زر الإدخال ENTER لتنفيذ السطر السابق ونلاحظ ظهور الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_31" style="">
<span class="pun">{</span><span class="pln">
  getRandomColor</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Function</span><span class="pun">],</span><span class="pln">
  allColors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'brightred'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#E74C3C'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'soothingpurple'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#9B59B6'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'skyblue'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#5DADE2'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'leafygreen'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#48C9B0'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'sunkissedyellow'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#F4D03F'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'groovygray'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#D7DBDD'</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ظهرت لنا قيمة الوحدة البرمجية colors التي تم استيرادها، وهي عبارة عما صدّرناه منها، حيث يعيد التابع <code>require</code> عند استدعائه قيمة الكائن <code>exports</code> من الوحدة المستوردة وهي colors في حالتنا، والذي أضفنا إليه داخلها تابعًا بالاسم <code>getRandomColor()‎</code> وخاصيةً بالاسم <code>allColors</code>، وهو ما ظهر ضمن الخرج، ويمكننا الآن اختبار التابع <code>getRandomColor()‎</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_33" style="">
<span class="pln">colors</span><span class="pun">.</span><span class="pln">getRandomColor</span><span class="pun">();</span></pre>

<p>
	نلاحظ كيف أعاد لنا لونًا عشوائيًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_35" style="">
<span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'groovygray'</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#D7DBDD'</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	سيظهر لك لونًا مختلفًا عند تنفيذ الأمر في كل مرة، وذلك لأن الاختيار عشوائي، والآن بعد إتمام الاختبار يمكننا الخروج من جلسة REPL بتنفيذ أمر الخروج التالي الذي سيعيدنا إلى سطر الأوامر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_37" style="">
<span class="pun">.</span><span class="pln">exit</span></pre>

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

<h2>
	تثبيت وحدة منشأة محليًا كاعتمادية
</h2>

<p>
	استوردنا الوحدة البرمجية أثناء اختبارها ضمن صدفة REPL في الفقرة السابقة بذكر المسار النسبي لها، أي ذكرنا مسار مجلد الملف index.js بدءًا من المسار الحالي، ولا تُعتمد طريقة الاستيراد هذه إلا في حالات خاصة إذ تُستورد الوحدات بذكر أسمائها لتجنب المشاكل التي قد تحدث عند نقل مجلدات المشاريع التي نعمل عليها أو تعديل مساراتها، وسنثبت في هذه الفقرة الوحدة البرمجية colors باستخدام أمر التثبيت <code>install</code> من npm، لذلك ننشئ بدايةً وحدة برمجية جديدة خارج مجلد الوحدة colors، بالرجوع إلى المجلد الأب له وإنشاء مجلد جديد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_39" style="">
<span class="pln">cd </span><span class="pun">..</span><span class="pln">
mkdir really</span><span class="pun">-</span><span class="pln">large</span><span class="pun">-</span><span class="pln">application</span></pre>

<p>
	وننتقل لمجلد المشروع الجديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_41" style="">
<span class="pln">cd really</span><span class="pun">-</span><span class="pln">large</span><span class="pun">-</span><span class="pln">application</span></pre>

<p>
	ثم نُهيئ كما تعلمنا سابقًا ملف الحزمة package.json لهذا المشروع بتنفيذ الأمر:
</p>

<pre class="ipsCode">
npm init -y
</pre>

<p>
	سيتم توليد ملف package.json بالمحتوى التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_43" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"really-large-application"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نثبت الآن الوحدة colors كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_45" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save </span><span class="pun">../</span><span class="pln">colors
</span><span class="pun">.</span></pre>

<p>
	بذلك نكون قد ثبتنا الوحدة colors ضمن المشروع الجديد، ونعاين الآن الملف package.json لنرى كيف تُحفَظ الاعتماديات المحلية فيه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_47" style="">
<span class="pln">nano package</span><span class="pun">.</span><span class="pln">json</span></pre>

<p>
	نُلاحظ إضافة سطر جديد ضمن الخاصية <code>dependencies</code> يُذكر فيه اسم الوحدة ومسارها النسبي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_49" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"really-large-application"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"colors"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"file:../colors"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حيث نُسخِت الوحدة colors إلى مجلد الاعتماديات node_modules للمشروع الجديد، ويمكننا التأكد من ذلك باستعراض محتوياته باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_51" style="">
<span class="pln">ls node_modules</span></pre>

<p>
	يظهر اسم مجلد الاعتمادية موجودًا ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_53" style="">
<span class="pln">colors</span></pre>

<p>
	يمكن الآن استخدام تلك الوحدة ضمن هذا المشروع، لذلك نُنشئ ملف جافاسكربت جديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_55" style="">
<span class="pln">nano index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونستورد بدايةً الوحدة colors ونستخدم منها الدالة <code>getRandomColor()‎</code> لاختيار لون عشوائي، ثم نطبع رسالة إلى الطرفية تخبر المستخدم باللون الذي يمكنه استخدامه، لذا نكتب داخل الملف index.js الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_57" style="">
<span class="kwd">const</span><span class="pln"> colors </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'colors'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> chosenColor </span><span class="pun">=</span><span class="pln"> colors</span><span class="pun">.</span><span class="pln">getRandomColor</span><span class="pun">();</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">You</span><span class="pln"> should use $</span><span class="pun">{</span><span class="pln">chosenColor</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> on your website</span><span class="pun">.</span><span class="pln"> </span><span class="typ">It</span><span class="str">'s HTML code is ${chosenColor.code}`);</span></pre>

<p>
	نحفظ الملف ونخرج منه، والآن عند تنفيذ هذا البرنامج سيخبرنا بلون عشوائي يمكننا استخدامه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_59" style="">
<span class="pln">node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_61" style="">
<span class="typ">You</span><span class="pln"> should use leafygreen on your website</span><span class="pun">.</span><span class="pln"> </span><span class="typ">It</span><span class="str">'s HTML code is #48C9B0</span></pre>

<p>
	بهذا نكون ثبتنا الوحدة البرمجية colors ضمن المشروع ويمكننا التعامل معها وإدارتها كأي اعتمادية أخرى ضمن المشروع، لكن يجب الانتباه أنه في كل مرة نعدل شيفرة الوحدة colors مثلًا لإضافة ألوان جديدة يجب علينا حينها تنفيذ أمر الترقية <code>npm update</code> ضمن مشروع التطبيق لتحديث الاعتمادية واستخدام المزايا الجديدة، ولتجنب تكرار تنفيذ ذلك عند كل تعديل في الفقرة التالية سنستخدم الوحدة colors بطريقة مختلفة تمكننا من استخدام أحدث إصدار لها ضمن المشاريع المعتمدة عليها أثناء العمل عليها وتطويرها.
</p>

<h2>
	ربط وحدة محلية
</h2>

<p>
	قد نمر في حالة نعمل فيها على تطوير وحدة برمجية محليًا ونستخدمها في الوقت نفسه ضمن مشروع آخر، وسيصعب آنذاك ترقيتها باستمرار ضمن المشروع كما أشرنا سابقًا، والحل يكمن في ربط الوحدات البرمجية بدلًا من تثبيتها لاستخدامها مباشرة وهي قيد التطوير والبناء.
</p>

<p>
	سنتعلم ذلك في هذه الفقرة عن طريق ربط الوحدة colors ضمن التطبيق الذي يستخدمها، وسنختبر الربط بإجراء تعديلات على الوحدة colors ونتحقق من التحديث الآني لتلك التعديلات ضمن اعتمادية التطبيق دون الحاجة للترقية أو لتثبيت الوحدة من جديد، لذلك نزيل بدايةً تثبيت الوحدة من التطبيق بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_63" style="">
<span class="pln">npm un colors</span></pre>

<p>
	يربط <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">مدير الحزم npm</a> الوحدات البرمجية مع بعضها باستخدام الوصلات الرمزية symbolic links والتي تمثل مؤشرًا يشير إلى ملف أو مجلد ما ضمن نظام الملفات، ويُنفذ الربط هذا على مرحلتين:
</p>

<ol>
<li>
		إنشاء وصلة أو رابط عام global link للوحدة حيث يُنشئ npm وصلة رمزية بين مجلد الوحدة البرمجية ومجلد الاعتماديات العام node_modules الذي تُثبَّت فيه كل الحزم العامة على مستوى النظام كله، أي الحزم المُثبَّتة باستخدام الخيار <code>‎-g</code>.
	</li>
	<li>
		إنشاء وصلة محلية local link بحيث يُنشئ npm وصلة رمزية بين المشروع المحلي وبين الرابط العام للوحدة البرمجية المراد استخدامها فيه.
	</li>
</ol>
<p>
	ننشئ الرابط العام بالدخول إلى مجلد الوحدة colors واستخدام الأمر <code>link</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_66" style="">
<span class="pln">cd </span><span class="pun">../</span><span class="pln">colors
sudo npm link</span></pre>

<p>
	سيظهر لنا خرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_68" style="">
<span class="str">/usr/</span><span class="pln">local</span><span class="pun">/</span><span class="pln">lib</span><span class="pun">/</span><span class="pln">node_modules</span><span class="pun">/</span><span class="pln">colors </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="str">/home/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">colors</span></pre>

<p>
	أنشِئت بذلك وصلة رمزية في مجلد node_modules العام تشير إلى مجلد الوحدة colors، والآن نعود إلى مجلد المشروع really-large-application لربط الوحدة ضمنه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_70" style="">
<span class="pln">cd </span><span class="pun">../</span><span class="pln">really</span><span class="pun">-</span><span class="pln">large</span><span class="pun">-</span><span class="pln">application
sudo npm link colors</span></pre>

<p>
	سنلاحظ ظهور خرج مشابه للتالي:
</p>

<pre class="ipsCode">
/home/hassan/really-large-application/node_modules/colors -&gt; /usr/local/lib/node_modules/colors -&gt; /home/hassan/colors
</pre>

<p>
	<strong>ملاحظة</strong>: يمكن اختصار الأمر <code>link</code> بكتابة <code>ln</code> بدلًا منه، ليصبح أمر الربط كالتالي <code>npm ln colors</code> وسنحصل على نفس النتيجة.
</p>

<p>
	وكما يُظهر خرج أمر الربط السابق فقد أنشِئت وصلة رمزية في مجلد node_modules للمشروع really-large-application تشير إلى الوصلة الرمزية لمجلد الوحدة colors الموجودة في مجلد node_modules العام على مستوى النظام، والتي بدورها تشير إلى مجلد الوحدة colors الفعلي، وبهذا تكون عملية الربط اكتملت ويمكن تشغيل ملف المشروع للتأكد بأن الربط صحيح ولا زال المشروع يعمل كما هو:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_72" style="">
<span class="pln">node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_74" style="">
<span class="typ">OutputYou</span><span class="pln"> should use sunkissedyellow on your website</span><span class="pun">.</span><span class="pln"> </span><span class="typ">It</span><span class="str">'s HTML code is #F4D03F</span></pre>

<p>
	نلاحظ عدم تأثر المشروع ولا زال يعمل كما هو، والآن لنختبر ما إذا كانت التعديلات على الوحدة التي طورناها ستنعكس مباشرة ضمن المشروع الذي يستخدمها، لذلك نفتح الملف index.js الخاص بالوحدة colors ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_76" style="">
<span class="pln">cd </span><span class="pun">../</span><span class="pln">colors
nano index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونضيف مثلًا دالةً جديدةً مهمتها جلب درجة من درجات اللون الأزرق من الألوان المتوفرة، ولا تحتاج لتمرير معاملات وستعيد العنصر الثالث من مصفوفة الألوان المحلية <code>allColors</code> مباشرةً والذي هو من درجات اللون الأزرق، لذا نضيف الأسطر الأخيرة إلى الملف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_78" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Color</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">code </span><span class="pun">=</span><span class="pln"> code</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> allColors </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'brightred'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#E74C3C'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'soothingpurple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#9B59B6'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'skyblue'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#5DADE2'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'leafygreen'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#48C9B0'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'sunkissedyellow'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#F4D03F'</span><span class="pun">),</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Color</span><span class="pun">(</span><span class="str">'groovygray'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'#D7DBDD'</span><span class="pun">),</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">getRandomColor </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> allColors</span><span class="pun">[</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> allColors</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)];</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">allColors </span><span class="pun">=</span><span class="pln"> allColors</span><span class="pun">;</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">getBlue </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> allColors</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نحفظ الملف ونخرج منه، ونفتح ملف index.js ضمن مجلد المشروع really-large-application:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_80" style="">
<span class="pln">cd </span><span class="pun">../</span><span class="pln">really</span><span class="pun">-</span><span class="pln">large</span><span class="pun">-</span><span class="pln">application
nano index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونستخدم داخله الدالة الجديدة <code>‎getBlue()‎</code> المضافة إلى الوحدة ونطبع إلى الطرفية جملة تحوي خصائص ذلك اللون كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_82" style="">
<span class="kwd">const</span><span class="pln"> colors </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'colors'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> chosenColor </span><span class="pun">=</span><span class="pln"> colors</span><span class="pun">.</span><span class="pln">getRandomColor</span><span class="pun">();</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">You</span><span class="pln"> should use $</span><span class="pun">{</span><span class="pln">chosenColor</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> on your website</span><span class="pun">.</span><span class="pln"> </span><span class="typ">It</span><span class="str">'</span><span class="pln">s HTML code is $</span><span class="pun">{</span><span class="pln">chosenColor</span><span class="pun">.</span><span class="pln">code</span><span class="pun">}`);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> favoriteColor </span><span class="pun">=</span><span class="pln"> colors</span><span class="pun">.</span><span class="pln">getBlue</span><span class="pun">();</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">My</span><span class="pln"> favorite color is $</span><span class="pun">{</span><span class="pln">favoriteColor</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}/</span><span class="pln">$</span><span class="pun">{</span><span class="pln">favoriteColor</span><span class="pun">.</span><span class="pln">code</span><span class="pun">},</span><span class="pln"> btw</span><span class="pun">`);</span></pre>

<p>
	نحفظ الملف ونخرج منه، وبذلك يصبح المشروع يستخدم التابع الجديد الذي أنشأناه <code>‎getBlue()‎</code>، والآن ننفذ البرنامج ونرى النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_84" style="">
<span class="pln">node index</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سنحصل على خرج مشابه لما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1369_86" style="">
<span class="typ">OutputYou</span><span class="pln"> should use brightred on your website</span><span class="pun">.</span><span class="pln"> </span><span class="typ">It</span><span class="str">'</span><span class="pln">s HTML code is </span><span class="pun">#</span><span class="pln">E74C3C
</span><span class="typ">My</span><span class="pln"> favorite color is skyblue</span><span class="pun">/#</span><span class="lit">5DADE2</span><span class="pun">,</span><span class="pln"> btw</span></pre>

<p>
	نلاحظ كيف تمكنا من استخدام آخر التعديلات التي أجريناها ضمن الوحدة colors مباشرةً دون الحاجة لتنفيذ أمر الترقية <code>npm update</code> لتلك الوحدة، حيث يسهل ذلك عملية تطوير الوحدات البرمجية ويخفف من تكرار تنفيذ نفس الأوامر بكثرة.
</p>

<p>
	حاول التفكير دومًا عند تطوير التطبيقات الكبيرة والمعقدة نسبيًا كيف يمكن تجميع الشيفرات التي يتم تطويرها ضمن وحدات برمجية منفصلة تعتمد على بعضها، ويمكن إعادة استخدامها في عدة مشاريع، أما في حال كانت الوحدة البرمجية تستخدم فقط ضمن برنامج واحد عندها يفضل إبقاءها ضمن نفس مجلد المشروع ذاك وربطها عن طريق المسار النسبي لها، وأما في حال التخطيط لمشاركة الوحدة بشكل منفصل لاحقًا أو في استخدامها في مشروع مختلف عن المشروع الحالي فانظر إن كان الربط أنسب لحالتك أم التثبيت كما تعلمت إلى الآن، إذ الفائدة الأكبر من ربط الوحدات قيد التطوير استخدام أحدث إصدار منها دومًا دون الحالة لترقيتها كل حين، وإلا فمن الأسهل تثبيتها باستخدام الأمر <code>npm install</code>.
</p>

<h2>
	ختامًا
</h2>

<p>
	غصنا عميقًا في هذا المقال في وحدات نود والتي هي مجموعة من التوابع والكائنات في جافاسكربت خصوصًا في كيفية استخدامها من قبل البرامج الأخرى، فأنشأنا وحدة برمجية وحددنا داخلها بعض الدوال والكائنات كخصائص للكائن <code>exports</code> لإتاحتها للاستخدام من قبل التطبيقات الخارجية، واستوردنا تلك الوحدة إلى برنامج جديد واستخدمناها ضمنه.
</p>

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

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-node-js-module" rel="external nofollow">How To Create a Node.js Module</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/" rel="">إدارة الوحدات البرمجية في Node.js باستخدام npm وملف package.json</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1742</guid><pubDate>Sat, 08 Oct 2022 06:08:20 +0000</pubDate></item><item><title>&#x625;&#x62F;&#x627;&#x631;&#x629; &#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x641;&#x64A; Node.js &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; npm &#x648;&#x645;&#x644;&#x641; package.json</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-npm-%D9%88%D9%85%D9%84%D9%81-packagejson-r1728/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_10/633965c4dd5cb_----Node--npm-.png.f0e9ca85a82fe4c7974148d35be7255d.png" /></p>

<p>
	الشهرة والاستخدام الواسع لبيئة <a href="https://wiki.hsoub.com/Node.js" rel="external">نود Node.js</a> في تطوير تطبيقات النظم أو الواجهات الخلفية للويب سببها الأساسي مزايا السرعة والأداء العالي <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-javascript-r664/" rel="">للغة جافاسكربت</a> عند التعامل مع الدخل والخرج I/O، واعتمدت عليها العديد من التطبيقات كبيرة الحجم ما زاد تعقيد وصعوبة إدارة اعتمادياتها dependencies، حيث يوفر نود نظام تقسيم الشيفرة والاعتماديات إلى <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/" rel="">وحدات modules</a> لتنظيمها وحل تلك المشكلة، ومن أبسط أشكالها هي أي ملف جافاسكربت يحوي توابع وكائنات يمكن استخدامها من قبل البرامج أو الوحدات الأخرى، ويُدعى تجمع عدة وحدات معًا بالحزمة package، وتُدار مجموعة الحزم باستخدام برنامج مخصص لإدارة الحزم من أشهرها مدير حزم نود <a href="https://www.npmjs.com/" rel="external nofollow">npm</a>، والذي يأتي افتراضيًا مع نود ويستخدم لإدارة الحزم الخارجية في المشاريع المبنية ضمن نود، ويستخدم أيضًا لتثبيت العديد من أدوات <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> ولتشغيل النصوص أو السكربتات البرمجية للمشاريع، فهو يدير تلك الحزم ويخزن معلوماتها ضمن ملف يسمى package.json داخل مجلد المشروع ويحوي على معلومات مثل:
</p>

<ul>
<li>
		الحزم التي يعتمد عليها المشروع وأرقام الإصداراتها.
	</li>
	<li>
		معلومات تصف المشروع نفسه، كاسم المطور ورخصة الاستخدام وغيرها.
	</li>
	<li>
		السكربتات البرمجية الممكن تنفيذها، كالتي تؤتمت بعض المهام الخاصة بالمشروع.
	</li>
</ul>
<p>
	تساعد عملية إدارة البيانات الوصفية metadata والاعتماديات الخاصة بمشروع ضمن ملف واحد هو package.json على توحيد تلك المعلومات ومشاركتها خلال مرحلة تطوير أي مشروع برمجي على أي جهاز ومع أي مطور، حيث يُستخدم ذلك الملف من قبل مدير الحزم لإدارة تلك المعلومات تلقائيًا، ونادرًا ما نضطر لتعديل البيانات داخل هذا الملف يدويًا لإدارة الوحدات البرمجية المستخدمة في المشروع.
</p>

<p>
	سنستخدم في هذا المقال مدير حزم نود npm لإدارة الحزم وسنتعرف بالتفصيل على محتوى ملف package.json ونستخدمه لإدارة الوحدات البرمجية المُثبتة ضمن المشروع، وسنتعلم طريقة عرض الاعتماديات المستخدمة حاليًا وطريقة تحديثها أو إلغاء تثبيتها وفحصها للعثور على المشاكل الأمنية داخلها.
</p>

<p>
	ستحتاج للمتابعة وتطبيق الأمثلة في هذا المقال لتثبيت بيئة Node.js على جهازك، حيث استخدمنا في هذا المقال الإصدار رقم 18.3.0 وبذلك يكون قد ثُبت أيضًا مدير الحزم npm.
</p>

<h2>
	إنشاء ملف الحزمة package.json
</h2>

<p>
	لنبدأ بإعداد المشروع الذي سنطبق عليه كافة الخطوات اللاحقة، والذي سيكون عبارة عن حزمة لتحديد المواقع سنسميه locator، ووظيفته تحويل <a href="https://academy.hsoub.com/devops/networking/%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D8%B1%D8%B9%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%B9%D9%86%D8%A7%D9%88%D9%8A%D9%86-%D9%88%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-ip-r498/" rel="">عناوين IP</a> إلى اسم البلد المقابل لها، ولن نخوض في تفاصيل تضمين الشيفرة لذلك المشروع بل سيكون تركيزنا على جانب إدارة الحزم والاعتماديات للمشروع فقط، وسنستخدم في ذلك حزمًا خارجية كاعتماديات للمشروع وفي حال أردت تضمين المشروع بنفسك يمكنك استخدامها نفسها.
</p>

<p>
	بدايةً، نُنشئ ملفًا نسميه package.json، سيحوي على البيانات الوصفية للمشروع وتفاصيل الاعتماديات التي سيعتمد عليها، وكما تشير لاحقة ذلك الملف فمحتوياته ستكون مكتوبة <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">بصيغة JSON</a> وهي الصيغة المعتمدة لتخزين البيانات ومشاركتها على شكل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r796/" rel="">كائنات جافاسكربت objects</a>، وتتألف من أزواج من المفاتيح والقيم key/value المقابلة لها.
</p>

<p>
	وبما أن الملف package.json سيحوي العديد من البيانات يمكننا تجنب كتابتها يدويًا ونسخ ولصق قالب جاهز لتلك البيانات من مكان آخر، لهذا فإن أول ميزة سنتعرف عليها في مدير الحزم npm هو الأمر <code>init</code>، والذي سيسأل عند تنفيذه عدة أسئلة سيبني ملف package.json للمشروع تلقائيًا اعتمادًا على أجوبتنا لها.
</p>

<h3>
	استخدام الأمر init
</h3>

<p>
	أول خطوة هي إنشاء مجلد للمشروع الذي سنتدرب عليه من سطر الأوامر أو بأي طريقة أخرى ننشئ مجلدًا جديدًا بالاسم locator:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_14" style="">
<span class="pln">mkdir locator</span></pre>

<p>
	وننتقل إليه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_16" style="">
<span class="pln">cd locator</span></pre>

<p>
	والآن ننفذ أمر تهيئة ملف package.json:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_19" style="">
<span class="pln">npm init</span></pre>

<p>
	<strong>ملاحظة</strong>: إذا كنا ننوي استخدام <a href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%A7-%D9%87%D9%88-git%D8%9F-r1592/" rel="">مدير الإصدارات Git</a> لإدارة إصدارات المشروع وحفظ ملفاته، ننشئ مستودع Git داخل مجلد المشروع أولًا قبل تنفيذ أمر التهيئة <code>npm init</code>، وسيعلم حينها الأمر أن عملية التهيئة لملف الحزمة تتم بداخل مجلد يحوي مستودع Git، وإذا كان عنوان المستودع البعيد متاحًا ضمنه سيتم إضافة قيم للحقول <code>repository</code> و <code>bugs</code> و <code>homepage</code> تلقائيًا إلى ملف package.json، أما في حال تهيئة المستودع بعد تنفيذ أمر التهيئة سنحتاج حينها لإضافة تلك الحقول وتعيين قيمها يدويًا.
</p>

<p>
	بعد تنفيذ الأمر السابق سيظهر الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_21" style="">
<span class="typ">This</span><span class="pln"> utility will walk you through creating a package</span><span class="pun">.</span><span class="pln">json file</span><span class="pun">.</span><span class="pln">
</span><span class="typ">It</span><span class="pln"> only covers the most common items</span><span class="pun">,</span><span class="pln"> and tries to guess sensible defaults</span><span class="pun">.</span><span class="pln">

</span><span class="typ">See</span><span class="pln"> </span><span class="pun">`</span><span class="pln">npm help init</span><span class="pun">`</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> definitive documentation on these fields
and exactly what they </span><span class="kwd">do</span><span class="pun">.</span><span class="pln">

</span><span class="typ">Use</span><span class="pln"> </span><span class="pun">`</span><span class="pln">npm install </span><span class="pun">`</span><span class="pln"> afterwards to install a package and
save it as a dependency in the package</span><span class="pun">.</span><span class="pln">json file</span><span class="pun">.</span><span class="pln">

</span><span class="typ">Press</span><span class="pln"> </span><span class="pun">^</span><span class="pln">C at any time to quit</span><span class="pun">.</span><span class="pln">
package name</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">locator</span><span class="pun">)</span></pre>

<p>
	أول سؤال سنُسأل عنه هو اسم المشروع <code>name</code>، فإن لم تُعط فستأخذ افتراضيًا اسم المجلد للمشروع، ونلاحظ دومًا اقتراح القيم الافتراضية بين القوسين <code>()</code>، وبما أن القيمة الافتراضية هي ما نريدها يمكننا الضغط على زر الإدخال ENTER مباشرةً لقبولها.
</p>

<p>
	السؤال التالي هو عن رقم إصدار المشروع <code>version</code>، حيث أنها ضرورية مع اسم المشروع في حال مشاركة الحزمة التي سنطورها في مستودع حزم npm، فتستخدم حزم نود عادة <a href="https://semver.org/lang/ar/" rel="external nofollow">الترقيم الدلالي Semantic Versioning</a> لإصداراتها، وفيها يدل الرقم الأول على الإصدار الأساسي <code>MAJOR</code> الذي يشير أنه أجريت تغييرات جذرية على الحزمة، والرقم الثاني يدل على الإصدار الثانوي <code>MINOR</code> الذي يشير لإضافة مزايا على الحزمة، والرقم الثالث والأخير يدل على إصدار الترقيع <code>PATCH</code> الذي يشير لتصحيح أخطاء ضمن الحزمة.
</p>

<p>
	نضغط على زر الإدخال ENTER لقبول القيمة الافتراضية لأول إصدار من الحزمة وهو <code>1.0.0</code>.
</p>

<p>
	الحقل التالي هو حقل الوصف للمشروع <code>description</code> وهو شرح مختصر عن المشروع ووظيفته يفيد عند البحث عن تلك الحزمة من قبل المستخدمين إن نُشر على <a href="https://academy.hsoub.com/devops/networking/%D8%A2%D9%84%D9%8A%D8%A9-%D8%B9%D9%85%D9%84-%D8%B4%D8%A8%D9%83%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r571/" rel="">الإنترنت</a>، والحزمة locator التي سنطورها وظيفتها جلب عنوان IP للمستخدم وإعادة اسم البلد الذي ينتمي له هذا العنوان، وهنا يمكننا كتابة وصف معبر عن وظيفة هذه الحزمة باللغة الإنكليزية شبيه بالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_24" style="">
<span class="typ">Finds</span><span class="pln"> the country of origin of the incoming request</span></pre>

<p>
	السؤال التالي هو عن الملف الأساسي أو المدخل للمشروع entry point فعند تثبيت أي حزمة واستخدامها ضمن مشروع آخر واستيرادها فإن أول ما سيُحمّل هو الملف الذي سنحدده في هذا الحقل، وقيمة المسار للملف المحدد في الحقل <code>main</code> يجب أن تكون نسبةً لمجلد المشروع الجذري الذي أول ما يحوي فيه الملف package.json، ويمكننا قبول القيمة الافتراضية المقترحة والضغط على زر الإدخال ENTER باعتبار أن الملف index.js سيكون المدخل هنا.
</p>

<p>
	<strong>ملاحظة</strong>: تستخدم معظم الحزم الملف index.js كمدخل لها، لهذا تعتبر هذه القيمة الافتراضية للحقل <code>main</code> كمدخل لوحدات npm، وحتى عند غياب ملف package.json من مجلد الوحدة ستحاول نود افتراضيًا تحميل الملف index.js من مجلد جذر الحزمة المُستخدمة.
</p>

<p>
	السؤال التالي هو عن أمر تنفيذ اختبارات الحزمة test command، وقيمته يمكن أن تكون إما مسار لملف تنفيذي أو أمر لتشغيل اختبارات المشروع، وتستخدم معظم وحدات نود الشهيرة أطر اختبار مثل <a href="https://mochajs.org/" rel="external nofollow">Mocha</a> أو <a href="https://jestjs.io/" rel="external nofollow">Jest</a> أو <a href="https://jasmine.github.io/" rel="external nofollow">Jasmine</a> أو غيرها لكتابة اختبارات المشروع، ويمكننا ترك قيمة هذا الحقل فارغة بالضغط على زر الإدخال.
</p>

<p>
	سنُسأل بعدها عن عنوان مستودع Git للمشروع، هنا نُدخل مسار المستودع للمشروع الحالي الذي قد يكون مُستضافًا على أحد الخدمات الشهيرة مثل GitHub، ويمكنك ترك قيمته فارغة أيضًا.
</p>

<p>
	سيُطلب منا بعدها إدخال بعض الكلمات المفتاحية كقيمة للحقل <code>keywords</code>، والقيمة عبارة عن مصفوفة من السلاسل النصية تحوي مصطلحات وكلمات مفتاحية ستفيد المستخدمين عند البحث عن الحزمة عند نشرها عبر الإنترنت، لذا يفضل إدخال بعض الكلمات القصيرة التي تتعلق بعمل الحزمة لتزداد فرصة العثور عليها وظهورها ضمن عمليات البحث، وندخل الكلمات المفتاحية مفصولًا بينها بفاصلة، فمثلًا لمشروعنا يمكن إدخال بعض الكلمات كالتالي <code>ip,geo,country</code>، ينتج عن ذلك مصفوفة تحوي ثلاث عناصر كقيمة للحقل <code>keywords</code> داخل الملف package.json.
</p>

<p>
	الحقل التالي هو اسم صاحب المشروع أو الكاتب والمطور له <code>author</code>، حيث يفيد إدخال تلك المعلومة المستخدمين الراغبين بالتواصل معه لأي سبب، مثل اكتشاف ثغرة أو مشكلة في عمل الحزمة، وتكون قيمة هذا الحقل سلسة نصية بالصيغة التالية: <code>"الاسم &lt;عنوان البريد الإلكتروني&gt; (موقع الويب)"</code> مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_27" style="">
<span class="str">"Hassan &lt;hassan@example.com&gt; (https://mywebsite.com)"</span></pre>

<p>
	وإدخال عنوان البريد الإلكتروني وموقع الويب اختياريان ويمكن الاكتفاء بإدخال الاسم فقط.
</p>

<p>
	القيمة الأخيرة هي لحقل رخصة الاستخدام <code>license</code>، حيث يحدد ذلك الصلاحيات القانونية والحدود المسموح بها استخدام هذه الحزمة أو المشروع، وبما أن أغلب حزم نود <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر</a> لذا القيمة الافتراضية المقترحة هي رخصة <a href="https://www.npmjs.com/package/isc-license" rel="external nofollow">ISC</a>، لذا يجب قبل تعيين تلك القيمة مراجعة الرخص المتاحة واختيار المناسبة منها للمشروع، ويمكنك الاطلاع على معلومات أكثر على <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B1%D8%A7%D8%AE%D9%8A%D8%B5-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-r1586/" rel="">رخص المشاريع المفتوحة المصدر</a> وفي حال كانت الحزمة مطورة للاستخدام الخاص وليست للمشاركة يمكن إدخال القيمة <code>UNLICENSED</code> لتحديد الحزمة كغير مرخصة للاستخدام العام أبدًا، ولمشروعنا الحالي يمكن استخدام القيمة الافتراضية بالضغط على زر الإدخال وإنهاء تهيئة وإنشاء الملف.
</p>

<p>
	سيعرض بعد ذلك الأمر <code>init</code> محتوى ملف package.json الذي سيُنشئه لنراجعه ونتأكد من جميع القيم وسيظهر خرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_30" style="">
<span class="typ">About</span><span class="pln"> to write to </span><span class="pun">/</span><span class="pln">home</span><span class="pun">/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">locator</span><span class="pun">/</span><span class="pln">package</span><span class="pun">.</span><span class="pln">json</span><span class="pun">:</span><span class="pln">

</span><span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"locator"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Finds the country of origin of the incoming request"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="str">"ip"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"geo"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"country"</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hassan &lt;hassan@your_domain&gt; (https://your_domain)"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="typ">Is</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> OK</span><span class="pun">?</span><span class="pln"> </span><span class="pun">(</span><span class="pln">yes</span><span class="pun">)&lt;/</span><span class="pln">hassan@your_domain</span><span class="pun">&gt;</span></pre>

<p>
	في حال كانت كل البيانات صحيحة نضغط زر الإدخال للإنهاء وإنشاء ملف package.json وبعدها يمكننا تثبيت الوحدات البرمجية الخارجية ليعتمد عليها مشروعنا وتضاف تفاصيلها في ذلك الملف.
</p>

<h2>
	تثبيت الوحدات البرمجية
</h2>

<p>
	عند تطوير المشاريع البرمجية عادة ما نفوض المهام التي لا تتعلق بصلب عمل المشروع إلى مكتبات برمجية خارجية متخصصة في ذلك، ما يتيح للمطور التركيز على عمل المشروع الحالي فقط وتطوير التطبيق بسرعة وكفاءة أكبر عبر استخدام الأدوات والشيفرات البرمجية التي طورها الآخرون على مبدأ لا تخترع العجلة من جديد، فمثلًا إذا احتاج مشروعنا locator لإرسال طلب خارجي إلى <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> لخدمة تقدم البيانات الجغرافية اللازمة لنا وهنا يمكننا استخدام مكتبة خاصة بإرسال <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">طلبات HTTP</a> مباشرةً بدلًا من كتابة ذلك بأنفسنا، حيث وظيفة المشروع هي تقديم تلك البيانات الجغرافية إلى مستخدم الحزمة فقط، وأما تفاصيل إرسال طلبات <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> لا تتعلق بوظيفة الحزمة لذا يمكن تفويضها لمكتبة خارجية جاهزة مختصة بذلك، يمكننا مثلًا استخدام مكتبة <a href="https://github.com/axios/axios" rel="external nofollow">axios</a> والتي تساعد في إرسال طلبات HTTP بشكل عملي وسهل، ولتثبيتها ننفذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_34" style="">
<span class="pln">npm install axios </span><span class="pun">--</span><span class="pln">save</span></pre>

<p>
	الجزء الأول من هذا الأمر <code>npm install</code> هو أمر تثبيت الحزم، ويمكن اختصارًا تنفيذه كالتالي <code>npm i</code>، حيث نمرر له أسماء الحزم التي نرغب بتثبيتها مفصولة بفراغات بينها وفي حالتنا نريد فقط تثبيت حزمة مكتبة <code>axios</code>، بعدها واختياريًا يمكن تمرير الخيار <code>‎--save</code> لحفظ المكتبات المُثبتة كاعتماديات للمشروع ضمن ملف package.json وهو السلوك الافتراضي حتى دون ذكر ذلك الخيار، وبعد تثبيت المكتبة سنلاحظ ظهور خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_36" style="">
<span class="pun">...</span><span class="pln">
</span><span class="pun">+</span><span class="pln"> axios@0</span><span class="pun">.</span><span class="lit">27.2</span><span class="pln">
added </span><span class="lit">5</span><span class="pln"> packages from </span><span class="lit">8</span><span class="pln"> contributors and audited </span><span class="lit">5</span><span class="pln"> packages in </span><span class="lit">0.764s</span><span class="pln">
found </span><span class="lit">0</span><span class="pln"> vulnerabilities</span></pre>

<p>
	والآن باستخدام أي محرر نصوص نعاين محتوى الملف package.json لنلاحظ التغييرات، سنستخدم مثلًا محرر <code>nano</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_39" style="">
<span class="pln">nano package</span><span class="pun">.</span><span class="pln">json</span></pre>

<p>
	نلاحظ ظهور خاصية جديدة بالاسم <code>dependencies</code> أو الاعتماديات، والتي تحوي على اعتماديات المشروع الحالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_41" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"locator"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Finds the country of origin of the incoming request"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="str">"ip"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"geo"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"country"</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hassan hassan@your_domain (https://your_domain)"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"axios"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^0.27.2"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وإضافة الوحدة البرمجية التي ثبتناها مع رقم إصدارها يحدد للمطورين الآخرين العاملين على نفس المشروع الاعتماديات الخارجية التي يتطلبها تشغيله.
</p>

<p>
	<strong>ملاحظة</strong>: قد انتبهت إلى وجود الرمز <code>^</code> قبل رقم الإصدار لاعتمادية <code>axios</code>، وبما أن <a href="https://semver.org/lang/ar/" rel="external nofollow">الترقيم الدلالي</a> يحوي ثلاثة أرقام وهي الأساسي الجذري MAJOR والثانوي البسيط MINOR والترقيع PATCH فيشير ذلك الرمز إلى تثبيت الإصدار الأساسي للاعتمادية ولا مانع من تغير الإصدار الثانوي البسيط أو إصدار الترقيع أي يمكن تنزيل الإصدار 0.28.0 أو 0.28.1 مثلًا واستخدامه ضمن المشروع، ويمكن استخدام الرمز <code>~</code> أيضًا لتثبيت الإصدار الأساسي والثانوي وسماحية تغير إصدار الترقيع فقط أي يُقبَل إصدار 0.27.3 أو 0.27.4 مثلًا.
</p>

<p>
	ويمكننا إغلاق الملف package.json الآن بعد الانتهاء من الاطلاع عليه، وفي حال استخدام محرر nano يمكن الخروج بالضغط على <code>CTRL + X</code> ثم <code>ENTER</code>.
</p>

<h3>
	اعتماديات لازمة أثناء تطوير المشروع
</h3>

<p>
	اعتماديات التطوير development dependencies هي الاعتماديات التي ستُستخدم فقط خلال مرحلة تطوير المشروع وليس خلال مراحل بناء المشروع ونشره ولا يعتمد عليها خلال مرحلة الإنتاج production وتشبه تلك الدعامات والسلالم والسقالات التي توضع أثناء بناء عمارة ثم تُزال عند الانتهاء، فمثلًا يستخدم المطورون عادة مكتبات لفحص الشيفرات البرمجية وكشف الأخطاء المحتملة وتوحيد تنسيق كتابة الشيفرات أو ما يدعى <a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">Linter</a>.
</p>

<p>
	لنجرب تثبيت اعتمادية تطوير لتنقيح صياغة الشيفرات تدعى eslint ضمن المشروع بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_43" style="">
<span class="pln">npm i eslint@8</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln"> </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev</span></pre>

<p>
	نلاحظ إضافة الخيار <code>‎--save-dev</code>، والذي يخبر npm بحفظ الاعتماديات التي نثبتها كاعتمادية تطوير فقط، لاحظ أيضًا إضافة اللاحقة <code>‎@8.0.0</code> بعد اسم الاعتمادية حيث يتم وسم إصدارات المكتبات عند تحديثها، ويدل الرمز <code>@</code> مدير الحزم npm أن يثبت إصدار معين من تلك الاعتمادية وفي حال تجاهلنا إضافة ذلك الوسم سيتم تثبيت آخر نسخة موسومة متاحة من تلك الاعتمادية، والآن لنعاين ملف package.json مجددًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_45" style="">
<span class="pln">nano package</span><span class="pun">.</span><span class="pln">json</span></pre>

<p>
	ونلاحظ تغير محتواه وإضافة اعتمادية التطوير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_47" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"locator"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Finds the country of origin of the incoming request"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"index.js"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"keywords"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="str">"ip"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"geo"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"country"</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hassan hassan@your_domain (https://your_domain)"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"license"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ISC"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"dependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"axios"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^0.19.0"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"eslint"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^8.0.0"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ إضافة الاعتمادية <code>eslint</code> ضمن الحقل <code>devDependencies</code> مع رقم الإصدار الذي حددناه لها.
</p>

<h3>
	المجلد node_modules والملف package-lock.json المولدان تلقائيا
</h3>

<p>
	عند أول تثبيت لأي حزمة ضمن مشروع نود سيُنشئ npm تلقائيًا المجلد node_modules ليُخزن ضمنه كل الوحدات البرمجية التي يحتاج إليها المشروع الحالي، وأيضًا سيُنشئ الملف package-lock.json والذي يحوي معلومات عن تفاصيل إصدارات المكتبات المُثبتة في المشروع، ولنتأكد من وجود تلك الملفات ضمن مجلد المشروع يمكننا ذلك بتنفيذ الأمر <code>ls</code> في سطر الأوامر لعرض الملفات الموجودة وسيظهر لنا التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_49" style="">
<span class="pln">node_modules    package</span><span class="pun">.</span><span class="pln">json    package</span><span class="pun">-</span><span class="pln">lock</span><span class="pun">.</span><span class="pln">json</span></pre>

<p>
	يحوي المجلد node_modules كافة الاعتماديات المُثبتة في المشروع، وعادة لا نضيف هذا المجلد إلى مستودع المشروع لأن حجم هذا المجلد سيكبر بعد تثبيتنا لعدة اعتماديات، ولأن ملف package-lock.json يحوي داخله أساسًا تفاصيل إصدارات المكتبات المثبتة ضمن مجلد node_modules تمامًا كما هي، ما يجعل وجود ذلك المجلد ضمن مستودع المشروع غير ضروري.
</p>

<p>
	ويحوي الملف package.json على قائمة بالاعتماديات المقبولة لاستخدامها ضمن المشروع، بينما يحوي الملف package-lock.json على كل التغييرات التي تحدث على ملف package.json أو مجلد node_modules ويحوي أيضًا على أرقام إصدارات الحزم المثبتة بدقة، ويمكن إضافة هذا الملف إلى مستودع المشروع عادة بدلًا من مجلد node_modules لأن محتواه يعبر عن جميع اعتماديات المشروع بكافة تفاصيلها.
</p>

<h3>
	تثبيت الاعتماديات باستخدام package.json
</h3>

<p>
	يمكن باستخدام الملفين package.json و package-lock.json إعداد الاعتماديات المحددة فيهما لبدء أو استئناف العمل على تطوير مشروع مع فريق، ولنفهم ذلك أكثر يمكننا إنشاء مجلد جديد فارغ بجوار مجلد المشروع الحالي بالاسم <code>cloned_locator</code> بتنفيذ الأوامر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_51" style="">
<span class="pln">cd </span><span class="pun">..</span><span class="pln">
mkdir cloned_locator</span></pre>

<p>
	ثم ننتقل إلى ذلك المجلد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_53" style="">
<span class="pln">cd cloned_locator</span></pre>

<p>
	ننسخ الآن ملفي package.json و package-lock.json من مجلد المشروع الأصلي locator إلى المجلد الجديد <code>cloned_locator</code> بتنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_57" style="">
<span class="pln">cp </span><span class="pun">../</span><span class="pln">locator</span><span class="pun">/</span><span class="pln">package</span><span class="pun">.</span><span class="pln">json </span><span class="pun">../</span><span class="pln">locator</span><span class="pun">/</span><span class="pln">package</span><span class="pun">-</span><span class="pln">lock</span><span class="pun">.</span><span class="pln">json </span><span class="pun">.</span></pre>

<p>
	والآن يمكننا تثبيت نفس اعتماديات المشروع الأصلي بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_55" style="">
<span class="pln">npm i</span></pre>

<p>
	سيتحقق بعدها npm من وجود ملف package-lock.json داخل المجلد الحالي، وفي حال عدم وجوده سيقرأ محتويات ملف package.json لمعرفة الاعتماديات المطلوب تثبيتها، وعادة تكون عملية التثبيت أسرع عند وجود ملف package-lock.json لأنه يحوي الأرقام الدقيقة لإصدارات الاعتماديات المطلوبة، ولن يحتاج حينها npm للبحث عن أرقام إصدارات تناسب المشروع.
</p>

<p>
	وكما ذكرنا، يمكن تجاهل تثبيت اعتماديات التطوير عند نشر التطبيق في مرحلة الإنتاج، وهي الاعتماديات المذكورة في ملف package.json ضمن الحقل <code>devDependencies</code> ولا تؤثر أبدًا على عمل التطبيق، لذا عند تثبيت المشروع خلال عملية نشر التطبيق يمكن تجاهل تثبيت تلك الاعتماديات بتنفيذ أمر التثبيت كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_59" style="">
<span class="pln">npm i </span><span class="pun">--</span><span class="pln">production</span></pre>

<p>
	حيث يشير الخيار <code>‎--production</code> إلى تجاهل اعتماديات التطوير خلال عملية تثبيت اعتماديات المشروع، ولن نستعمل هذا الخيار إلا في حالات محدَّدة فقط تتعمل بمرحلة بناء المشروع وتجهيزه للنشر على الإنترنت.
</p>

<p>
	ولا ننسَ أيضًا العودة إلى مجلد المشروع الأساسي قبل لمتابعة تطبيق باقي الأمثلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_61" style="">
<span class="pln">cd </span><span class="pun">../</span><span class="pln">locator</span></pre>

<h3>
	تثبيت الحزم على مستوى النظام
</h3>

<p>
	ثبتنا حتى الآن الاعتماديات الخاصة بمشروعنا locator، ولكن يمكن استخدام npm أيضًا للتثبيت اعتماديات وحزم على مستوى نظام التشغيل، ما يعني أن الحزمة المثبتة بتلك الطريقة ستكون متاحة للمستخدم في أي مكان ضمن النظام بشكل مشابه للأوامر المتوفرة في سطر الأوامر، حيث تفيد هذه الميزة باستخدام الوحدات البرمجية كأدوات سطر الأوامر لتنفيذ مهام محددة في المشروع، فمثلًا يمكن استخدام مكتبة <a href="https://hexo.io" rel="external nofollow">Hexo</a> من سطر الأوامر من أي مكان بعد تثبيتها لإنشاء موقع لمدونة بمحتوى ثابت، وذلك بتنفيذ أمر التثبيت العام كالتالي:
</p>

<pre class="ipsCode">
npm i hexo-cli -g
</pre>

<p>
	كما نلاحظ إذا أردنا تثبيت أي حزمة عامة سنضيف الخيار <code>‎-g</code> -اختصارًا إلى الكلمة Global عام- لنهاية أمر التثبيت فقط.
</p>

<p>
	<strong>ملاحظة</strong>: قد يظهر خطأ عند محاولة تثبيت حزمة عامة والسبب قد يكون في صلاحيات المستخدم الحالي، لذا قد تحتاج لصلاحيات مستخدم مسؤول وحاول آنذاك فتح الطرفية بصلاحية مسؤول super user أو إذا كنت تستخدم نظام شبيه بيونكس يمكن تنفيذ الأمر كالتالي: <code>sudo npm i hexo-cli -g</code>.
</p>

<p>
	ويمكن التأكد من نجاح عملية التثبيت للمكتبة بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_64" style="">
<span class="pln">hexo </span><span class="pun">--</span><span class="pln">version</span></pre>

<p>
	سيظهر خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_68" style="">
<span class="pln">hexo</span><span class="pun">-</span><span class="pln">cli</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4.3</span><span class="pun">.</span><span class="lit">0</span><span class="pln">
os</span><span class="pun">:</span><span class="pln"> linux </span><span class="lit">5.15</span><span class="pun">.</span><span class="lit">0</span><span class="pun">-</span><span class="lit">35</span><span class="pun">-</span><span class="pln">generic </span><span class="typ">Ubuntu</span><span class="pln"> </span><span class="lit">22.04</span><span class="pln"> LTS </span><span class="lit">22.04</span><span class="pln"> LTS </span><span class="pun">(</span><span class="typ">Jammy</span><span class="pln"> </span><span class="typ">Jellyfish</span><span class="pun">)</span><span class="pln">
node</span><span class="pun">:</span><span class="pln"> </span><span class="lit">18.3</span><span class="pun">.</span><span class="lit">0</span><span class="pln">
v8</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10.2</span><span class="pun">.</span><span class="lit">154.4</span><span class="pun">-</span><span class="pln">node</span><span class="pun">.</span><span class="lit">8</span><span class="pln">
uv</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.43</span><span class="pun">.</span><span class="lit">0</span><span class="pln">
zlib</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.2</span><span class="pun">.</span><span class="lit">11</span><span class="pln">
brotli</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.0</span><span class="pun">.</span><span class="lit">9</span><span class="pln">
ares</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.18</span><span class="pun">.</span><span class="lit">1</span><span class="pln">
modules</span><span class="pun">:</span><span class="pln"> </span><span class="lit">108</span><span class="pln">
nghttp2</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.47</span><span class="pun">.</span><span class="lit">0</span><span class="pln">
napi</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pln">
llhttp</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6.0</span><span class="pun">.</span><span class="lit">6</span><span class="pln">
openssl</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3.0.3+quic</span><span class="pln">
cldr</span><span class="pun">:</span><span class="pln"> </span><span class="lit">41.0</span><span class="pln">
icu</span><span class="pun">:</span><span class="pln"> </span><span class="lit">71.1</span><span class="pln">
tz</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2022a</span><span class="pln">
unicode</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14.0</span><span class="pln">
ngtcp2</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">.</span><span class="lit">0</span><span class="pun">-</span><span class="pln">DEV
nghttp3</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">.</span><span class="lit">0</span><span class="pun">-</span><span class="pln">DEV</span></pre>

<p>
	تعلمنا كيف يمكن تثبيت الوحدات البرمجية الخارجية باستخدام npm، وكيف أنه يمكن تثبيت الحزم محليًا إما كاعتمادية إنتاج أو تطوير، وشاهدنا كيف يمكن تثبيت الحزم باستخدام ملف package.json بمفرده أو مع ملف package-lock.json مجهزة مسبقًا لتوحيد تثبيت إصدارات الاعتماديات للمشروع بين أفراد فريق المطورين، وتعلمنا كيف يمكن تثبيت الحزم عمومًا على النظام باستخدام الخيار <code>‎-g</code> لنتمكن من استخدامها من أي مكان سواء داخل مشروع نود أو خارجه.
</p>

<p>
	والآن بعد ما تعلمناه من طرق لتثبيت الوحدات البرمجية، سنتعلم في الفقرة التالية طرق إدارة تلك الاعتماديات.
</p>

<h2>
	إدارة الوحدات البرمجية
</h2>

<p>
	لا يقتصر دور مدير الحزم على تثبيت الوحدات البرمجية بل يتوسع إلى تنفيذ العديد من المهام الأخرى التي تتعلق بإدارة الحزم بعد تثبيتها فمثلًا يحوي npm على أكثر من 20 أمرًا يتعلق بذلك، حيث سنتعرف في هذه الفقرة على بعضها والتي تقوم بما يلي:
</p>

<ul>
<li>
		عرض الوحدات البرمجية المثبتة.
	</li>
	<li>
		ترقية الوحدات البرمجية إلى إصداراتها الأحدث.
	</li>
	<li>
		إلغاء تثبيت الوحدات البرمجية التي لا نحتاج إليها.
	</li>
	<li>
		فحص الوحدات البرمجية لتحديد الثغرات الأمنية وإصلاحها.
	</li>
</ul>
<p>
	سنطبق الأوامر المتعلقة بتلك المهام على مجلد مشروعنا locator، ويمكن تنفيذ نفس تلك المهام بشكل عام عبر إضافة الخيار <code>‎-g</code> في نهاية الأوامر، كما فعلنا عند تثبيت حزمة عامة على مستوى النظام سابقًا.
</p>

<h3>
	عرض قائمة بالوحدات المثبتة
</h3>

<p>
	يمكن معرفة الوحدات البرمجية المُثبتة ضمن مشروع ما بتنفيذ الأمر <code>list</code> أو <code>ls</code> الخاص بمدير الحزم npm بدلًا من معاينة الملف package.json يدويًا، وذلك بتنفيذ الأمر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_70" style="">
<span class="pln">npm ls</span></pre>

<p>
	ليظهر لنا خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_72" style="">
<span class="pun">├──</span><span class="pln"> axios@0</span><span class="pun">.</span><span class="lit">27.2</span><span class="pln">
</span><span class="pun">└──</span><span class="pln"> eslint@8</span><span class="pun">.</span><span class="lit">0.0</span></pre>

<p>
	يمكن إضافة الخيار <code>‎--depth</code> لتحديد مستوى عرض شجرة الاعتماديات السابقة، فمثلًا عندما نمرر له القيمة <code>0</code> سيظهر لنا الاعتماديات في أول مستوى فقط وهي اعتماديات المشروع الحالي فقط كما لو نفذنا الأمر <code>npm ls</code> دون خيارات إضافية، ويمكن إضافة الخيار <code>‎--all</code> لعرض شجرة الاعتماديات كاملة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_74" style="">
<span class="pln">npm ls </span><span class="pun">--</span><span class="pln">all</span></pre>

<p>
	ليظهر خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_76" style="">
<span class="pun">├─┬</span><span class="pln"> axios@0</span><span class="pun">.</span><span class="lit">27.2</span><span class="pln">
</span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> follow</span><span class="pun">-</span><span class="pln">redirects@1</span><span class="pun">.</span><span class="lit">15.1</span><span class="pln">
</span><span class="pun">│</span><span class="pln"> </span><span class="pun">└─┬</span><span class="pln"> form</span><span class="pun">-</span><span class="pln">data@4</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln">
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> asynckit@0</span><span class="pun">.</span><span class="lit">4.0</span><span class="pln">
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├─┬</span><span class="pln"> combined</span><span class="pun">-</span><span class="pln">stream@1</span><span class="pun">.</span><span class="lit">0.8</span><span class="pln">
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">│</span><span class="pln"> </span><span class="pun">└──</span><span class="pln"> delayed</span><span class="pun">-</span><span class="pln">stream@1</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln">
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">└─┬</span><span class="pln"> mime</span><span class="pun">-</span><span class="pln">types@2</span><span class="pun">.</span><span class="lit">1.35</span><span class="pln">
</span><span class="pun">│</span><span class="pln">     </span><span class="pun">└──</span><span class="pln"> mime</span><span class="pun">-</span><span class="pln">db@1</span><span class="pun">.</span><span class="lit">52.0</span><span class="pln">
</span><span class="pun">└─┬</span><span class="pln"> eslint@8</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln">
  </span><span class="pun">├─┬</span><span class="pln"> </span><span class="lit">@eslint</span><span class="pun">/</span><span class="pln">eslintrc@1</span><span class="pun">.</span><span class="lit">3.0</span><span class="pln">
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> ajv@6</span><span class="pun">.</span><span class="lit">12.6</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> debug@4</span><span class="pun">.</span><span class="lit">3.4</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> espree@9</span><span class="pun">.</span><span class="lit">3.2</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> globals@13</span><span class="pun">.</span><span class="lit">15.0</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> ignore@5</span><span class="pun">.</span><span class="lit">2.0</span><span class="pln">
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> </span><span class="kwd">import</span><span class="pun">-</span><span class="pln">fresh@3</span><span class="pun">.</span><span class="lit">3.0</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> js</span><span class="pun">-</span><span class="pln">yaml@4</span><span class="pun">.</span><span class="lit">1.0</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">├──</span><span class="pln"> minimatch@3</span><span class="pun">.</span><span class="lit">1.2</span><span class="pln"> deduped
  </span><span class="pun">│</span><span class="pln"> </span><span class="pun">└──</span><span class="pln"> strip</span><span class="pun">-</span><span class="pln">json</span><span class="pun">-</span><span class="pln">comments@3</span><span class="pun">.</span><span class="lit">1.1</span><span class="pln"> deduped

</span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span></pre>

<h3>
	ترقية الوحدات البرمجية
</h3>

<p>
	التحديث الدوري للوحدات البرمجية المستخدمة ضمن المشروع مهم جدًا للحصول على آخر الإصلاحات والتحسينات الأمنية عليها، لذلك يمكن استخدام الأمر <code>outdated</code> لعرض الوحدات البرمجية التي يتوفر لها تحديثات توافق متطلبات المشروع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_78" style="">
<span class="pln">npm outdated</span></pre>

<p>
	سيظهر خرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_81" style="">
<span class="typ">Package</span><span class="pln">  </span><span class="typ">Current</span><span class="pln">  </span><span class="typ">Wanted</span><span class="pln">  </span><span class="typ">Latest</span><span class="pln">  </span><span class="typ">Location</span><span class="pln">             </span><span class="typ">Depended</span><span class="pln"> by
eslint     </span><span class="lit">8.0</span><span class="pun">.</span><span class="lit">0</span><span class="pln">  </span><span class="lit">8.17</span><span class="pun">.</span><span class="lit">0</span><span class="pln">  </span><span class="lit">8.17</span><span class="pun">.</span><span class="lit">0</span><span class="pln">  node_modules</span><span class="pun">/</span><span class="pln">eslint  locator</span></pre>

<p>
	يحوي العمود الأول <code>Package</code> من الجدول السابق على أسماء الحزم الممكن ترقيتها، والعمود الثاني <code>Current</code> يُظهر رقم الإصدار الحالي للحزمة المثبتة ضمن المشروع، والعمود <code>Wanted</code> يُظهر رقم آخر إصدار يوافق متطلبات المشروع من الحزمة المطلوب ترقيتها والعمود <code>Latest</code> يُظهر آخر إصدار منشور من تلك الحزمة وقد لا يوافق متطلبات المشروع، والعمود <code>Location</code> يُظهر مسار مجلد الحزمة الحالي، حيث يمكن تمرير الخيار <code>‎--depth</code> أيضًا للأمر <code>outdated</code> تمامًا كما فعلنا مع الأمر <code>ls</code>، وتكون قيمته الافتراضية هي الصفر.
</p>

<p>
	ونجد من الخرج السابق أن الحزمة <code>eslint</code> يمكن ترقيتها إلى إصدار أحدث، لهذا يمكن استخدام أمر الترقية <code>update</code> أو اختصاره <code>up</code> مع ذكر أسماء الحزم التي نرغب بترقيتها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_83" style="">
<span class="pln">npm up eslint</span></pre>

<p>
	سيُظهر لنا خرج هذا الأمر رقم إصدار النسخة الجديدة المثبتة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_88" style="">
<span class="pln">removed </span><span class="lit">7</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> changed </span><span class="lit">4</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> and audited </span><span class="lit">91</span><span class="pln"> packages in </span><span class="lit">1s</span><span class="pln">

</span><span class="lit">14</span><span class="pln"> packages are looking </span><span class="kwd">for</span><span class="pln"> funding
  run </span><span class="pun">`</span><span class="pln">npm fund</span><span class="pun">`</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> details

found </span><span class="lit">0</span><span class="pln"> vulnerabilities</span></pre>

<p>
	وللتأكد من ذلك يمكننا الاستفادة من الأمر <code>npm ls</code> وتمرير اسم الحزمة <code>eslint</code> ليظهر لنا تفاصيل الحزمة المثبتة ضمن المشروع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_91" style="">
<span class="pln">npm ls eslint</span></pre>

<p>
	نلاحظ عند تمرير اسم حزمة معينة للأمر <code>npm ls</code> ستظهر لنا شجرة الاعتماديات المثبتة ضمن المشروع لكن ستحوي فقط على ما يخص الحزمة المحددة <code>eslint</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_93" style="">
<span class="pun">└─┬</span><span class="pln"> eslint@8</span><span class="pun">.</span><span class="lit">17.0</span><span class="pln">
  </span><span class="pun">└─┬</span><span class="pln"> eslint</span><span class="pun">-</span><span class="pln">utils@3</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln">
    </span><span class="pun">└──</span><span class="pln"> eslint@8</span><span class="pun">.</span><span class="lit">17.0</span><span class="pln"> deduped</span></pre>

<p>
	ويمكن ترقية كل الاعتماديات في المشروع باستخدام أمر الترقية دون تحديد اسم أي حزمة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_95" style="">
<span class="pln">npm up</span></pre>

<h3>
	إلغاء تثبيت الوحدات البرمجية
</h3>

<p>
	يمكن استخدام الأمر <code>uninstall</code> الخاص بمدير الحزم npm لإلغاء تثبيت وحدات من المشروع بإزالة الحزمة أو الوحدة تلك من مجلد node_modules ويُحذف اسم تلك الحزمة من قائمة الاعتماديات ضمن الملف package.json وملف package-lock.json.
</p>

<p>
	نضطر في الكثير من الأحيان لإزالة حزم معينة من مشروع نعمل عليه، مثلًا لإزالة حزمة ما بعد تجربتها وتبين أنها لا تحقق المطلوب أو أنها صعبة الاستخدام، فمثلًا لو أن حزمة <code>axios</code> التي نستخدمها لم تفي بالغرض المطلوب منها وهو إرسال طلبات HTTP أو أنها صعبة الاستخدام بالنسبة لهذا المشروع يمكن إلغاء تثبيتها بتنفيذ الأمر <code>uninstall</code> أو اختصاره <code>un</code> وتمرير اسم الحزمة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_97" style="">
<span class="pln">npm un axios</span></pre>

<p>
	نحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_99" style="">
<span class="pln">removed </span><span class="lit">8</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> and audited </span><span class="lit">83</span><span class="pln"> packages in </span><span class="lit">542ms</span><span class="pln">

</span><span class="lit">13</span><span class="pln"> packages are looking </span><span class="kwd">for</span><span class="pln"> funding
  run </span><span class="pun">`</span><span class="pln">npm fund</span><span class="pun">`</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> details

found </span><span class="lit">0</span><span class="pln"> vulnerabilities</span></pre>

<p>
	نلاحظ عدم ظهور اسم الحزمة التي ألغي تثبيتها، لذا نتأكد من ذلك بعرض الحزم المثبتة حاليًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_101" style="">
<span class="pln">npm ls</span></pre>

<p>
	سنلاحظ من الخرج التالي أن الحزمة <code>eslint</code> أصبحت الوحيدة المثبتة ضمن المشروع، ما يدل على إلغاء تثبيت حزمة <code>axios</code> بنجاح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_103" style="">
<span class="pln">locator@1</span><span class="pun">.</span><span class="lit">0.0</span><span class="pln"> </span><span class="pun">/</span><span class="pln">home</span><span class="pun">/</span><span class="pln">ubuntu</span><span class="pun">/</span><span class="pln">locator
</span><span class="pun">└──</span><span class="pln"> eslint@8</span><span class="pun">.</span><span class="lit">17.0</span></pre>

<h3>
	فحص الوحدات وتدقيقها
</h3>

<p>
	يُستعمل الأمر <code>audit</code> من مدير الحزم npm في تدقيق الحزم وفحصها لعرض المخاطر الأمنية المحتملة ضمن شجرة اعتماديات المشروع المثبتة، ولنختبر ذلك مثلًا بتثبيت إصدار قديم من حزمة <a href="https://github.com/request/request" rel="external nofollow">request</a> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_105" style="">
<span class="pln">npm i request@2</span><span class="pun">.</span><span class="lit">60.0</span></pre>

<p>
	وسنلاحظ فورًا عند تثبيت حزم قديمة منتهية الصلاحية ظهور خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_107" style="">
<span class="pln">npm WARN deprecated cryptiles@2</span><span class="pun">.</span><span class="lit">0.5</span><span class="pun">:</span><span class="pln"> </span><span class="typ">This</span><span class="pln"> version has been deprecated in accordance </span><span class="kwd">with</span><span class="pln"> the hapi support policy </span><span class="pun">(</span><span class="pln">hapi</span><span class="pun">.</span><span class="pln">im</span><span class="pun">/</span><span class="pln">support</span><span class="pun">).</span><span class="pln"> </span><span class="typ">Please</span><span class="pln"> upgrade to the latest version to </span><span class="kwd">get</span><span class="pln"> the best features</span><span class="pun">,</span><span class="pln"> bug fixes</span><span class="pun">,</span><span class="pln"> and security patches</span><span class="pun">.</span><span class="pln"> </span><span class="typ">If</span><span class="pln"> you are unable to upgrade at </span><span class="kwd">this</span><span class="pln"> time</span><span class="pun">,</span><span class="pln"> paid support is available </span><span class="kwd">for</span><span class="pln"> older versions </span><span class="pun">(</span><span class="pln">hapi</span><span class="pun">.</span><span class="pln">im</span><span class="pun">/</span><span class="pln">commercial</span><span class="pun">).</span><span class="pln">
npm WARN deprecated sntp@1</span><span class="pun">.</span><span class="lit">0.9</span><span class="pun">:</span><span class="pln"> </span><span class="typ">This</span><span class="pln"> module moved to </span><span class="lit">@hapi</span><span class="pun">/</span><span class="pln">sntp</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Please</span><span class="pln"> make sure to </span><span class="kwd">switch</span><span class="pln"> over as </span><span class="kwd">this</span><span class="pln"> distribution is no longer supported and may contain bugs and critical security issues</span><span class="pun">.</span><span class="pln">
npm WARN deprecated boom@2</span><span class="pun">.</span><span class="lit">10.1</span><span class="pun">:</span><span class="pln"> </span><span class="typ">This</span><span class="pln"> version has been deprecated in accordance </span><span class="kwd">with</span><span class="pln"> the hapi support policy </span><span class="pun">(</span><span class="pln">hapi</span><span class="pun">.</span><span class="pln">im</span><span class="pun">/</span><span class="pln">support</span><span class="pun">).</span><span class="pln"> </span><span class="typ">Please</span><span class="pln"> upgrade to the latest version to </span><span class="kwd">get</span><span class="pln"> the best features</span><span class="pun">,</span><span class="pln"> bug fixes</span><span class="pun">,</span><span class="pln"> and security patches</span><span class="pun">.</span><span class="pln"> </span><span class="typ">If</span><span class="pln"> you are unable to upgrade at </span><span class="kwd">this</span><span class="pln"> time</span><span class="pun">,</span><span class="pln"> paid support is available </span><span class="kwd">for</span><span class="pln"> older versions </span><span class="pun">(</span><span class="pln">hapi</span><span class="pun">.</span><span class="pln">im</span><span class="pun">/</span><span class="pln">commercial</span><span class="pun">).</span><span class="pln">
npm WARN deprecated node</span><span class="pun">-</span><span class="pln">uuid@1</span><span class="pun">.</span><span class="lit">4.8</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Use</span><span class="pln"> uuid module instead
npm WARN deprecated har</span><span class="pun">-</span><span class="pln">validator@1</span><span class="pun">.</span><span class="lit">8.0</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> library is no longer supported
npm WARN deprecated hoek@2</span><span class="pun">.</span><span class="lit">16.3</span><span class="pun">:</span><span class="pln"> </span><span class="typ">This</span><span class="pln"> version has been deprecated in accordance </span><span class="kwd">with</span><span class="pln"> the hapi support policy </span><span class="pun">(</span><span class="pln">hapi</span><span class="pun">.</span><span class="pln">im</span><span class="pun">/</span><span class="pln">support</span><span class="pun">).</span><span class="pln"> </span><span class="typ">Please</span><span class="pln"> upgrade to the latest version to </span><span class="kwd">get</span><span class="pln"> the best features</span><span class="pun">,</span><span class="pln"> bug fixes</span><span class="pun">,</span><span class="pln"> and security patches</span><span class="pun">.</span><span class="pln"> </span><span class="typ">If</span><span class="pln"> you are unable to upgrade at </span><span class="kwd">this</span><span class="pln"> time</span><span class="pun">,</span><span class="pln"> paid support is available </span><span class="kwd">for</span><span class="pln"> older versions </span><span class="pun">(</span><span class="pln">hapi</span><span class="pun">.</span><span class="pln">im</span><span class="pun">/</span><span class="pln">commercial</span><span class="pun">).</span><span class="pln">
npm WARN deprecated request@2</span><span class="pun">.</span><span class="lit">60.0</span><span class="pun">:</span><span class="pln"> request has been deprecated</span><span class="pun">,</span><span class="pln"> see https</span><span class="pun">:</span><span class="com">//github.com/request/request/issues/3142</span><span class="pln">
npm WARN deprecated hawk@3</span><span class="pun">.</span><span class="lit">1.3</span><span class="pun">:</span><span class="pln"> </span><span class="typ">This</span><span class="pln"> module moved to </span><span class="lit">@hapi</span><span class="pun">/</span><span class="pln">hawk</span><span class="pun">.</span><span class="pln"> </span><span class="typ">Please</span><span class="pln"> make sure to </span><span class="kwd">switch</span><span class="pln"> over as </span><span class="kwd">this</span><span class="pln"> distribution is no longer supported and may contain bugs and critical security issues</span><span class="pun">.</span><span class="pln">

added </span><span class="lit">56</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> and audited </span><span class="lit">139</span><span class="pln"> packages in </span><span class="lit">4s</span><span class="pln">

</span><span class="lit">13</span><span class="pln"> packages are looking </span><span class="kwd">for</span><span class="pln"> funding
  run </span><span class="pun">`</span><span class="pln">npm fund</span><span class="pun">`</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> details

</span><span class="lit">9</span><span class="pln"> vulnerabilities </span><span class="pun">(</span><span class="lit">5</span><span class="pln"> moderate</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> high</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> critical</span><span class="pun">)</span><span class="pln">

</span><span class="typ">To</span><span class="pln"> address all issues</span><span class="pun">,</span><span class="pln"> run</span><span class="pun">:</span><span class="pln">
  npm audit fix </span><span class="pun">--</span><span class="pln">force

</span><span class="typ">Run</span><span class="pln"> </span><span class="pun">`</span><span class="pln">npm audit</span><span class="pun">`</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> details</span><span class="pun">.</span></pre>

<p>
	يخبرنا npm بوجود حزم قديمة يُفضل عدم استخدامها ووجود ثغرات ضمن الاعتماديات الحالية للمشروع، ولعرض تفاصيل أكثر عن ذلك يمكننا تنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_109" style="">
<span class="pln">npm audit</span></pre>

<p>
	سيظهر لنا جدولًا يعرض المخاطر الأمنية الموجودة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_111" style="">
<span class="pun">#</span><span class="pln"> npm audit report

bl  </span><span class="pun">&lt;</span><span class="lit">1.2</span><span class="pun">.</span><span class="lit">3</span><span class="pln">
</span><span class="typ">Severity</span><span class="pun">:</span><span class="pln"> moderate
</span><span class="typ">Remote</span><span class="pln"> </span><span class="typ">Memory</span><span class="pln"> </span><span class="typ">Exposure</span><span class="pln"> in bl </span><span class="pun">-</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//github.com/advisories/GHSA-pp7h-53gx-mx7r</span><span class="pln">
fix available via </span><span class="pun">`</span><span class="pln">npm audit fix</span><span class="pun">`</span><span class="pln">
node_modules</span><span class="pun">/</span><span class="pln">bl
  request  </span><span class="lit">2.16</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2.86</span><span class="pun">.</span><span class="lit">0</span><span class="pln">
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of bl
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of hawk
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of qs
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of tunnel</span><span class="pun">-</span><span class="pln">agent
  node_modules</span><span class="pun">/</span><span class="pln">request

cryptiles  </span><span class="pun">&lt;=</span><span class="lit">4.1</span><span class="pun">.</span><span class="lit">1</span><span class="pln">
</span><span class="typ">Severity</span><span class="pun">:</span><span class="pln"> critical
</span><span class="typ">Insufficient</span><span class="pln"> </span><span class="typ">Entropy</span><span class="pln"> in cryptiles </span><span class="pun">-</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//github.com/advisories/GHSA-rq8g-5pc5-wrhr</span><span class="pln">
</span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of boom
fix available via </span><span class="pun">`</span><span class="pln">npm audit fix</span><span class="pun">`</span><span class="pln">
node_modules</span><span class="pun">/</span><span class="pln">cryptiles
  hawk  </span><span class="pun">&lt;=</span><span class="lit">9.0</span><span class="pun">.</span><span class="lit">0</span><span class="pln">
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of boom
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of cryptiles
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of hoek
  </span><span class="typ">Depends</span><span class="pln"> on vulnerable versions of sntp
  node_modules</span><span class="pun">/</span><span class="pln">hawk

</span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">.</span><span class="pln">

</span><span class="lit">9</span><span class="pln"> vulnerabilities </span><span class="pun">(</span><span class="lit">5</span><span class="pln"> moderate</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> high</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> critical</span><span class="pun">)</span><span class="pln">

</span><span class="typ">To</span><span class="pln"> address all issues</span><span class="pun">,</span><span class="pln"> run</span><span class="pun">:</span><span class="pln">
  npm audit fix</span></pre>

<p>
	نلاحظ ظهور مسارات لتلك الثغرات واقتراح npm طرقًا لسدها إما بتحديث تلك الاعتماديات أو تنفيذ الأمر الفرعي <code>fix</code> للأمر <code>audit</code> لإصلاح المشاكل تلقائيًا كما هو مقترح، ولنجرب ذلك الأمر ونرى ما يحصل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_113" style="">
<span class="pln">npm audit fix</span></pre>

<p>
	يظهر لنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7827_115" style="">
<span class="pln">npm WARN deprecated har</span><span class="pun">-</span><span class="pln">validator@5</span><span class="pun">.</span><span class="lit">1.5</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> library is no longer supported
npm WARN deprecated uuid@3</span><span class="pun">.</span><span class="lit">4.0</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Please</span><span class="pln"> upgrade  to version </span><span class="lit">7</span><span class="pln"> or higher</span><span class="pun">.</span><span class="pln">  </span><span class="typ">Older</span><span class="pln"> versions may use </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> in certain circumstances</span><span class="pun">,</span><span class="pln"> which is known to be problematic</span><span class="pun">.</span><span class="pln">  </span><span class="typ">See</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//v8.dev/blog/math-random for details.</span><span class="pln">
npm WARN deprecated request@2</span><span class="pun">.</span><span class="lit">88.2</span><span class="pun">:</span><span class="pln"> request has been deprecated</span><span class="pun">,</span><span class="pln"> see https</span><span class="pun">:</span><span class="com">//github.com/request/request/issues/3142</span><span class="pln">

added </span><span class="lit">19</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> removed </span><span class="lit">34</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> changed </span><span class="lit">13</span><span class="pln"> packages</span><span class="pun">,</span><span class="pln"> and audited </span><span class="lit">124</span><span class="pln"> packages in </span><span class="lit">3s</span><span class="pln">

</span><span class="lit">14</span><span class="pln"> packages are looking </span><span class="kwd">for</span><span class="pln"> funding
  run </span><span class="pun">`</span><span class="pln">npm fund</span><span class="pun">`</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> details

found </span><span class="lit">0</span><span class="pln"> vulnerabilities</span></pre>

<p>
	نفذ npm ترقية لحزمتين موجودتين ما أدى لحل المشاكل الأمنية الموجودة، مع ذلك لا زال هناك ثلاث حزم ضمن المشروع قديمة ويفضل عدم استخدامها، وبهذا نرى أن الأمر <code>audit fix</code> لا يُصلح كافة المشاكل الموجودة دومًا، وذلك لأن حل تلك المشاكل يتطلب ترقية الحزم إلى إصدارات أعلى والتي قد تؤدي بدورها إلى حصول تعارض في شجرة الاعتماديات مما يتسبب بمشاكل توقف عمل المشروع كله، ولكن يمكن إجبار npm على ترقية تلك الحزم بتمرير الخيار <code>‎--force</code> وحل جميع تلك المشاكل كالتالي:
</p>

<pre class="ipsCode">
npm audit fix --force
</pre>

<p>
	ولا ينصح بتنفيذ ذلك لما يسببه من مشاكل في التوافقية بين الاعتماديات كما ذكرنا.
</p>

<h2>
	ختامًا
</h2>

<p>
	تعلمنا في هذا القصل طريقة ترتيب نود للوحدات البرمجية ضمن حزم، وكيف يدير مدير حزم نود npm تلك الحزم، وكيف أن المشاريع في نود تستخدم الملف package.json لتعريف اعتماديات المشروع وإدارتها بالإضافة إلى تخزين بيانات تصف المشروع نفسه.
</p>

<p>
	واستخدمنا أمر npm من سطر الأوامر لتثبيت وترقية وإزالة الوحدات البرمجية وعرض شجرة الاعتماديات للمشروع وللتحقق من إمكانية ترقية الوحدات البرمجية القديمة، وهدف كل ذلك إعادة استخدام الوحدات البرمجية بين المشاريع بدلًا من إعادة كتابتها لتسريع عملية تطوير، حيث يمكنك الآن كتابة الوحدات البرمجية الخاصة بك ومشاركتها مع الآخرين لاستخدامها في مشاريعهم الخاصة، ويمكنك التدرب على ما تعلمته في هذا المقال بالبحث عن بعض الحزم التي تخدم مشكلة ما تحاول حلها وتثبيتها واختبارها، فمثلًا يمكنك تجربة استخدام <a href="https://wiki.hsoub.com/TypeScript" rel="external">TypeScript</a> لإضافة مزايا على لغة جافاسكربت، أو تحويل موقع ويب تعمل عليه إلى تطبيق جوال باستخدام <a href="https://wiki.hsoub.com/Cordova" rel="external">Cordova</a>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-node-js-modules-with-npm-and-package-json" rel="external nofollow">How To Use Node.js Modules with npm and package.json</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">دليلك الشامل إلى مدير الحزم npm في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1728</guid><pubDate>Sun, 02 Oct 2022 10:55:16 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x648;&#x636;&#x639; &#x627;&#x644;&#x62A;&#x641;&#x627;&#x639;&#x644;&#x64A; REPL &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B6%D8%B9-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D9%8A-repl-%D9%81%D9%8A-nodejs-r1712/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_09/6321bb7b108ca_---REPL-.png.1dfc56b47f76b39b18d0cd16f1f35905.png" /></p>

<p>
	حلقة اقرأ-قيِّم-اطبع أو REPL -اختصارًا للعبارة Read Evaluate Print Loop- هي صدفة تفاعلية interactive shell تعالج تعابير جافاسكربت البرمجية ضمن بيئة نود، حيث تقرأ تلك الصدفة الشيفرات التي يدخلها المستخدم وتُصرّفها ثم تُقيّم نتيجتها وتطبع تلك النتيجة للمستخدم على الشاشة آنيًا، وتكرر ذلك لحين خروج المستخدم من تلك الصدفة، وتأتي REPL مثبتة مسبقًا مع نود، وتسمح لنا باختبار واستكشاف <a href="https://academy.hsoub.com/programming/javascript/%D9%86%D9%85%D8%B7-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r785/" rel="">شيفرات جافاسكربت</a> داخل بيئة نود بسرعة ودون الحاجة لحفظها أولًا داخل ملف ثم تنفيذها، وسيلزمك في هذا الفصل للمتابعة معرفة <a href="https://academy.hsoub.com/programming/javascript/jquery/%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d9%91%d8%a7%d8%aa-%d8%ac%d8%a7%d9%81%d8%a7%d8%b3%d9%83%d8%b1%d9%8a%d8%a8%d8%aa-%d9%88%d8%a7%d9%84%d8%a3%d8%ae%d8%b7%d8%a7%d8%a1-%d8%a7%d9%84%d8%b4%d9%91%d8%a7%d8%a6%d8%b9%d8%a9-%d8%a7%d9%84%d9%91%d8%aa%d9%8a-%d9%8a%d8%b1%d8%aa%d9%83%d8%a8%d9%87%d8%a7-%d8%a7%d9%84%d9%85%d8%a8%d8%aa%d8%af%d8%a6%d9%88%d9%86-r59/" rel="">بأساسيات لغة جافاسكريبت</a>، ولبيئة نود مُثبتة على الجهاز.
</p>

<h2>
	الدخول والخروج من الوضع REPL
</h2>

<p>
	بعد تثبيت نود على جهازك، سيكون وضع حلقة REPL متاحًا للاستخدام مباشرةً، وللدخول إليه ننفذ الأمر <code>node</code> فقط ضمن <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_11" style="">
<span class="pln">node</span></pre>

<p>
	سيدخلنا ذلك في وضع التفاعلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_13" style="">
<span class="pun">&gt;</span></pre>

<p>
	حيث يشير الرمز <code>‎&gt;‎</code> في بداية السطر لإمكانية إدخالنا <a href="https://academy.hsoub.com/programming/javascript/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%85%D9%82%D8%B1%D9%88%D8%A1%D8%A9-%D9%82%D8%B5%D8%A9-%D9%86%D9%88%D8%B9%D9%8A%D9%86-%D9%85%D9%86-%D8%A7%D9%84%D8%AE%D8%A8%D8%B1%D8%A7%D8%A1-r1272/" rel="">شيفرات جافاسكربت</a> لتُعالج، ويمكننا تجربة ذلك بجمع عددين كالتالي:
</p>

<pre class="ipsCode">
&gt; 2 + 2
</pre>

<p>
	نضغط زر الإدخال ENTER لتُقيّم صدفة نود ذلك التعبير البرمجي وتطبع نتيجته مباشرةً:
</p>

<pre class="ipsCode">
4
</pre>

<p>
	للخروج من ذلك الوضع يمكننا إما كتابة الأمر <code>‎.exit</code> أو الضغط من لوحة المفاتيح على الاختصار CTRL+D، أو الضغط مرتين على الاختصار CTRL+C، للخروج والعودة إلى سطر الأوامر.
</p>

<p>
	والآن بعد أن علمنا طريقة الدخول والخروج من الوضع REPL، سنتعلم طريقة تنفيذ بعض شيفرات <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت</a> البسيطة ضمنه.
</p>

<h2>
	تنفيذ شيفرة جافاسكربت ضمن REPL
</h2>

<p>
	يمنحنا الوضع REPL التفاعلي طريقة سريعة لاختبار شيفرات جافاسكربت فورًا، ودون الحاجة لإنشاء ملف لها أولًا، حيث يمكننا تنفيذ أي تعبير برمجي سليم يمكن تنفيذه عادةً ضمن بيئة نود، إذ اختبرنا في المثال السابق جمع عددين، ولنختبر الآن تنفيذ قسمة عددين.
</p>

<p>
	ندخل أولًا إلى الوضع REPL كما تعلمنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_17" style="">
<span class="pln">node</span></pre>

<p>
	ونُدخل التعبير البرمجي ونضغط زر الإدخال لتنفيذه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_19" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">5</span></pre>

<p>
	نحصل على الخرج التالي وهو ناتج العملية السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_21" style="">
<span class="lit">2</span></pre>

<p>
	يمكن أيضًا مثلًا تنفيذ العمليات على <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-strings-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r817/" rel="">السلاسل النصية</a>، ولنختبر ذلك بتنفيذ ضم سلسلتين نصيتين كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_23" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="str">"Hello "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"World"</span></pre>

<p>
	وسيظهر لنا نتيجة ضم السلسلتين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_25" style="">
<span class="str">'Hello World'</span></pre>

<p>
	نلاحظ ظهور النص في النتيجة محاطًا بعلامات اقتباس مفردة بدلًا من علامات الاقتباس المزدوجة، ففي جافاسكربت لا يؤثر نوع علامات الاقتباس على قيمة السلسلة النصية، لذا يستخدم الوضع REPL عند إظهار نتيجة فيها سلسلة نصية علامات الاقتباس المفردة دومًا.
</p>

<h3>
	استدعاء التوابع
</h3>

<p>
	يستخدم التابع العام <code>console.log</code> أو توابع طباعة الرسائل المشابهة له كثيرًا في <a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%AE%D8%A7%D8%B1%D8%AC-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1402/" rel="">بيئة نود</a>، حيث يمكننا داخل REPL استدعاء التوابع أيضًا، فلنجرب مثلًا أمر طباعة رسالة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_28" style="">
<span class="pun">&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Hi"</span><span class="pun">)</span></pre>

<p>
	سيُستدعى التابع وتظهر نتيجة التنفيذ التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_32" style="">
<span class="typ">Hi</span><span class="pln">
</span><span class="kwd">undefined</span></pre>

<p>
	يمثل السطر الأول نتيجة استدعاء التابع <code>console.log</code>، والذي يطبع الرسالة إلى المجرى <code>stdout</code> والذي يمثل الشاشة، ولأن الوظيفة طباعة وليس إعادة عبر التعبير <code>return</code> كنتيجة لتنفيذ التابع، نلاحظ عدم وجود علامات الاقتباس حولها، بينما السطر الثاني يعرض القيمة <code>undefined</code> وهي النتيجة التي أعادها التابع بعد انتهاء تنفيذه.
</p>

<h3>
	تعريف متغيرات
</h3>

<p>
	تُستخدم المتغيرات variables أيضًا بكثرة خلال كتابتنا للشيفرات البرمجية ولا نكتفي بالتعامل مع القيم مباشرة، لذا يتيح لنا REPL إمكانية تعريف المتغيرات تمامًا كما لو كنا نكتبها ضمن ملفات جافاسكريبت، ويمكننا اختبار ذلك كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_34" style="">
<span class="pun">&gt;</span><span class="pln"> let age </span><span class="pun">=</span><span class="pln"> </span><span class="lit">30</span></pre>

<p>
	تظهر لنا النتيجة التالية بعد ضغط زر الإدخال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_36" style="">
<span class="kwd">undefined</span></pre>

<p>
	كما لاحظنا سابقًا عند استدعاء التابع <code>console.log</code> كانت القيمة التي يعيدها هي <code>undefined</code>، وهنا أيضًا جرى تعريف المتغير <code>age</code> ولم نُعد أي قيمة، وسيكون ذلك المتغير متاحًا حتى الانتهاء والخروج من جلسة REPL الحالية، ولاختبار ذلك نستخدم المتغير <code>age</code> ضمن عملية ما ولتكن ضربه بعدد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_38" style="">
<span class="pun">&gt;</span><span class="pln"> age </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span></pre>

<p>
	تظهر لنا نتيجة العملية بعد الضغط على زر الإدخال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_40" style="">
<span class="lit">60</span></pre>

<p>
	نلاحظ أن REPL يعيد ويطبع لنا نتيجة التعبير البرمجي فورًا، لذا لا نحتاج لاستخدام التابع <code>console.log</code> في كل مرة نريد طباعة قيمة على الشاشة، حيث سيطبع تلقائيًا أي قيمة يعيدها الأمر المُدخل.
</p>

<h3>
	إدخال الشيفرات متعددة الأسطر
</h3>

<p>
	يدعم REPL أيضًا إدخال الشيفرات متعددة السطر، ولنختبر ذلك ننشئ تابعًا يضيف القيمة 3 إلى العدد المُمرر له، ونبدأ تعريفه بإدخال أول سطر منه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_42" style="">
<span class="kwd">const</span><span class="pln"> add3 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">num</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	وبعد الضغط على زر الإدخال ستلاحظ تغير الرمز <code>&lt;</code> في أول السطر إلى رمز النُقط الثلاث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_44" style="">
<span class="pun">...</span></pre>

<p>
	يلاحظ REPL وجود قوس معقوص <code>}</code> في نهاية الأمر المدخل، ما يشير إلى وجود بقية له، فيتم إضافة هامش من النقط وانتظار إدخالنا لباقي الأمر، وذلك لتسهيل القراءة حيث يضيف REPL ثلاث نقط ومسافة في السطر التالي، ليبدو أن الشيفرة يسبقها مسافة بادئة، ونكمل إدخال سطر جسم الدالة، ثم سطر قوس الإغلاق لإنهاء تعريف التابع، ونضغط زر الإدخال بعد كل سطر منها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_46" style="">
<span class="kwd">return</span><span class="pln"> num </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبعد إدخال آخر سطر الحاوي على قوس الإغلاق للتابع، ستظهر لنا القيمة <code>undefined</code>، والتي تدل على القيمة المُرجعة من أمر إسناد الدالة إلى الثابت، ونلاحظ عودة الرمز في بداية السطر إلى رمز إدخال الأوامر <code>‎&gt;‎</code> بدلًا من النقط <code>...</code>، وتظهر لنا قيمة الأمر المُدخل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_48" style="">
<span class="kwd">undefined</span><span class="pln">
</span><span class="pun">&gt;</span></pre>

<p>
	يمكننا الآن استخدام الدالة التي عرفناها <code>add3()‎</code> بتمرير قيمة لها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_50" style="">
<span class="pun">&gt;</span><span class="pln"> add3</span><span class="pun">(</span><span class="lit">10</span><span class="pun">)</span></pre>

<p>
	ويظهر لنا نتيجة الإضافة التي تعيدها الدالة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_53" style="">
<span class="lit">13</span></pre>

<p>
	يمكن الاستفادة من REPL في تجربة شيفرات جافاسكربت واللعب بها قبل إضافتها إلى النظام أو المشروع الذي نعمل عليه، حيث يوفر REPL اختصارات تساعدنا خلال تلك العملية سنتعرف عليها في الفقرة التالية.
</p>

<h2>
	التعرف على الاختصارات في REPL
</h2>

<p>
	يوفر REPL عدة اختصارات تسهل عملية ادخال الشيفرات وتوفر الوقت، فمثلًا يحفظ REPL -كما معظم الصدفات- سجلًا بالأوامر المدخلة السابقًا لنتمكن من الرجوع إليها بدلًا من إعادة إدخالها يدويًا مرة أخرى.
</p>

<p>
	جرب مثلًا كتابة القيمة النصية الطويلة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_55" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="str">"The answer to life the universe and everything is 32"</span></pre>

<p>
	يظهر لنا النص نفسه كنتيجة لذلك الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_57" style="">
<span class="str">'The answer to life the universe and everything is 32'</span></pre>

<p>
	الآن إذا أردنا إدخال النص السابق نفسه، لكن مع اختلاف وهو تبديل العدد 32 إلى 42، فيمكننا ذلك عبر الضغط على مفتاح السهم العلوي UP من لوحة المفاتيح للوصول إلى آخر قيمة أدخلناها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_59" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="str">"The answer to life the universe and everything is 32"</span></pre>

<p>
	بعدها يمكننا تحريك المؤشر داخل النص وإزالة العدد <code>3</code> وتبديله إلى العدد <code>4</code> ونضغط زر الإدخال ENTER مجددًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_61" style="">
<span class="str">'The answer to life the universe and everything is 42'</span></pre>

<p>
	يمكن بالضغط المستمر على السهم العلوي UP الرجوع في سجل تاريخ الأوامر المدخلة سابقًا واحد تلو الآخر، وبالمقابل يمكن الضغط على مفتاح السهم السفلي DOWN للتقدم إلى الأمام في سجل تاريخ الأوامر، ويمكن بعد الانتهاء من تفحص سجلات الأوامر المُخزنة الضغط مرارًا على مفتاح السهم السفلي DOWN إلى حين العودة إلى سطر الإدخال الفارغ لكتابة أمر جديد، ويمكن الوصول إلى قيمة آخر نتيجة عبر محرف الشرطة سفلية <code>_</code>، ولاختبار ذلك نكتب الرمز <code>_</code> ثم نضغط على زر الإدخال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_63" style="">
<span class="pun">&gt;</span><span class="pln"> _</span></pre>

<p>
	سيظهر لنا السلسلة النصية التي أدخلناها مؤخرًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_65" style="">
<span class="str">'The answer to life the universe and everything is 42'</span></pre>

<p>
	يتيح REPL أيضًا ميزة الإكمال التلقائي للتوابع والمتغيرات والكلمات المفتاحية أثناء كتابة الشيفرة، فمثلًا إذا أردنا استخدام التابع العام <code>Math.sqrt</code> لحساب الجذر التربيعي لعدد يمكننا فقط كتابة الأحرف الأولى لذلك الاستدعاء كالتالي مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_67" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sq</span></pre>

<p>
	ثم الضغط على زر الجدولة تاب TAB ليكمل لنا REPL كتابة باقي اسم التابع بشكل صحيح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_69" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span></pre>

<p>
	وعندما يكون هناك أكثر من طريقة لإكمال الأمر، سيظهر لنا جميع الاحتمالات الممكنة، فمثلًا إذا حاولنا استدعاء تابع ما من الصنف <code>Math</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_71" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span></pre>

<p>
	بالضغط على زر الجدولة مرتين سيظهر لنا جميع الاحتمالات الممكنة للإكمال التلقائي لذلك الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_73" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">__defineGetter__      </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">__defineSetter__      </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">__lookupGetter__
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">__lookupSetter__      </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">__proto__             </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">constructor
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">hasOwnProperty        </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">isPrototypeOf         </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">propertyIsEnumerable
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">toLocaleString        </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">toString              </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">valueOf

</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">E                     </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">LN10                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">LN2
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">LOG10E                </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">LOG2E                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">PI
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">SQRT1_2               </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">SQRT2                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">abs
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">acos                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">acosh                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">asin
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">asinh                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">atan                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">atan2
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">atanh                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">cbrt                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">ceil
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">clz32                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">cos                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">cosh
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">exp                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">expm1                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">fround                </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">hypot                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">imul
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">log                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">log10                 </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">log1p
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">log2                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">max                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">min
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">pow                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random                </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sign                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sin                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sinh
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt                  </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">tan                   </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">tanh
</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">trunc</span></pre>

<p>
	بحيث تظهر النتيجة السابقة بتنسيق يناسب حجم نافذة سطر الأوامر من ناحية عدد الأعمدة والأسطر لتلك الاحتمالات، وتمثل تلك الاحتمالات جميع التوابع أو الخاصيات المتاحة ضمن الوحدة <code>Math</code>.
</p>

<p>
	يمكننا في أي وقت الحصول على سطر فارغ جديد لإدخال الأوامر بالضغط على الاختصار CTRL+C، وذلك دون تنفيذ الأمر الجاري كتابته في السطر الحالي.
</p>

<p>
	إن معرفة الاختصارات السابقة يزيد من كفاءة وسرعة كتابة الشيفرات داخل REPL، كما يحتوي أيضًا على أوامر تزيد الإنتاجية سنتعرف عليها في الفقرة التالية.
</p>

<h2>
	أوامر REPL
</h2>

<p>
	يوفر REPL كلمات مفتاحية خاصة تساعدنا في التحكم به، ويبدأ كل من تلك الأوامر برمز النقطة <code>.</code> سنتعرف عليها.
</p>

<h3>
	الأمر ‎.help
</h3>

<p>
	لعرض كل الأوامر المتاحة ضمن REPL يمكننا استخدام الأمر <code>‎.help</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_75" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">help</span></pre>

<p>
	سيظهر لنا جميع الأوامر المتاحة الخاصة بالوضع REPL وهي قليلة لكن مفيدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_77" style="">
<span class="pun">.</span><span class="kwd">break</span><span class="pln">    </span><span class="typ">Sometimes</span><span class="pln"> you </span><span class="kwd">get</span><span class="pln"> stuck</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> gets you out
</span><span class="pun">.</span><span class="pln">clear    </span><span class="typ">Alias</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">.</span><span class="kwd">break</span><span class="pln">
</span><span class="pun">.</span><span class="pln">editor   </span><span class="typ">Enter</span><span class="pln"> editor mode
</span><span class="pun">.</span><span class="pln">exit     </span><span class="typ">Exit</span><span class="pln"> the repl
</span><span class="pun">.</span><span class="pln">help     </span><span class="typ">Print</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> help message
</span><span class="pun">.</span><span class="pln">load     </span><span class="typ">Load</span><span class="pln"> JS from a file into the REPL session
</span><span class="pun">.</span><span class="pln">save     </span><span class="typ">Save</span><span class="pln"> all evaluated commands in </span><span class="kwd">this</span><span class="pln"> REPL session to a file

</span><span class="typ">Press</span><span class="pln"> </span><span class="pun">^</span><span class="pln">C to abort current expression</span><span class="pun">,</span><span class="pln"> </span><span class="pun">^</span><span class="pln">D to exit the repl</span></pre>

<p>
	يفيد تنفيذ هذا الأمر في حال نسيان الأوامر المتاحة أو وظيفة كل منها.
</p>

<h3>
	الأمران ‎.break و ‎.clear
</h3>

<p>
	تظهر فائدة الأمران <code>‎.break</code> و <code>‎.clear</code> خلال كتابتنا الشيفرة متعددة الأسطر إذ تساعد على الخروج من ذلك الوضع، ولنختبر ذلك بكتابة أول سطر من حلقة التكرار <code>for</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_79" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">100000000</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	بدلًا من إكمال كتابة أسطر ذلك الأمر يمكننا تنفيذ الأمر <code>‎.break</code> أو الأمر <code>‎.clear</code> للخروج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_81" style="">
<span class="pun">.</span><span class="kwd">break</span></pre>

<p>
	سيظهر لنا الرمز <code>&gt;</code> من جديد، ونلاحظ أن REPL استجاب لهذا الأمر وانتقل إلى سطر جديد فارغ دون تنفيذ الشيفرة التي كنا نحاول إدخالها تمامًا كما لو أننا ضغطنا على الاختصار CTRL+C.
</p>

<h3>
	الأمران ‎.save و ‎.load
</h3>

<p>
	يُمكّننا الأمر <code>‎.save</code> من حفظ كافة الشيفرات التي أدخلناها منذ بداية جلسة REPL الحالية إلى ملف جافاسكربت، بالمقابل يُمكّننا الأمر <code>‎.load</code> من تنفيذ شيفرات جافاسكربت من ملف خارجي داخل REPL، وذلك بدلًا من كتابة تلك الشيفرات يدويًا، و لاختبار ذلك نخرج أولًا من الجلسة الحالية إما بتنفيذ الأمر <code>‎.exit</code> أو باستخدام الاختصار CTRL+D، ونبدأ جلسة REPL جديدة بتنفيذ الأمر <code>node</code>، حيث ستحفظ كل الشيفرات التي سنقوم بكتابتها منذ الآن داخل الملف عند استخدامنا لأمر الحفظ <code>‎.save</code> لاحقًا.
</p>

<p>
	نُعرّف مصفوفة من الفواكه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_83" style="">
<span class="pun">&gt;</span><span class="pln"> fruits </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'banana'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'apple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mango'</span><span class="pun">]</span></pre>

<p>
	في سطر النتيجة سيظهر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_85" style="">
<span class="pun">[</span><span class="pln"> </span><span class="str">'banana'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'apple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mango'</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	نحفظ الآن المتغير السابق إلى ملف جديد بالاسم <code>fruits.js</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_87" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">save fruits</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ستظهر رسالة تؤكد حفظ الملف بنجاح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_89" style="">
<span class="typ">Session</span><span class="pln"> saved to</span><span class="pun">:</span><span class="pln"> fruits</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	مكان حفظ ذلك الملف هو نفس مسار المجلد الذي بدأنا منه جلسة REPL من سطر الأوامر، فمثلًا لو كان مسار سطر الأوامر عندها هو مجلد المنزل home للمستخدم، فسيُحفَظ الملف داخل ذلك المجلد.
</p>

<p>
	والآن نخرج من الجلسة الحالية ونبدأ جلسة جديدة بتنفيذ الأمر <code>node</code> مرة أخرى، ونُحمِّل ملف <code>fruits.js</code> الذي حفظناه سابقًا بتنفيذ الأمر <code>‎.load</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_92" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">load fruits</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ليظهر لنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_94" style="">
<span class="pln">fruits </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'banana'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'apple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mango'</span><span class="pun">]</span><span class="pln">

</span><span class="pun">[</span><span class="pln"> </span><span class="str">'banana'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'apple'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'mango'</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	قرأ الأمر <code>‎.load</code> كل سطر داخل ذلك الملف ونفذه تمامًا كطريقة عمل مفسر جافاسكربت، حيث أصبح بإمكاننا الآن استخدام المتغير <code>fruits</code> كما لو أننا أدخلناه سابقًا يدويًا ضمن الجلسة الحالية، ولنختبر ذلك ونحاول الوصول لأول عنصر من تلك المصفوفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_96" style="">
<span class="pun">&gt;</span><span class="pln"> fruits</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span></pre>

<p>
	نحصل على الخرج المتوقع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_98" style="">
<span class="str">'apple'</span></pre>

<p>
	ويمكن تحميل أي ملف جافاسكربت باستخدام الأمر <code>‎.load</code> مهما كان، وليس فقط الملفات التي نحفظها، لنختبر ذلك بكتابة ملف جافاسكربت بسيط نُنشئ ملفًا جديدًا ونفتحه باستخدام محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_100" style="">
<span class="pln">nano peanuts</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ثم ندخل ضمنه الشيفرة التالية ونحفظ التغييرات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_104" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'I love peanuts!'</span><span class="pun">);</span></pre>

<p>
	نبدأ جلسة REPL جديدة من نفس مسار المجلد الحاوي على ملف جافاسكربت <code>peanuts.js</code> الجديد بتنفيذ الأمر <code>node</code>، ونُحمِّل الملف إلى الجلسة الحالية بتنفيذ التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_106" style="">
<span class="pun">&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">load peanuts</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سيُنفذ الأمر <code>‎.load</code> التعبير البرمجي <code>console</code> ضمن ذلك الملف ويُظهر الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_71_108" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'I love peanuts!'</span><span class="pun">);</span><span class="pln">

I love peanuts</span><span class="pun">!</span><span class="pln">
</span><span class="kwd">undefined</span><span class="pln">
</span><span class="pun">&gt;</span></pre>

<p>
	تظهر فائدة كلا الأمرين <code>‎.save</code> و <code>‎.load</code> عند كتابة الكثير من الشيفرات داخل REPL أو عندما نريد حفظ ما أدخلناه خلال الجلسة الحالية ومشاركته ضمن ملف جافاسكربت.
</p>

<h2>
	ختامًا
</h2>

<p>
	تتيح لنا بيئة REPL التفاعلية تنفيذ شيفرات جافاسكربت دون الحاجة لإنشاء ملف لها أولًا، كتنفيذ التعابير البرمجية واستدعاء التوابع وتعريف المتغيرات، وتوفر العديد من الاختصارات والأوامر والمزايا الداعمة لتلك العملية، كتنسيق النص تلقائيًا للأوامر متعددة الأسطر، وسجل بتاريخ الأوامر المُدخلة، إلى أوامر المسح أو الحفظ والتحميل، يضيف لك تعلم REPL مهارة قد تحتاج إليها خلال عملك في وقت ما.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-the-node-js-repl" rel="external nofollow">How To Use the Node.js REPL</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%88%D9%84-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D9%88%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87-r1711/" rel="">كتابة أول برنامج في بيئة Node.js وتنفيذه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%AE%D8%A7%D8%B1%D8%AC-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1402/" rel="">بيئة Node.js: استخدام جافاسكربت خارج المتصفح</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-nodejs-r1467/" rel="">البرمجة غير المتزامنة في Node.js</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1712</guid><pubDate>Wed, 14 Sep 2022 11:55:12 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x623;&#x648;&#x644; &#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x641;&#x64A; &#x628;&#x64A;&#x626;&#x629; Node.js &#x648;&#x62A;&#x646;&#x641;&#x64A;&#x630;&#x647;</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A3%D9%88%D9%84-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D9%88%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87-r1711/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_09/6321ace6a1134_-----node.png.38f7e0fb88df5020adbef4d113fa9343.png" /></p>

<p>
	<a href="https://nodejs.org/" rel="external nofollow">Node.js</a> -تُلفظ نود جي إس- هو بيئة تشغيل جافاسكربت مفتوحة المصدر تتيح تنفيذ شيفرات جافاسكربت خارج المتصفح، وذلك باستخدام محرك جافاسكربت V8 الشهير المُستخدم ضمن متصفح جوجل كروم، ومن أشهر استخدامات هذه البيئة هو تطوير تطبيقات وخوادم الويب وحتى أدوات <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a>، وتوفر لنا هذه البيئة كتابة شيفرات الواجهات الأمامية Front-end والواجهات الخلفية Back-end بلغة برمجة واحدة وهي جافاسكربت، كما تتيح لنا ذلك توحيد لغة البرمجة ضمن طبقات المشروع كافة ما يزيد التركيز ويوفر إمكانية لاستخدام نفس المكتبات ومشاركة الشيفرة بين الواجهات الأمامية بطرف العميل والواجهة الخلفية على <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">الخادم</a>.
</p>

<p>
	تتميز بيئة نود بطريقة التنفيذ الغير متزامنة asynchronous execution ما يمنحها قوة وأفضلية بالأداء في تنفيذ المهام التي تتطلب غزارة في الدخل والخرج ضمن في تطبيقات الويب أو تطبيقات الزمن الحقيقي، كتطبيقات بث الفيديو أو التطبيقات التي تحتاج لإرسال واستقبال مستمر للبيانات.
</p>

<p>
	سنكتب في هذا الفصل معًا برنامجنا الأول في بيئة تشغيل نود، وسنتعرف على بعض المفاهيم في تلك البيئة التي ستساعدنا في تطوير برنامج يتيح للمستخدم معاينة متغيرات البيئة على النظام لديه، ولتنفيذ ذلك سنتعلم طباعة السلاسل النصية إلى <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">الطرفية console</a>، واستقبال الدخل من المستخدم، ثم الوصول لمتغيرات البيئة environment variables على النظام.
</p>

<h2>
	المستلزمات
</h2>

<ul>
<li>
		معرفة <a href="https://academy.hsoub.com/programming/javascript/jquery/%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d9%91%d8%a7%d8%aa-%d8%ac%d8%a7%d9%81%d8%a7%d8%b3%d9%83%d8%b1%d9%8a%d8%a8%d8%aa-%d9%88%d8%a7%d9%84%d8%a3%d8%ae%d8%b7%d8%a7%d8%a1-%d8%a7%d9%84%d8%b4%d9%91%d8%a7%d8%a6%d8%b9%d8%a9-%d8%a7%d9%84%d9%91%d8%aa%d9%8a-%d9%8a%d8%b1%d8%aa%d9%83%d8%a8%d9%87%d8%a7-%d8%a7%d9%84%d9%85%d8%a8%d8%aa%d8%af%d8%a6%d9%88%d9%86-r59/" rel="">أساسيات لغة جافاسكربت</a>، حيث يمكنك الاطلاع على سلسلة <a href="https://academy.hsoub.com/search/?&amp;page=4&amp;tags=%D8%AF%D9%84%D9%8A%D9%84%20%D8%AA%D8%B9%D9%84%D9%85%20%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA&amp;sortby=newest" rel="">دليل تعلم جافاسكربت</a> وفيها 90 مقالًا ستساعدك على تعلم أساسيات اللغة كاملةً.
	</li>
	<li>
		بيئة نود مثبتة على جهازك، حيث استخدمنا في هذا الفصل الإصدار رقم 16.15.1، ويمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-nodejs-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r419/" rel="">تثبيت Node.js على نظام أبونتو</a> للتعرف على طريقة تثبيتها.
	</li>
</ul>
<p>
	<strong>ملاحظة</strong>: قد يختلف الإصدار الحالي لديك عن الإصدار الذي استعملناه، ولن تكون هنالك اختلافات أو مشاكل تذكر أثناء تطبيق الأمثلة والشيفرات ولكن إن حصلت إلى خطأ متعلق بتنفيذ شيفرة مطابقة تمامًا لشيفرة شرحناها فتأكد من اختلاف الإصدارات آنذاك وإن كانت المشكلة مرتبطة بها.
</p>

<h2>
	الخطوة الأولى - الطباعة إلى الطرفية
</h2>

<p>
	المهمة الأولى للمبرمج عند تعلمه للغة برمجة أو تجربة بيئة جديدة هي كتابة برنامج لطباعة عبارة "أهلًا بالعالم!" أو "‎Hello, World!‎"، لذا نبدأ بإنشاء ملف جديد نسميه"hello.js" ونفتحه ضمن أي برنامج محرر نصوص تريد كبرنامج المُفكرة Notepad مثلًا، سنستخدم في هذا المقال المحرر nano من سطر الأوامر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_10" style="">
<span class="pln">nano hello</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نكتب الشيفرة التالية داخله ونحفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_12" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span></pre>

<p>
	يوفر الكائن <code>console</code> في بيئة نود في السطر السابق توابعًا تمكننا من الكتابة إلى مجاري الخرج مثل مجرى الخرج القياسي <code>stdout</code> أو إلى مجرى الخطأ القياسي <code>stderr</code> وغيرهما والتي عادةً تمثل سطر الأوامر، ويطبع التابع <code>log</code> القيم المُمررة له إلى المجرى <code>stdout</code> لتظهر لنا في الطرفية، حيث أن المجاري في نود هي إما كائنات تستقبل بيانات مثل المجرى <code>stdout</code>، أو تُخرج بيانات كمقبس شبكة أو ملف، وأي بيانات تُرسل إلى المجرى <code>stdout</code> أو <code>stderr</code> ستظهر مباشرًة في الطرفية، ومن أهم مزايا المجاري سهولة إمكانية إعادة توجيهها، كتوجيه خرج تنفيذ برنامج ما إلى ملف أو إلى برنامج آخر، والآن وبعد التأكد من حفظ الملف والخروج من محرر النصوص، حيث إذا كنت تستخدم <code>nano</code> اضغط على <code>CTRL+X</code> للخروج واضغط <code>Y</code> عند سؤالك عن حفظ الملف، وبهذا يكون البرنامج الذي كتبناه جاهزًا للتنفيذ.
</p>

<h2>
	الخطوة الثانية - تشغيل البرنامج
</h2>

<p>
	نستخدم الأمر <code>node</code> لتشغيل البرنامج السابق كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_14" style="">
<span class="pln">node hello</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	سيتم تنفيذ شيفرات البرنامج داخل ملف hello.js ويظهر الناتج ضمن الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_16" style="">
<span class="typ">Hello</span><span class="pln"> </span><span class="typ">World</span></pre>

<p>
	ما حدث هو أن مفسر نود قرأ الملف ونفذ التعليمة <code>‎console.log("Hello World");‎</code> عبر استدعاء التابع <code>log</code> من الكائن العام <code>console</code>، الذي مررنا له السلسلة النصية <code>"Hello World"</code> كوسيط، ونلاحظ عدم طباعة علامات الاقتباس التي مررناها على الشاشة، لأنها ضرورية ضمن الشيفرة فقط لتحديد النص كسلسلة نصية، والآن بعد أن نفذنا برنامجنا البسيط السابق بنجاح، سنطوره ليصبح أكثر تفاعلية.
</p>

<h2>
	الخطوة الثالثة - استقبال الدخل من المستخدم عبر وسائط سطر الأوامر
</h2>

<p>
	يُظهر البرنامج السابق نفس الخرج كل مرة عند تنفيذه، لذا ولجعل الخرج متغيرًا يمكننا جلب المدخلات من المستخدم وعرضها على الشاشة كما هي، وهذا هو مبدأ عمل أدوات سطر الأوامر، حيث أنها تقبل من المستخدم عددًا من الوسائط التي تحدد طريقة عمل البرنامج مثال على ذلك الأمر <code>node</code> نفسه، حيث أنه يقبل الوسيط <code>‎--version</code> ليطبع عندها رقم إصدار بيئة نود المثبتة على الجهاز بدلًا من تشغيل مفسر البرامج.
</p>

<p>
	سنقوم بالتعديل على برنامجنا ليستقبل الدخل من المستخدم عن طريق وسائط سطر الأوامر، لهذا نُنشئ ملفًا جديدًا بالاسم arguments.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_18" style="">
<span class="pln">nano arguments</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونكتب داخله الشيفرة التالية ونحفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_20" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">);</span></pre>

<p>
	يحوي الكائن العام <code>process</code> في نود على توابع وبيانات تتعلق بالإجرائية الحالية، والخاصية <code>argv</code> ضمنه هي مصفوفة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-strings-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r817/" rel="">سلاسل نصية</a> تُمثل عناصرها وسائط سطر الأوامر المٌمررة للبرنامج عند تنفيذه، وأصبح بإمكاننا الآن تمرير عدة وسائط إلى البرنامج أثناء تنفيذه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_24" style="">
<span class="pln">node arguments</span><span class="pun">.</span><span class="pln">js hello world</span></pre>

<p>
	لنحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_29" style="">
<span class="pun">[</span><span class="pln">
   </span><span class="str">'/usr/bin/node'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'/home/hassan/first-program/arguments.js'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln">
  </span><span class="str">'world'</span><span class="pln">
</span><span class="pun">]</span></pre>

<p>
	يمثل أول وسيط ضمن المصفوفة <code>process.argv</code> مسار الملف التنفيذي لنود الذي نفَّذ البرنامج، بينما الوسيط الثاني هو مسار ذلك البرنامج، والوسائط البقية تمثل الوسائط التي أدخلها المستخدم في حالتنا هي كلمة hello وكلمة world، وهي عادةً ما يهمنا عند التعامل مع الوسائط المُمررة للبرنامج وليس الوسائط التي يمررها نود افتراضيًا.
</p>

<p>
	الآن نفتح ملف البرنامج arguments.js مجددًا لنعدل عليه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_31" style="">
<span class="pln">nano arguments</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونحذف التعليمة السابقة ونضع بدلًا منها التعليمة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_33" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span></pre>

<p>
	بما أن الخاصية <code>argv</code> هي <a href="https://wiki.hsoub.com/JavaScript/Array" rel="external">مصفوفة Array</a>، يمكننا الاستفادة من التوابع المتاحة ضمن <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r818/" rel="">المصفوفات في جافاسكربت</a>، مثل التابع <a href="https://wiki.hsoub.com/JavaScript/Array/slice" rel="external"><code>slice</code></a> لنختار العناصر التي نريدها فقط من المصفوفة، فنمرر له العدد <code>2</code> كوسيط لنحصل على كافة عناصر المصفوفة <code>argv</code> بعد العنصر الثاني والتي تمثل الوسائط التي مررها المستخدم بالضبط.
</p>

<p>
	نعيد تنفيذ البرنامج كما نفذناه آخر مرة ونلاحظ الفرق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_36" style="">
<span class="pln">node arguments</span><span class="pun">.</span><span class="pln">js hello world</span></pre>

<p>
	سيظهر لنا الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_40" style="">
<span class="pun">[</span><span class="pln"> </span><span class="str">'hello'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'world'</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	بعد أن أصبح البرنامج يستقبل الدخل من المستخدم، سنطوره الآن ليعرض لنا متغيرات البيئة المتاحة للبرنامج.
</p>

<h2>
	الخطوة الرابعة - الوصول لمتغيرات البيئة
</h2>

<p>
	سنعرض في هذه الخطوة متغيرات البيئة environment variables المتاحة في النظام وقيمها باستخدام الكائن العام <code>process.env</code> ونطبعها في الطرفية، فمتغيرات البيئة هي بيانات على شكل مفتاح وقيمة key/value مُخزَّنة خارج البرنامج يوفرها <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a>، حيث يتم تعيين قيمها إما من قبل النظام أو المستخدم، وتكون متاحة لجميع الإجرائيات لاستخدامها كطريقة لضبط إعدادات البرامج أو حالتها أو طريقة عملها، ويمكننا الوصول إليها عن طريق الكائن العام <code>process</code>.
</p>

<p>
	نُنشئ ملفًا جديدًا بالاسم <code>environment.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_43" style="">
<span class="pln">nano environment</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونكتب داخله الشيفرة التالية ونحفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_45" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">);</span></pre>

<p>
	يحوي الكائن <code>env</code> على متغيرات البيئة المتاحة لحظة تشغيل نود للبرنامج.
</p>

<p>
	ننفذ الآن البرنامج الجديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_47" style="">
<span class="pln">node environment</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نلاحظ ظهور خرج مشابه للتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_50" style="">
<span class="pun">{</span><span class="pln">
  SHELL</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/bin/bash'</span><span class="pun">,</span><span class="pln">
  SESSION_MANAGER</span><span class="pun">:</span><span class="pln"> </span><span class="str">'local/hassan-laptop:@/tmp/.ICE-unix/1638,unix/hassan-laptop:/tmp/.ICE-unix/1638'</span><span class="pun">,</span><span class="pln">
  WINDOWID</span><span class="pun">:</span><span class="pln"> </span><span class="str">'0'</span><span class="pun">,</span><span class="pln">
  QT_ACCESSIBILITY</span><span class="pun">:</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">,</span><span class="pln">
  COLORTERM</span><span class="pun">:</span><span class="pln"> </span><span class="str">'truecolor'</span><span class="pun">,</span><span class="pln">
  XDG_CONFIG_DIRS</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/home/hassan/.config/kdedefaults:/etc/xdg/xdg-plasma:/etc/xdg:/usr/share/kubuntu-default-settings/kf5-settings'</span><span class="pun">,</span><span class="pln">
  GTK_IM_MODULE</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ibus'</span><span class="pun">,</span><span class="pln">
  LANGUAGE</span><span class="pun">:</span><span class="pln"> </span><span class="str">'en_US:ar'</span><span class="pun">,</span><span class="pln">
  SSH_AGENT_PID</span><span class="pun">:</span><span class="pln"> </span><span class="str">'1427'</span><span class="pun">,</span><span class="pln">
  PWD</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/home/hassan/first-program'</span><span class="pun">,</span><span class="pln">
  LOGNAME</span><span class="pun">:</span><span class="pln"> hassan</span><span class="pun">,</span><span class="pln">
  HOME</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/home/hassan'</span><span class="pun">,</span><span class="pln">
  IM_CONFIG_PHASE</span><span class="pun">:</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">,</span><span class="pln">
  LANG</span><span class="pun">:</span><span class="pln"> </span><span class="str">'en_US.UTF-8'</span><span class="pun">,</span><span class="pln">
  LESSCLOSE</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/usr/bin/lesspipe %s %s'</span><span class="pun">,</span><span class="pln">
  TERM</span><span class="pun">:</span><span class="pln"> </span><span class="str">'xterm-256color'</span><span class="pun">,</span><span class="pln">
  USER</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hassan'</span><span class="pun">,</span><span class="pln">
  PATH</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/home/hassan/.nvm/versions/node/v16.15.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin'</span><span class="pun">,</span><span class="pln">
  DBUS_SESSION_BUS_ADDRESS</span><span class="pun">:</span><span class="pln"> </span><span class="str">'unix:path=/run/user/1000/bus'</span><span class="pun">,</span><span class="pln">
  OLDPWD</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/'</span><span class="pun">,</span><span class="pln">
  _</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/home/hassan/.nvm/versions/node/v16.15.1/bin/node'</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	القيم الظاهرة في متغيرات البيئة في الخرج السابق تعتمد بغالبها على إعدادات نظام التشغيل، لذا ستلاحظ وجود فرق في الخرج عند محاولتك لتنفيذ البرنامج، والآن بدلًا من عرض قائمة بكافة متغيرات البيئة المتاحة سنطور البرنامج للحصول على متغير معين منها فقط.
</p>

<h2>
	الخطوة الخامسة - الوصول لمتغير بيئة محدد
</h2>

<p>
	تُمثل خصائص الكائن <code>process.env</code> رابطة بين أسماء متغيرات البيئة وقيمها مُخزَّنة كسلاسل نصية، حيث يمكننا الوصول لأي خاصية ضمن الكائن في جافاسكربت بذكر اسمها بين قوسين مربعين.
</p>

<p>
	نفتح الملف <code>environment.js</code> ضمن محرر النصوص ونعدل محتواه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_52" style="">
<span class="pln">nano environment</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نعدل التعليمة الموجودة فيه لتصبح ما يلي ثم نحفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_54" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">[</span><span class="str">"HOME"</span><span class="pun">]);</span></pre>

<p>
	ننفذ البرنامج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_57" style="">
<span class="pln">node environment</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	نحصل على خرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_59" style="">
<span class="str">/home/</span><span class="pln">hassan</span></pre>

<p>
	بدلًا من طباعة الكائن <code>process.env</code> بكل قيمه اخترنا الخاصية <code>HOME</code> فقط منه، والتي تمثل مسار مجلد المستخدم الحالي، وهي نفس القيمة التي يمثلها متغير البيئة <code>‎$HOME</code> المتوفر في بيئات يونكس، وستلاحظ اختلافًا في خرج هذا البرنامج أيضًا عند تنفيذه لنفس السبب السابق، حيث سيُعرَض مسار مجلد المستخدم الخاص بك.
</p>

<p>
	والآن بعد أن تعلمنا طريقة الوصول لقيمة متغير بيئة محدد، سنطور البرنامج ليسأل المستخدم عن متغير البيئة الذي يريد عرضه.
</p>

<h2>
	الخطوة السادسة - جلب متغير بيئة يحدده المستخدم
</h2>

<p>
	سنستفيد من إمكانية وصولنا لوسائط سطر الأوامر التي يُمرِّرها المستخدم، مع إمكانية وصولنا لمتغيرات البيئة لإنشاء أداة سطر أوامر بسيطة مهمتها طباعة قيمة متغير بيئة محدد على الشاشة.
</p>

<p>
	نُنشئ ملفًا جديدًا بالاسم echo.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_61" style="">
<span class="pln">nano echo</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونكتب داخله الشيفرة التالية ونحفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_63" style="">
<span class="kwd">const</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">[</span><span class="pln">args</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]]);</span></pre>

<p>
	يُخزن السطر الأول من هذا البرنامج جميع الوسائط التي مَرَّرها المستخدم ضمن ثابت يدعى <code>args</code>، ثم يطبع السطر الثاني عند تنفيذه متغير بيئة محدد بحسب قيمة أول عنصر من عناصر الثابت <code>args</code>، أي بحسب أول وسيط مرَّره المستخدم عند تنفيذ البرنامج.
</p>

<p>
	ننفِّذ البرنامج ونمرر له اسم متغير بيئة ما كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_65" style="">
<span class="pln">node echo</span><span class="pun">.</span><span class="pln">js HOME</span></pre>

<p>
	سيظهر لنا الخرج التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_67" style="">
<span class="str">/home/</span><span class="pln">hassan</span></pre>

<p>
	حُفِظ الوسيط <code>HOME</code> الذي مرَّرناه للبرنامج السابق ضمن المصفوفة <code>args</code>، ثم استخدمناه للعثور على قيمة متغير البيئة المقابل له باستخدام الكائن <code>process.env</code>، وبذلك يصبح بإمكاننا الوصول لقيمة أي متغير بيئة متوفر في النظام، وجرِّب الآن بنفسك وحاول عرض قيم متغيرات البيئة التالية: <code>PWD</code>, <code>USER</code>, <code>PATH</code>.
</p>

<p>
	والآن سنطور البرنامج لعرض عدة متغيرات بيئة معًا يطلبها المستخدم بدلًا من واحد فقط.
</p>

<h2>
	الخطوة السابعة - عرض عدة متغيرات بيئة
</h2>

<p>
	يمكن للبرنامج الآن في كل مرة عرض متغير بيئة واحد فقط، لذا في هذه الخطوة سنطوره ليستقبل عدد من الوسائط من سطر الأوامر ويعرض متغيرات البيئة المقابلة لها.
</p>

<p>
	نفتح ملف البرنامج <code>echo.js</code> ضمن محرر النصوص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_69" style="">
<span class="pln">nano echo</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونبدل بمحتواه الشيفرة التالية ثم نحفظ الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_71" style="">
<span class="kwd">const</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">

args</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">arg </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">[</span><span class="pln">arg</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	توفر لنا جافاسكربت افتراضيًا التابع <a href="https://wiki.hsoub.com/JavaScript/Array/forEach" rel="external"><code>forEach</code></a> ضمن المصفوفات، والذي يقبل تابع رد نداء callback كمعامل له يتم استدعاءه خلال المرور على كل عنصر من عناصر المصفوفة، حيث نلاحظ أننا مررنا للتابع <code>forEach</code> من الكائن <code>args</code> رد نداء يمثل وظيفة طبع قيمة متغير البيئة المقابل للوسيط الحالي.
</p>

<p>
	ننفذ البرنامج السابق ونمرر له عدة أسماء لمتغيرات بيئة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_73" style="">
<span class="pln">node echo</span><span class="pun">.</span><span class="pln">js HOME PWD</span></pre>

<p>
	لنحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_75" style="">
<span class="str">/home/</span><span class="pln">hassan
</span><span class="pun">/</span><span class="pln">home</span><span class="pun">/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">first</span><span class="pun">-</span><span class="pln">program</span></pre>

<p>
	نتأكد باستخدامنا للتابع <code>forEach</code> من معالجة كافة الوسائط التي مررها المستخدم للبرنامج، والمُخزنة ضمن الثابت <code>args</code> وطباعة متغير البيئة المقابل لها، وبعد أن أصبح البرنامج الآن يعرض قيم جميع متغيرات البيئة التي يطلبها المستخدم، يجب معالجة الحالة التي يمرر فيها المستخدم متغير بيئة غير موجود.
</p>

<h2>
	الخطوة الثامنة - معالجة طلب المستخدم لمتغير بيئة غير موجود
</h2>

<p>
	لنحاول طلب عرض قيمة متغير بيئة ما غير موجود من البرنامج ونلاحظ ماذا سيحدث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_77" style="">
<span class="pln">node echo</span><span class="pun">.</span><span class="pln">js HOME PWD NOT_DEFINED</span></pre>

<p>
	نحصل على خرج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_79" style="">
<span class="str">/home/</span><span class="pln">hassan
</span><span class="pun">/</span><span class="pln">home</span><span class="pun">/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">first</span><span class="pun">-</span><span class="pln">program
</span><span class="kwd">undefined</span></pre>

<p>
	نلاحظ عرض قيمة أول متغيري بيئة في أول سطرين كما هو متوقع، أما في السطر الأخير ظهرت لنا القيمة undefined، وكما نعلم في جافاسكربت القيمة undefined تعني أن الخاصية أو المتغير غير مُعرَّف ولم تُحدد قيمته بعد، وذلك لأن متغير البيئة الذي طلبناه <code>NOT_DEFINED</code> غير موجود لذا طُبعت تلك القيمة عوضًا، وبدلًا من ذلك يمكننا عرض رسالة خطأ للمستخدم تُعلمه أن متغير البيئة الذي يطلبه غير موجود.
</p>

<p>
	نفتح الملف مرة أخرى للتعديل عليه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_81" style="">
<span class="pln">nano echo</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	ونضيف الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_83" style="">
<span class="kwd">const</span><span class="pln"> args </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">argv</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">

args</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">arg </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let envVar </span><span class="pun">=</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">[</span><span class="pln">arg</span><span class="pun">];</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">envVar </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Could</span><span class="pln"> not find </span><span class="str">"${arg}"</span><span class="pln"> in environment</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">envVar</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ما قمنا به هو تعديل تابع رد النداء المُمرر للتابع <code>forEach</code> ليقوم بالخطوات التالية:
</p>

<ol>
<li>
		استخراج متغير البيئة للوسيط الحالي وتخزين قيمته في المتغير <code>envVar</code>.
	</li>
	<li>
		التحقق ما إذا كانت قيمة <code>envVar</code> غير مُعرّفة <code>undefined</code>.
	</li>
	<li>
		في حال كانت قيمة <code>envVar</code> غير مُعرّفة <code>undefined</code> نطبع رسالة تُعلم المستخدم بعدم وجود متغير بيئة لهذا الوسيط.
	</li>
	<li>
		في حال عُثر على متغير البيئة نطبع قيمته.
	</li>
</ol>
<p>
	يطبع التابع <code>console.error</code> رسالة على الشاشة من خلال مجرى الخطأ القياسي <code>stderr</code>، بينما يطبع التابع <code>console.log</code> القيم المُمررة له عبر مجرى الخرج القياسي <code>stdout</code>، ولن نلاحظ أي فرق بين استخدام المجريين <code>stdout</code> و <code>stderr</code> عند تنفيذ البرنامج من خلال سطر الأوامر، ويعتبر استخدام كل تابع منهما في حالته الخاصة وتحديدًا طباعة رسائل الخطأ عبر المجرى <code>stderr</code> من الممارسات الجيدة في تطوير البرمجيات، لأنه يُمَكّن البرامج الأخرى من تحديد تلك الأخطاء والتعامل معها إن لزم ذلك.
</p>

<p>
	والآن نعيد تنفيذ البرنامج كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_85" style="">
<span class="pln">node echo</span><span class="pun">.</span><span class="pln">js HOME PWD NOT_DEFINED</span></pre>

<p>
	لنحصل على الخرج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3331_87" style="">
<span class="str">/home/</span><span class="pln">hassan
</span><span class="pun">/</span><span class="pln">home</span><span class="pun">/</span><span class="pln">hassan</span><span class="pun">/</span><span class="pln">first</span><span class="pun">-</span><span class="pln">program
</span><span class="typ">Could</span><span class="pln"> not find </span><span class="str">"NOT_DEFINED"</span><span class="pln"> in environment</span></pre>

<p>
	نلاحظ ظهور رسالة للمستخدم تفيد بأن المتغير NOT_DEFINED لم يُعثر عليه.
</p>

<h2>
	ختامًا
</h2>

<p>
	بدأنا في هذا الفصل بكتابة برنامج بسيط لطباعة عبارة بسيطة على الشاشة، وانتهينا بكتابة أداة لسطر الأوامر في نود تعرض للمستخدم متغيرات البيئة التي يطلبها، ويمكنك الآن التطوير على تلك الأداة بنفسك بمحاولة التحقق مثلًا من مدخلات المستخدم قبل طباعة أي قيمة، وإعادة خطأ مباشرةً في حال أن أحد متغيرات البيئة المطلوبة غير موجود، وبذلك سيحصل المستخدم من البرنامج على قيم متغيرات البيئة فقط في حال كانت جميع المتغيرات المطلوبة موجودة.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-write-and-run-your-first-program-in-node-js" rel="external nofollow">How To Write and Run Your First Program in Node.js</a> لصاحبه Stack Abuse.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%AE%D8%A7%D8%B1%D8%AC-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1402/" rel="">بيئة Node.js: استخدام جافاسكربت خارج المتصفح</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-nodejs-r1467/" rel="">البرمجة غير المتزامنة في Node.js</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1711</guid><pubDate>Wed, 14 Sep 2022 11:15:18 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x648;&#x62D;&#x62F;&#x627;&#x62A; Node.js &#x627;&#x644;&#x623;&#x633;&#x627;&#x633;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-nodejs-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r1470/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/6203a46e4b78b_---.png.9f2e4e3ad6fea5f3ea24e1c9e6a35867.png" /></p>

<p>
	سنتعرّف من خلال هذا المقال على وحدات <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a> الأساسية مثل وحدة fs وpath وos وevents وتوابعها المتعددة، كما يمكنك بناء وحدة مخصَّصة بالاعتماد على الوحدات الأساسية، حيث سنتعرّف على كيفية استخدام واجهة module.exports البرمجية لتصدير بياناتك، وسنتعرّف على وحدة <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%AA%D8%B9%D9%84%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-mysql-r297/" rel="">MySQL</a> للتعامل مع قواعد البيانات.
</p>

<h2>
	وحدة fs
</h2>

<p>
	توفّر وحدة <code>fs</code> عمليات متعددة ومفيدة للوصول إلى نظام الملفات والتفاعل معه، وليست هناك حاجة لتثبيتها نظرًا لكونها جزءًا من نواة نود، إذ يمكن استخدامها ببساطة عن طريق طلبها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_9" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span></pre>

<p>
	ثم يمكنك الوصول إلى جميع توابعها التي تشمل ما يلي:
</p>

<ul>
<li>
		<code>fs.access()‎</code>: يتحقق من وجود الملف ويمكن لنود الوصول إلى الملف باستخدام أذوناته.
	</li>
	<li>
		<code>fs.appendFile()‎</code>: يُلحِق بيانات بملف، وينشئ الملف إذا كان غير موجود مسبقًا.
	</li>
	<li>
		<code>fs.chmod()‎</code>: يغيّر أذونات الملف المحدَّد بواسطة اسم الملف المُمرَّر، ويتعلق بالتابعَين <code>fs.lchmod()‎</code> و<code>fs.fchmod()‎</code>.
	</li>
	<li>
		<code>fs.chown()‎</code>: يغيّر مالك ومجموعة الملف المحدَّد بواسطة اسم الملف المُمرَّر، ويتعلق بالتابعَين <code>fs.fchown()‎</code> و<code>fs.lchown()‎</code>.
	</li>
	<li>
		<code>fs.close()‎</code>: يغلق واصف الملف file descriptor.
	</li>
	<li>
		<code>fs.copyFile()‎</code>: ينسخ ملفًا.
	</li>
	<li>
		<code>fs.createReadStream()‎</code>: ينشئ مجرى stream ملف قابل للقراءة.
	</li>
	<li>
		<code>fs.createWriteStream()‎</code>: ينشئ مجرى ملف قابل للكتابة.
	</li>
	<li>
		<code>fs.link()‎</code>: ينشئ رابطًا صلبًا hard link جديدًا إلى ملف.
	</li>
	<li>
		<code>fs.mkdir()‎</code>: ينشئ مجلدًا جديدًا.
	</li>
	<li>
		<code>fs.mkdtemp()‎</code>: ينشئ مجلدًا مؤقتًا.
	</li>
	<li>
		<code>fs.open()‎</code>: يضبط نمط الملف.
	</li>
	<li>
		<code>fs.readdir()‎</code>: يقرأ محتويات مجلد.
	</li>
	<li>
		<code>fs.readFile()‎</code>: يقرأ محتوى ملف، ويتعلق بالتابع <code>fs.read()‎</code>.
	</li>
	<li>
		<code>fs.readlink()‎</code>: يقرأ قيمة الوصلة الرمزية symbolic link.
	</li>
	<li>
		<code>fs.realpath()‎</code>: يُستخدَم لربط resolve مؤشرات مسار الملف النسبي (<code>.</code> و<code>..</code>) مع المسار الكامل.
	</li>
	<li>
		<code>fs.rename()‎</code>: يعيد تسمية ملف أو مجلد.
	</li>
	<li>
		<code>fs.rmdir()‎</code>: يزيل مجلدًا.
	</li>
	<li>
		<code>fs.stat()‎</code>: يعيد حالة الملف المحدَّد بواسطة اسم الملف المُمرَّر، ويتعلق بالتابعَين <code>fs.fstat()‎</code> و<code>fs.lstat()‎</code>.
	</li>
	<li>
		<code>fs.symlink()‎</code>: ينشئ وصلةً رمزيةً جديدًا إلى ملف.
	</li>
	<li>
		<code>fs.truncate()‎</code>: يقتطع الملف المحدَّد بواسطة اسم الملف المُمرَّر إلى طول معيَّن، ويتعلق بالتابع <code>fs.ftruncate()‎</code>.
	</li>
	<li>
		<code>fs.unlink()‎</code>: يزيل ملفًا أو وصلةً رمزيةً.
	</li>
	<li>
		<code>fs.unwatchFile()‎</code>: يوقِف مشاهدة التغييرات على ملف.
	</li>
	<li>
		<code>fs.utimes()‎</code>: يغيّر الطابع الزمني timestamp للملف المحدَّد باسم الملف المُمرَّر، ويتعلق بالتابع <code>fs.futimes()‎</code>.
	</li>
	<li>
		<code>fs.watchFile()‎</code>: يبدأ بمشاهدة التغييرات على ملف، ويتعلق بالتابع <code>fs.watch()‎</code>.
	</li>
	<li>
		<code>fs.writeFile()‎</code>: يكتب بيانات في ملف، ويتعلق بالتابع: <code>fs.write()‎</code>.
	</li>
</ul>
<p>
	جميع التوابع في وحدة <code>fs</code> غير متزامنة افتراضيًا، ولكن يمكنها العمل بطريقة متزامنة من خلال إلحاق الكلمة <code>Sync</code> باسم التابع كما يلي:
</p>

<ul>
<li>
		<code>fs.rename()‎</code>
	</li>
	<li>
		<code>fs.renameSync()‎</code>
	</li>
	<li>
		<code>fs.write()‎</code>
	</li>
	<li>
		<code>fs.writeSync()‎</code>
	</li>
</ul>
<p>
	ويحدِث ذلك فرقًا كبيرًا في تدفق تطبيقك.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: تتضمن نود 10 أنواع من <a href="https://nodejs.org/api/fs.html#fs_fs_promises_api" rel="external nofollow">الدعم التجريبي</a> لواجهة برمجة تطبيقات قائمة على الوعود promise.
	</p>
</blockquote>

<p>
	لنختبر التابع <code>fs.rename()‎</code> مثلًا، حيث تُستخدَم <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-stream-api-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1433/" rel="">واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> غير المتزامنة مع <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%B1%D8%AF%D9%88%D8%AF-%D8%A7%D9%84%D9%86%D8%AF%D8%A7%D8%A1-callbacks-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r914/" rel="">دالة رد نداء callback</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_12" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">rename</span><span class="pun">(</span><span class="str">'before.json'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'after.json'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">//done</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يمكن استخدام واجهة برمجة تطبيقات متزامنة مثل المثال التالي مع كتلة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1%D8%8C-%D8%AC%D8%B1%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%B7-trycatch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r908/" rel="">try/catch</a> لمعالجة الأخطاء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_16" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  fs</span><span class="pun">.</span><span class="pln">renameSync</span><span class="pun">(</span><span class="str">'before.json'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'after.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="com">//done</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الاختلاف الرئيسي هو إيقاف تنفيذ السكربت الخاص بك في المثال الثاني إلى أن تنجح عملية الملف.
</p>

<p>
	للمزيد من المعلومات حول هذه الوحدة، يمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Node.js/fs" rel="external">التعامل مع نظام الملفات في Node.js</a> في موسوعة حسوب.
</p>

<h2>
	وحدة المسار path
</h2>

<p>
	توفِّر وحدة <code>path</code> عمليات متعددة ومفيدة للوصول إلى نظام الملفات والتفاعل معه، وليست هناك حاجة لتثبيتها نظرًا لكونها جزءًا من نواة نود، إذ يمكن استخدامها ببساطة عن طريق طلبها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_19" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">)</span></pre>

<p>
	توفِّر هذه الوحدة محرف <code>path.sep</code> الذي يوفّر فاصل مقاطع المسار path segment separator (<code>\</code> على نظام ويندوز Windows و<code>/</code> على نظامي <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس Linux</a> و<a href="https://academy.hsoub.com/apps/operating-systems/macos/" rel="">macOS</a>)، بالإضافة إلى محرف <code>path.delimiter</code> الذي يوفّر محدّد المسار path delimiter (<code>;</code> على ويندوز Windows و<code>:</code> على نظامَي لينكس Linux وmacOS).
</p>

<p>
	توابع وحدة <code>path</code> هي:
</p>

<ul>
<li>
		<code>path.basename()‎</code>
	</li>
	<li>
		<code>path.dirname()‎</code>
	</li>
	<li>
		<code>path.extname()‎</code>
	</li>
	<li>
		<code>path.isAbsolute()‎</code>
	</li>
	<li>
		<code>path.join()‎</code>
	</li>
	<li>
		<code>path.normalize()‎</code>
	</li>
	<li>
		<code>path.parse()‎</code>
	</li>
	<li>
		<code>path.relative()‎</code>
	</li>
	<li>
		<code>path.resolve()‎</code>
	</li>
</ul>
<p>
	للمزيد من المعلومات حول هذه الوحدة، يمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Node.js/path" rel="external">وحدة المسار (Path) في Node.js</a> في موسوعة حسوب.
</p>

<h3>
	التابع path.basename()‎
</h3>

<p>
	يعيد هذا التابع الجزء الأخير من المسار، ويمكن للمعامِل الثاني تحديد امتداد الملف لإعطاء الملف دون امتداده كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_23" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">basename</span><span class="pun">(</span><span class="str">'/test/something'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//something</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">basename</span><span class="pun">(</span><span class="str">'/test/something.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//something.txt</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">basename</span><span class="pun">(</span><span class="str">'/test/something.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//something</span></pre>

<h3>
	التابع path.dirname()‎
</h3>

<p>
	يعيد هذا التابع جزء المجلد أو الدليل من المسار كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_25" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">dirname</span><span class="pun">(</span><span class="str">'/test/something'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// /test</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">dirname</span><span class="pun">(</span><span class="str">'/test/something/file.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// /test/something</span></pre>

<h3>
	التابع path.extname()‎
</h3>

<p>
	يعيد هذا التابع جزء الامتداد من المسار كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_27" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">dirname</span><span class="pun">(</span><span class="str">'/test/something'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// ''</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">dirname</span><span class="pun">(</span><span class="str">'/test/something/file.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// '.txt'</span></pre>

<h3>
	التابع path.isAbsolute()‎
</h3>

<p>
	يعيد هذا التابع القيمة true إذا كان المسار مسارًا مطلقًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_29" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">isAbsolute</span><span class="pun">(</span><span class="str">'/test/something'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">isAbsolute</span><span class="pun">(</span><span class="str">'./test/something'</span><span class="pun">)</span><span class="pln"> </span><span class="com">// false</span></pre>

<h3>
	التابع path.join()‎
</h3>

<p>
	يربط هذا التابع جزأين أو أكثر من المسار مع بعضها البعض كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_31" style="">
<span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'flavio'</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">join</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'users'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="str">'notes.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//'/users/flavio/notes.txt'</span></pre>

<h3>
	التابع path.normalize()‎
</h3>

<p>
	يحاول هذا التابع حساب المسار الفعلي عندما يحتوي على محددات نسبية مثل <code>.</code> أو <code>..</code> أو شرطات مائلة مزدوجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_33" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">normalize</span><span class="pun">(</span><span class="str">'/users/flavio/..//test.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">///users/test.txt</span></pre>

<h3>
	التابع path.parse()‎
</h3>

<p>
	يوزّع هذا التابع مسارًا على كائن يتكون من أجزاء متعددة هي:
</p>

<ul>
<li>
		<code>root</code>: يمثّل الجذر.
	</li>
	<li>
		<code>dir</code>: هو مسار المجلد بداية من الجذر.
	</li>
	<li>
		<code>base</code>: يمثّل اسم الملف مع الامتداد.
	</li>
	<li>
		<code>name</code>: هو اسم الملف.
	</li>
	<li>
		<code>ext</code>: يمثّل امتداد الملف.
	</li>
</ul>
<p>
	إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_35" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">parse</span><span class="pun">(</span><span class="str">'/users/test.txt'</span><span class="pun">)</span></pre>

<p>
	وتكون النتيجة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_37" style="">
<span class="pun">{</span><span class="pln">
  root</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/'</span><span class="pun">,</span><span class="pln">
  dir</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/users'</span><span class="pun">,</span><span class="pln">
  base</span><span class="pun">:</span><span class="pln"> </span><span class="str">'test.txt'</span><span class="pun">,</span><span class="pln">
  ext</span><span class="pun">:</span><span class="pln"> </span><span class="str">'.txt'</span><span class="pun">,</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'test'</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	التابع path.relative()‎
</h3>

<p>
	يقبل هذا التابع مسارين على أساس وسائط، ويعيد المسار النسبي من المسار الأول إلى المسار الثاني بناءً على مجلد العمل الحالي مثل المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_39" style="">
<span class="pln">require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">relative</span><span class="pun">(</span><span class="str">'/Users/flavio'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//'test.txt'</span><span class="pln">
require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">).</span><span class="pln">relative</span><span class="pun">(</span><span class="str">'/Users/flavio'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'/Users/flavio/something/test.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//'something/test.txt'</span></pre>

<h3>
	التابع path.resolve()‎
</h3>

<p>
	يمكنك حساب المسار المطلق لمسار نسبي باستخدام التابع <code>path.resolve()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_41" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="str">'flavio.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//‫'‎/Users/flavio/flavio.txt' إذا شُغِّل من المجلد المحلي</span></pre>

<p>
	إذا حدّدت المعامل الثاني، فسيستخدم التابع <code>resolve</code> المعامِل الأول أساسًا للمعامِل الثاني كما يلي:
</p>

<pre class="ipsCode">
path.resolve('tmp', 'flavio.txt') // '/Users/flavio/tmp/flavio.txt' إذا شُغِّل من المجلد المحلي
</pre>

<p>
	إذا بدأ المعامِل الأول بشرطة مائلة، فهذا يعني أنه مسار مطلق كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_43" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="str">'/etc'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'flavio.txt'</span><span class="pun">)</span><span class="com">//'/etc/flavio.txt'</span></pre>

<h2>
	وحدة os
</h2>

<p>
	توفِّر هذه الوحدة عمليات متعددة يمكنك استخدامها لاسترداد معلومات من <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a> الأساسي والحاسوب الذي يعمل عليه البرنامج والتفاعل معه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_46" style="">
<span class="kwd">const</span><span class="pln"> os </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'os'</span><span class="pun">)</span></pre>

<p>
	هناك بعض الخاصيات المفيدة التي تخبرنا ببعض الأمور الأساسية المتعلقة بمعالجة الملفات مثل:
</p>

<ul>
<li>
		<code>os.EOL</code> التي تعطينا متسلسلة محدّد السطور، وهي <code>‎\n</code> على نظامَي لينكس Linux وmacOS؛ أما على نظام ويندوز Windows فهي <code>‎\r\n</code>.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: نقصد بنظامَي لينكس وmacOS منصات POSIX، واستبعدنا بهدف التبسيط أنظمة التشغيل الأخرى الأقل شيوعًا التي يمكن تشغيل نود Node عليها.
	</p>
</blockquote>

<ul>
<li>
		<code>os.constants.signals</code> التي تعطينا كل الثوابت المتعلقة بمعالجة إشارات العمليات مثل SIGHUP وSIGKILL وما إلى ذلك، كما يمكنك الاطلاع على جميع هذه الثوابت على <a href="https://wiki.hsoub.com/Node.js/os" rel="external">/node_os</a>.
	</li>
	<li>
		<code>os.constants.errno</code> التي تضبط الثوابت في تقارير الخطأ مثل EADDRINUSE وEOVERFLOW وغير ذلك.
	</li>
</ul>
<p>
	لنتعرّف الآن على التوابع الرئيسية التي توفرها وحدة <code>os</code> وهي:
</p>

<ul>
<li>
		<code>os.arch()‎</code>
	</li>
	<li>
		<code>os.cpus()‎</code>
	</li>
	<li>
		<code>os.endianness()‎</code>
	</li>
	<li>
		<code>os.freemem()‎</code>
	</li>
	<li>
		<code>os.homedir()‎</code>
	</li>
	<li>
		<code>os.hostname()‎</code>
	</li>
	<li>
		<code>os.hostname()‎</code>
	</li>
	<li>
		<code>os.loadavg()‎</code>
	</li>
	<li>
		<code>os.networkInterfaces()‎</code>
	</li>
	<li>
		<code>os.platform()‎</code>
	</li>
	<li>
		<code>os.release()‎</code>
	</li>
	<li>
		<code>os.tmpdir()‎</code>
	</li>
	<li>
		<code>os.totalmem()‎</code>
	</li>
	<li>
		<code>os.type()‎</code>
	</li>
	<li>
		<code>os.uptime()‎</code>
	</li>
	<li>
		<code>os.userInfo()‎</code>
	</li>
</ul>
<p>
	للمزيد من المعلومات حول هذه الوحدة، يمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Node.js/os" rel="external">الوحدة os في Node.js</a> في موسوعة حسوب.
</p>

<h3>
	التابع os.arch()‎
</h3>

<p>
	يعيد هذا التابع السلسلة النصية التي تحدد البنية الأساسية مثل <code>arm</code> و<code>x64</code> و<code>arm64</code>.
</p>

<h3>
	التابع os.cpus()‎
</h3>

<p>
	يعيد معلومات وحدات المعالجة المركزية المتوفرة على نظامك، وإليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_48" style="">
<span class="pun">[</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz'</span><span class="pun">,</span><span class="pln">
   speed</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2400</span><span class="pun">,</span><span class="pln">
   times</span><span class="pun">:</span><span class="pln">
   </span><span class="pun">{</span><span class="pln"> user</span><span class="pun">:</span><span class="pln"> </span><span class="lit">281685380</span><span class="pun">,</span><span class="pln">
     nice</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
     sys</span><span class="pun">:</span><span class="pln"> </span><span class="lit">187986530</span><span class="pun">,</span><span class="pln">
     idle</span><span class="pun">:</span><span class="pln"> </span><span class="lit">685833750</span><span class="pun">,</span><span class="pln">
     irq</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
 </span><span class="pun">{</span><span class="pln"> model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz'</span><span class="pun">,</span><span class="pln">
   speed</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2400</span><span class="pun">,</span><span class="pln">
   times</span><span class="pun">:</span><span class="pln">
   </span><span class="pun">{</span><span class="pln"> user</span><span class="pun">:</span><span class="pln"> </span><span class="lit">282348700</span><span class="pun">,</span><span class="pln">
     nice</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
     sys</span><span class="pun">:</span><span class="pln"> </span><span class="lit">161800480</span><span class="pun">,</span><span class="pln">
     idle</span><span class="pun">:</span><span class="pln"> </span><span class="lit">703509470</span><span class="pun">,</span><span class="pln">
     irq</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">]</span></pre>

<h3>
	التابع os.endianness()‎
</h3>

<p>
	يعيد هذا التابع القيمة <code>BE</code> أو القيمة <code>LE</code> بناءً على طريقة تصريف نود باستخدام <a href="https://en.wikipedia.org/wiki/Endianness" rel="external nofollow">تخزين البتات الأقل أهمية أولًا Big Endian أو تخزين البتات الأكثر أهمية أولًا Little Endian</a>.
</p>

<h3>
	التابع os.freemem()‎
</h3>

<p>
	يعيد هذا التابع عدد البايتات التي تمثل الذاكرة المتاحة في النظام.
</p>

<h3>
	التابع os.homedir()‎
</h3>

<p>
	يعيد هذا التابع المسار إلى مجلد المستخدِم الحالي الرئيسي مثل المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_50" style="">
<span class="str">'/Users/flavio'</span></pre>

<h3>
	التابع os.hostname()‎
</h3>

<p>
	يعيد هذا التابع اسم المضيف hostname.
</p>

<h3>
	التابع os.loadavg()‎
</h3>

<p>
	يعيد هذا التابع الحساب الذي أجراه نظام التشغيل على متوسط التحميل، حيث يعيد فقط قيمة ذات معنى في نظامَي لينكس Linux وmacOS مثل المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_52" style="">
<span class="pun">[</span><span class="pln"> </span><span class="lit">3.68798828125</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4.00244140625</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11.1181640625</span><span class="pln"> </span><span class="pun">]</span></pre>

<h3>
	التابع os.networkInterfaces()‎
</h3>

<p>
	يعيد هذا التابع تفاصيل واجهات الشبكة المتوفرة على نظامك، وإليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_54" style="">
<span class="pun">{</span><span class="pln"> lo0</span><span class="pun">:</span><span class="pln">
 </span><span class="pun">[</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">'127.0.0.1'</span><span class="pun">,</span><span class="pln">
     netmask</span><span class="pun">:</span><span class="pln"> </span><span class="str">'255.0.0.0'</span><span class="pun">,</span><span class="pln">
     family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IPv4'</span><span class="pun">,</span><span class="pln">
     mac</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe:82:00:00:00:00'</span><span class="pun">,</span><span class="pln">
     internal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
   </span><span class="pun">{</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">'::1'</span><span class="pun">,</span><span class="pln">
     netmask</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'</span><span class="pun">,</span><span class="pln">
     family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IPv6'</span><span class="pun">,</span><span class="pln">
     mac</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe:82:00:00:00:00'</span><span class="pun">,</span><span class="pln">
     scopeid</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
     internal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
   </span><span class="pun">{</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe80::1'</span><span class="pun">,</span><span class="pln">
     netmask</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ffff:ffff:ffff:ffff::'</span><span class="pun">,</span><span class="pln">
     family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IPv6'</span><span class="pun">,</span><span class="pln">
     mac</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe:82:00:00:00:00'</span><span class="pun">,</span><span class="pln">
     scopeid</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
     internal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">],</span><span class="pln">
 en1</span><span class="pun">:</span><span class="pln">
 </span><span class="pun">[</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe82::9b:8282:d7e6:496e'</span><span class="pun">,</span><span class="pln">
     netmask</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ffff:ffff:ffff:ffff::'</span><span class="pun">,</span><span class="pln">
     family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IPv6'</span><span class="pun">,</span><span class="pln">
     mac</span><span class="pun">:</span><span class="pln"> </span><span class="str">'06:00:00:02:0e:00'</span><span class="pun">,</span><span class="pln">
     scopeid</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
     internal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
 </span><span class="pun">{</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">'192.168.1.38'</span><span class="pun">,</span><span class="pln">
     netmask</span><span class="pun">:</span><span class="pln"> </span><span class="str">'255.255.255.0'</span><span class="pun">,</span><span class="pln">
     family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IPv4'</span><span class="pun">,</span><span class="pln">
     mac</span><span class="pun">:</span><span class="pln"> </span><span class="str">'06:00:00:02:0e:00'</span><span class="pun">,</span><span class="pln">
     internal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">],</span><span class="pln">
 utun0</span><span class="pun">:</span><span class="pln">
 </span><span class="pun">[</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe80::2513:72bc:f405:61d0'</span><span class="pun">,</span><span class="pln">
     netmask</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ffff:ffff:ffff:ffff::'</span><span class="pun">,</span><span class="pln">
     family</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IPv6'</span><span class="pun">,</span><span class="pln">
     mac</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fe:80:00:20:00:00'</span><span class="pun">,</span><span class="pln">
     scopeid</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln">
     internal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">]</span><span class="pln"> </span><span class="pun">}</span></pre>

<h3>
	التابع os.platform()‎
</h3>

<p>
	يعيد هذا التابع المنصة الذي جرى تصريف نود من أجلها مثل:
</p>

<ul>
<li>
		<code>darwin0</code>.
	</li>
	<li>
		<code>freebsd</code>.
	</li>
	<li>
		<code><a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">linux</a></code>.
	</li>
	<li>
		<code>openbsd</code>.
	</li>
	<li>
		<code>win32</code>.
	</li>
	<li>
		وغيرها الكثير.
	</li>
</ul>
<h3>
	التابع os.release()‎
</h3>

<p>
	يعيد هذا التابع سلسلةً نصيةً تحدِّد رقم إصدار نظام التشغيل.
</p>

<h3>
	التابع os.tmpdir()‎
</h3>

<p>
	يعيد هذا التابع المسار إلى المجلد المؤقت المعيَّن.
</p>

<h3>
	التابع os.totalmem()‎
</h3>

<p>
	يعيد هذا التابع عدد البايتات الذي يمثِّل إجمالي الذاكرة المتوفرة في النظام.
</p>

<h3>
	التابع os.type()‎
</h3>

<p>
	يحدّد هذا التابع نظام التشغيل كما يلي:
</p>

<ul>
<li>
		<code>Linux</code>.
	</li>
	<li>
		<code>Darwin</code> على نظام macOS.
	</li>
	<li>
		<code>Windows_NT</code> على نظام ويندوز.
	</li>
</ul>
<h3>
	التابع os.uptime()‎
</h3>

<p>
	يعيد هذا التابع عدد الثواني التي عمل فيها الحاسوب منذ آخر إعادة تشغيل.
</p>

<h3>
	التابع os.userInfo()‎
</h3>

<p>
	يعيد معلومات عن المستخدِم الفعّال حاليًا.
</p>

<h2>
	وحدة الأحداث events
</h2>

<p>
	توفِّر لنا وحدة الأحداث <code>events</code> الصنف EventEmitter، وتُعَدّ أساسًا للعمل مع الأحداث في نود.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_56" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'events'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> door </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pun">()</span></pre>

<p>
	يختبر مستمع الأحداث event listener أحداثه الخاصة ويستخدِم الحدثين التاليين:
</p>

<ul>
<li>
		<code>newListener</code> عند إضافة المستمع.
	</li>
	<li>
		<code>removeListener</code> عند إزالة المستمع.
	</li>
</ul>
<p>
	سنشرح فيما يلي التوابع المفيدة التالية:
</p>

<ul>
<li>
		<code>emitter.addListener()‎</code>
	</li>
	<li>
		<code>emitter.emit()‎</code>
	</li>
	<li>
		<code>emitter.eventNames()‎</code>
	</li>
	<li>
		<code>emitter.getMaxListeners()‎</code>
	</li>
	<li>
		<code>emitter.listenerCount()‎</code>
	</li>
	<li>
		<code>emitter.listeners()‎</code>
	</li>
	<li>
		<code>emitter.off()‎</code>
	</li>
	<li>
		<code>emitter.on()‎</code>
	</li>
	<li>
		<code>emitter.once()‎</code>
	</li>
	<li>
		<code>emitter.prependListener()‎</code>
	</li>
	<li>
		<code>emitter.prependOnceListener()‎</code>
	</li>
	<li>
		<code>emitter.removeAllListeners()‎</code>
	</li>
	<li>
		<code>emitter.removeListener()‎</code>
	</li>
	<li>
		<code>emitter.setMaxListeners()‎</code>
	</li>
</ul>
<p>
	للمزيد من المعلومات حول هذه الوحدة، يمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Node.js/events" rel="external">الأحداث في Node.js</a> في موسوعة حسوب.
</p>

<h3>
	التابع emitter.addListener()‎
</h3>

<p>
	وهو الاسم البديل للتابع <code>emitter.on()‎</code>.
</p>

<h3>
	التابع emitter.emit()‎
</h3>

<p>
	يصدر هذا التابع حدثًا، حيث يستدعي بصورة متزامنة كل مستمع حدث بالترتيب الذي سُجِّلت به.
</p>

<h3>
	التابع emitter.eventNames()‎
</h3>

<p>
	يعيد هذا التابع مصفوفةً من السلاسل النصية التي تمثِّل الأحداث المُسجَّلة في كائن EventListener الحالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_58" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">eventNames</span><span class="pun">()</span></pre>

<h3>
	التابع emitter.getMaxListeners()‎
</h3>

<p>
	يُستخدَم هذا التابع للحصول على الحد الأقصى من المستمعين الذي يمكن إضافته إلى كائن EventListener، حيث يُضبَط هذا العدد افتراضيًا على القيمة 10 ولكن يمكن زيادته أو إنقاصه باستخدام <code>setMaxListeners()‎</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_60" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">getMaxListeners</span><span class="pun">()</span></pre>

<h3>
	التابع emitter.listenerCount()‎
</h3>

<p>
	يُستخدَم هذا التابع للحصول على عدد مستمعي الحدث المُمرَّرين على أساس معامِلات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_63" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">listenerCount</span><span class="pun">(</span><span class="str">'open'</span><span class="pun">)</span></pre>

<h3>
	التابع emitter.listeners()‎
</h3>

<p>
	يُستخدَم هذا التابع للحصول على مصفوفة مستمعي الحدث المُمرَّرين على أساس معامِلات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_65" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">listeners</span><span class="pun">(</span><span class="str">'open'</span><span class="pun">)</span></pre>

<h3>
	التابع emitter.off()‎
</h3>

<p>
	يمثّل هذا التابع الاسم البديل للتابع <code>emitter.removeListener()‎</code> المُضاف في الإصدار 10 من نود.
</p>

<h3>
	التابع emitter.on()‎
</h3>

<p>
	يضيف هذا التابع دالة رد النداء التي تُستدعَى عند إصدار حدث، ويُستخدَم هذا التابع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_67" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'open'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'Door was opened'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	التابع emitter.once()‎
</h3>

<p>
	يضيف هذا التابع دالة رد النداء التي تُستدعَى عند إصدار حدث لأول مرة بعد تسجيله، حيث ستُستدعَى دالة رد النداء تلك مرةً واحدةً فقط، ولن تُستدعَى مرةً أخرى.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_69" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'events'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> ee </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">EventEmitter</span><span class="pun">()</span><span class="pln">

ee</span><span class="pun">.</span><span class="pln">once</span><span class="pun">(</span><span class="str">'my-event'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//ًاستدعِ دالة رد النداء مرةً واحدة</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	التابع emitter.prependListener()‎
</h3>

<p>
	يُضاف المستمع الذي تضيفه باستخدام <code>on</code> أو <code>addListener</code> في آخر طابور المستمعين ويُستدعَى أخيرًا كذلك، ولكنه يُضاف ويُستدعَى قبل المستمعين الآخرين باستخدام <code>prependListener</code>.
</p>

<h3>
	التابع emitter.prependOnceListener()‎
</h3>

<p>
	يُضاف المستمع الذي تضيفه باستخدام <code>once</code> في آخر طابور المستمعين ويُستدعَى أخيرًا كذلك، ولكنه يُضاف ويُستدعَى قبل المستمعين الآخرين باستخدام <code>prependOnceListener</code>.
</p>

<h3>
	التابع emitter.removeAllListeners()‎
</h3>

<p>
	يزيل هذا التابع جميع مستمعي الكائن الذي يصدر الأحداث ويستمع إلى حدث محدَّد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_71" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">removeAllListeners</span><span class="pun">(</span><span class="str">'open'</span><span class="pun">)</span></pre>

<h3>
	التابع emitter.removeListener()‎
</h3>

<p>
	يزيل مستمعًا محدَّدًا عن طريق حفظ دالة رد النداء في متغير عند إضافته، بحيث يمكنك الإشارة إليه لاحقًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_73" style="">
<span class="kwd">const</span><span class="pln"> doSomething </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
door</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'open'</span><span class="pun">,</span><span class="pln"> doSomething</span><span class="pun">)</span><span class="pln">
door</span><span class="pun">.</span><span class="pln">removeListener</span><span class="pun">(</span><span class="str">'open'</span><span class="pun">,</span><span class="pln"> doSomething</span><span class="pun">)</span></pre>

<h3>
	التابع emitter.setMaxListeners()‎
</h3>

<p>
	يضبط الحد الأقصى لعدد المستمعين الذي يمكن إضافته إلى كائن EventListener، حيث يُضبَط هذا العدد افتراضيًا على القيمة 10 ولكن يمكن زيادته أو إنقاصه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_75" style="">
<span class="pln">door</span><span class="pun">.</span><span class="pln">setMaxListeners</span><span class="pun">(</span><span class="lit">50</span><span class="pun">)</span></pre>

<h2>
	وحدة HTTP
</h2>

<p>
	توفّر وحدة http في Node.js دوالًا وأصنافًا مفيدة لبناء <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r574/" rel="">خادم HTTP</a>، وتُعَدّ الوحدة الأساسية لشبكات نود، كما يمكن تضمين وحدة http كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_78" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">)</span></pre>

<p>
	توفّر وحدة http بعض الخاصيات properties والتوابع methods والأصناف classes.
</p>

<p>
	للمزيد من المعلومات حول هذه الوحدة، يمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Node.js/http" rel="external">الوحدة HTTP في Node.js</a> في موسوعة حسوب.
</p>

<h3>
	الخاصيات
</h3>

<p>
	توفِّر وحدة HTTP الخاصيات التالية:
</p>

<ul>
<li>
		<code>http.METHODS</code>
	</li>
	<li>
		<code>http.STATUS_CODES</code>
	</li>
	<li>
		<code>http.globalAgent</code>
	</li>
</ul>
<h4>
	الخاصية http.METHODS
</h4>

<p>
	تعطي هذه الخاصية قائمةً بجميع توابع HTTP المدعومة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_80" style="">
<span class="pun">&gt;</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">).</span><span class="pln">METHODS

 </span><span class="pun">[</span><span class="pln"> </span><span class="str">'ACL'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'BIND'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'CHECKOUT'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'CONNECT'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'COPY'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'DELETE'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'GET'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'HEAD'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'LINK'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'LOCK'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'M-SEARCH'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'MERGE'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'MKACTIVITY'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'MKCALENDAR'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'MKCOL'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'MOVE'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'NOTIFY'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'OPTIONS'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'PATCH'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'PROPFIND'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'PROPPATCH'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'PURGE'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'PUT'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'REBIND'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'REPORT'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'SEARCH'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'SUBSCRIBE'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'TRACE'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'UNBIND'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'UNLINK'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'UNLOCK'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'UNSUBSCRIBE'</span><span class="pln"> </span><span class="pun">]</span></pre>

<h4>
	الخاصية http.STATUS_CODES
</h4>

<p>
	تعطي هذه الخاصية قائمةً بجميع رموز حالة HTTP ووصفها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_82" style="">
<span class="pun">&gt;</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">).</span><span class="pln">STATUS_CODES

 </span><span class="pun">{</span><span class="pln"> </span><span class="str">'100'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Continue'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'101'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Switching Protocols'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'102'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Processing'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'200'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'OK'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'201'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Created'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'202'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Accepted'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'203'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Non-Authoritative Information'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'204'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'No Content'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'205'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Reset Content'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'206'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Partial Content'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'207'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Multi-Status'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'208'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Already Reported'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'226'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'IM Used'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'300'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Multiple Choices'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'301'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Moved Permanently'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'302'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Found'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'303'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'See Other'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'304'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Not Modified'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'305'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Use Proxy'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'307'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Temporary Redirect'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'308'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Permanent Redirect'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'400'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Bad Request'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'401'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Unauthorized'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'402'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Payment Required'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'403'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Forbidden'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'404'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Not Found'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'405'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Method Not Allowed'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'406'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Not Acceptable'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'407'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Proxy Authentication Required'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'408'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Request Timeout'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'409'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Conflict'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'410'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Gone'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'411'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Length Required'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'412'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Precondition Failed'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'413'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Payload Too Large'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'414'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'URI Too Long'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'415'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Unsupported Media Type'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'416'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Range Not Satisfiable'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'417'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Expectation Failed'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'418'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'I\'m a teapot'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'421'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Misdirected Request'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'422'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Unprocessable Entity'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'423'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Locked'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'424'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Failed Dependency'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'425'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Unordered Collection'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'426'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Upgrade Required'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'428'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Precondition Required'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'429'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Too Many Requests'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'431'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Request Header Fields Too Large'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'451'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Unavailable For Legal Reasons'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'500'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Internal Server Error'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'501'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Not Implemented'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'502'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Bad Gateway'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'503'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Service Unavailable'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'504'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Gateway Timeout'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'505'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'HTTP Version Not Supported'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'506'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Variant Also Negotiates'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'507'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Insufficient Storage'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'508'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Loop Detected'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'509'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Bandwidth Limit Exceeded'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'510'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Not Extended'</span><span class="pun">,</span><span class="pln">
   </span><span class="str">'511'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Network Authentication Required'</span><span class="pln"> </span><span class="pun">}</span></pre>

<h4>
	الخاصية http.globalAgent
</h4>

<p>
	تؤشّر هذه الخاصية إلى نسخة كائن الوكيل Agent العامة، والذي هو نسخة من الصنف <code>http.Agent</code>، حيث يُستخدَم هذا الصنف لإدارة الاتصالات المستمرة وإعادة استخدام عملاء HTTP، وهو المكوّن الأساسي من شبكات HTTP الخاصة بنود Node.
</p>

<h3>
	التوابع
</h3>

<p>
	توفِّر وحدة HTTP التوابع التالية:
</p>

<ul>
<li>
		<code>http.createServer()‎</code>
	</li>
	<li>
		<code>http.request()‎</code>
	</li>
	<li>
		<code>http.get()‎</code>
	</li>
</ul>
<h4>
	التابع http.createServer()‎
</h4>

<p>
	يعيد هذا التابع نسخةً جديدةً من الصنف <code>http.Server</code>، حيث يُستخدَم كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_84" style="">
<span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">((</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//معالجة كل طلب باستخدام دالة رد النداء هذه</span><span class="pln">
</span><span class="pun">})</span></pre>

<h4>
	التابع http.request()‎
</h4>

<p>
	ينشئ طلب HTTP إلى خادم، مما يؤدي إلى إنشاء نسخة من الصنف <code>http.ClientRequest</code>.
</p>

<h4>
	التابع http.get()‎
</h4>

<p>
	يشبه التابع <code>http.request()‎</code>، ولكنه يضبط تلقائيًا تابع HTTP على GET ويستدعي التابع <code>req.end()‎</code> تلقائيًا.
</p>

<h3>
	الأصناف Classes
</h3>

<p>
	توفِّر وحدة HTTP خمسة أصناف هي:
</p>

<ul>
<li>
		<code>http.Agent</code>
	</li>
	<li>
		<code>http.ClientRequest</code>
	</li>
	<li>
		<code>http.Server</code>
	</li>
	<li>
		<code>http.ServerResponse</code>
	</li>
	<li>
		<code>http.IncomingMessage</code>
	</li>
</ul>
<h4>
	الصنف http.Agent
</h4>

<p>
	نُنشئ نود نسخةً عامةً من الصنف <code>http.Agent</code> لإدارة الاتصالات المستمرة وإعادة استخدام عملاء HTTP، وهو المكوّن الأساسي من شبكات HTTP الخاصة بنود Node، كما يتأكّد هذا الكائن من وضع كل طلب إلى الخادم في طابور ومن إعادة استخدام المقبس socket، كما أنه يحتفظ بمجمّع من المقابس بهدف تحسين الأداء.
</p>

<h4>
	الصنف http.ClientRequest
</h4>

<p>
	يُنشَأ الكائن <code>http.ClientRequest</code> عند استدعاء التابع <code>http.request()‎</code> أو التابع <code>http.get()‎</code>، حيث يُستدعَى حدث <code>response</code> مع الاستجابة عند تلقيها باستخدام نسخة من كائن <code>http.IncomingMessage</code> على أساس وسيط، ويمكن قراءة بيانات الاستجابة المُعادة بطريقتين هما:
</p>

<ul>
<li>
		استدعاء التابع <code>response.read()‎</code>.
	</li>
	<li>
		يمكنك إعداد مستمعٍ للحدث <code>data</code> في معالج الحدث <code>response</code> بحيث يمكنك الاستماع للبيانات المتدفقة إليه.
	</li>
</ul>
<h4>
	الصنف http.Server
</h4>

<p>
	تُنشَأ وتُعاد عادةً نسخة من هذا الصنف عند إنشاء خادم جديد باستخدام التابع <code>http.createServer()‎</code>، يمكنك الوصول إلى توابع كائن خادم بعد إنشائه، وهذه التوابع هي:
</p>

<ul>
<li>
		<code>close()‎</code> الذي يوقِف الخادم من قبول اتصالات جديدة.
	</li>
	<li>
		<code>listen()‎</code> الذي يشغّل خادم HTTP ويستمع للاتصالات.
	</li>
</ul>
<h4>
	الصنف http.ServerResponse
</h4>

<p>
	ينشئه الصنف <code>http.Server</code> ويمرّره على أساس معامل ثانٍ لحدث <code>request</code> الذي يشغّله، حيث يُعرَف هذا الصنف ويُستخدَم في الشيفرة على أنه كائن <code>res</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_86" style="">
<span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">((</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//‫res هو كائن http.ServerResponse</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	التابع الذي ستستدعيه دائمًا في المعالج هو <code>end()‎</code> والذي يغلق الاستجابة بعد اكتمال الرسالة ثم يستطيع الخادم إرسالها إلى العميل، إذ يجب استدعاؤه في كل استجابة، وتُستخدَم التوابع التالية للتفاعل مع ترويسات HTTP:
</p>

<ul>
<li>
		<code>getHeaderNames()‎</code>: للحصول على قائمة بأسماء ترويسات HTTP المضبوطة مسبقًا.
	</li>
	<li>
		<code>getHeaders()‎</code>: للحصول على نسخة من ترويسات HTTP المضبوطة مسبقًا.
	</li>
	<li>
		<code>setHeader('headername', value)‎</code>: يحدّد قيمة ترويسة HTTP.
	</li>
	<li>
		<code>getHeader('headername')‎</code>: للحصول على ترويسة HTTP المضبوطة مسبقًا.
	</li>
	<li>
		<code>removeHeader('headername')‎</code>: يزيل ترويسة HTTP المضبوطة مسبقًا.
	</li>
	<li>
		<code>hasHeader('headername')‎</code>: يعيد القيمة true إذا احتوت الاستجابة على هذه الترويسة المضبوطة.
	</li>
	<li>
		<code>headersSent()‎</code>: يعيد القيمة true إذا أُرسِلت الترويسات إلى العميل. مسبقًا
	</li>
</ul>
<p>
	يمكنك إرسال الترويسات بعد معالجتها إلى العميل عن طريق استدعاء التابع <code>response.writeHead()‎</code> الذي يقبل رمز الحالة statusCode على أساس معامل أول، ورسالة الحالة الاختيارية، وكائن الترويسات، كما يمكنك إرسال البيانات إلى العميل في جسم الاستجابة عن طريق استخدام التابع <code>write()‎</code> الذي سيرسل البيانات المخزَّنة إلى مجرى استجابة HTTP، فإذا لم تُرسَل الترويسات بعد باستخدام التابع <code>response.writeHead()‎</code>، فستُرسَل الترويسات أولًا مع رمز الحالة والرسالة المحدَّدة في الطلب والتي يمكنك تعديلها عن طريق ضبط قيم الخاصيات <code>statusCode</code> و<code>statusMessage</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_88" style="">
<span class="pln">response</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">=</span><span class="pln"> </span><span class="lit">500</span><span class="pln">
response</span><span class="pun">.</span><span class="pln">statusMessage </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Internal Server Error'</span></pre>

<h4>
	الصنف http.IncomingMessage
</h4>

<p>
	يُنشَأ كائن <code>http.IncomingMessage</code> باستخدام:
</p>

<ul>
<li>
		<code>http.Server</code> عند الاستماع إلى الحدث <code>request</code>.
	</li>
	<li>
		<code>http.ClientRequest</code> عند الاستماع إلى الحدث <code>response</code>.
	</li>
</ul>
<p>
	يمكن استخدام كائن <code>http.IncomingMessage</code> للوصول إلى خاصيات الاستجابة التالية:
</p>

<ul>
<li>
		الحالة status باستخدام توابع <code>statusCode</code> و<code>statusMessage</code> الخاصة به.
	</li>
	<li>
		الترويسات باستخدام توابع <code>headers</code> أو <code>rawHeaders</code> الخاصة به.
	</li>
	<li>
		تابع HTTP باستخدام تابع <code>method</code> الخاص به.
	</li>
	<li>
		إصدار HTTP باستخدام تابع <code>httpVersion</code>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%88-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1435/" rel="">عنوان URL</a> باستخدام تابع <code>url</code>.
	</li>
	<li>
		المقبس الأساسي underlying socket باستخدام تابع <code>socket</code>.
	</li>
</ul>
<p>
	يمكن الوصول إلى البيانات باستخدام المجاري streams، حيث ينفّذ كائن <code>http.IncomingMessage</code> واجهة المجرى Stream القابلة للقراءة.
</p>

<h2>
	وحدة MySQL
</h2>

<p>
	تُعَدّ <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-r28/" rel="">MySQL</a> واحدةً من أكثر قواعد البيانات العلائقية شيوعًا في العالم، إذ يحتوي نظام نود Node المجتمعي على حزم مختلفة تتيح لك التعامل مع MySQL وتخزين البيانات واسترداد البيانات وما إلى ذلك، كما سنستخدِم <a href="https://github.com/mysqljs/mysql" rel="external nofollow">حزمة mysqljs/mysql</a> التي تحتوي على أكثر من 12000 نجمة على GitHub وهي موجودة منذ سنوات.
</p>

<h3>
	تثبيت حزمة نود mysql
</h3>

<p>
	يمكنك تثبيتها باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_92" style="">
<span class="pln">npm install mysql</span></pre>

<h3>
	تهيئة الاتصال بقاعدة البيانات
</h3>

<p>
	يجب تضمين الحزمة أولًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_94" style="">
<span class="kwd">const</span><span class="pln"> mysql </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'mysql'</span><span class="pun">)</span></pre>

<p>
	ثم تنشئ اتصالًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_96" style="">
<span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  user</span><span class="pun">:</span><span class="pln"> </span><span class="str">'the_mysql_user_name'</span><span class="pun">,</span><span class="pln">
  password</span><span class="pun">:</span><span class="pln"> </span><span class="str">'the_mysql_user_password'</span><span class="pun">,</span><span class="pln">
  database</span><span class="pun">:</span><span class="pln"> </span><span class="str">'the_mysql_database_name'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> connection </span><span class="pun">=</span><span class="pln"> mysql</span><span class="pun">.</span><span class="pln">createConnection</span><span class="pun">(</span><span class="pln">options</span><span class="pun">)</span></pre>

<p>
	ثم تهيئ اتصالًا جديدًا عن طريق استدعاء ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_98" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="pln">connect</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">'An error occurred while connecting to the DB'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> err
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	خيارات الاتصال
</h3>

<p>
	احتوى كائن <code>options</code> في المثال السابق على 3 خيارات هي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_100" style="">
<span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  user</span><span class="pun">:</span><span class="pln"> </span><span class="str">'the_mysql_user_name'</span><span class="pun">,</span><span class="pln">
  password</span><span class="pun">:</span><span class="pln"> </span><span class="str">'the_mysql_user_password'</span><span class="pun">,</span><span class="pln">
  database</span><span class="pun">:</span><span class="pln"> </span><span class="str">'the_mysql_database_name'</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هناك خيارات أخرى متعددة يمكنك استخدامها مثل:
</p>

<ul>
<li>
		<code>host</code>: اسم مضيف قاعدة البيانات، وقيمته الافتراضية هي <code>localhost</code>.
	</li>
	<li>
		<code>port</code> رقم منفذ خادم MySQL، وقيمته الافتراضية هي 3306.
	</li>
	<li>
		<code>socketPath</code>: يُستخدَم لتحديد مقبس يونيكس unix بدلًا من <code>host</code> و<code>port</code>.
	</li>
	<li>
		<code>debug</code>: يمكن استخدامه لتنقيح الأخطاء debugging عند تعطيله افتراضيًا.
	</li>
	<li>
		<code>trace</code>: يطبع تعقبات المكدس stack traces عند حدوث الأخطاء عند تفعيله افتراضيًا.
	</li>
	<li>
		<code><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">ssl</abbr></code>: يُستخدَم لإعداد اتصال <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B6%D8%A8%D8%B7-%D8%AA%D8%B4%D9%81%D9%8A%D8%B1-ssltls-%D9%84%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84%D8%A7%D8%AA-%D8%A5%D9%84%D9%89-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1604-r341/" rel=""><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></a> إلى الخادم.
	</li>
</ul>
<h3>
	إجراء استعلام SELECT
</h3>

<p>
	أصبحتَ الآن جاهزًا لإجراء استعلام <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a> في قاعدة البيانات، وسيستدعي الاستعلامُ بمجرد تنفيذه دالةَ رد النداء التي تحتوي على الخطأ المُحتمَل error والنتائج results والحقول fields كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1276_104" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="pln">query</span><span class="pun">(</span><span class="str">'SELECT * FROM todos'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> todos</span><span class="pun">,</span><span class="pln"> fields</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">'An error occurred while executing the query'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> error
  </span><span class="pun">}</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">})</span></pre>

<p>
	يمكنك تمرير القيم التي ستُتجاوز تلقائيًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1276_106" style="">
<span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="lit">223</span><span class="pln">
connection</span><span class="pun">.</span><span class="pln">query</span><span class="pun">(</span><span class="str">'SELECT * FROM todos WHERE id = ?'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">id</span><span class="pun">],</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> todos</span><span class="pun">,</span><span class="pln"> fields</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">'An error occurred while executing the query'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> error
  </span><span class="pun">}</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يمكنك تمرير قيم متعددة من خلال وضع مزيد من العناصر في المصفوفة التي تمررها على أساس معامل ثانٍ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1276_108" style="">
<span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="lit">223</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> author </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Flavio'</span><span class="pln">
connection</span><span class="pun">.</span><span class="pln">query</span><span class="pun">(</span><span class="str">'SELECT * FROM todos WHERE id = ? AND author = ?'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> author</span><span class="pun">],</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln">
todos</span><span class="pun">,</span><span class="pln"> fields</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">'An error occurred while executing the query'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> error
  </span><span class="pun">}</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">todos</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	إجراء استعلام INSERT
</h3>

<p>
	يمكنك تمرير كائن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1276_110" style="">
<span class="kwd">const</span><span class="pln"> todo </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  thing</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Buy the milk'</span><span class="pln">
  author</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Flavio'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
connection</span><span class="pun">.</span><span class="pln">query</span><span class="pun">(</span><span class="str">'INSERT INTO todos SET ?'</span><span class="pun">,</span><span class="pln"> todo</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> results</span><span class="pun">,</span><span class="pln"> fields</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">'An error occurred while executing the query'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> error
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	إذا احتوى الجدول على مفتاح رئيسي primary key مع <code>auto_increment</code>، فستُعاد قيمته ضمن القيمة <code>results.insertId</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1276_112" style="">
<span class="kwd">const</span><span class="pln"> todo </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  thing</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Buy the milk'</span><span class="pln">
  author</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Flavio'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
connection</span><span class="pun">.</span><span class="pln">query</span><span class="pun">(</span><span class="str">'INSERT INTO todos SET ?'</span><span class="pun">,</span><span class="pln"> todo</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span class="pln"> results</span><span class="pun">,</span><span class="pln"> fields</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">'An error occurred while executing the query'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> error
  </span><span class="pun">}}</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">resultId
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln">
</span><span class="pun">)</span></pre>

<h3>
	إغلاق الاتصال
</h3>

<p>
	يمكنك استدعاء التابع <code>end()‎</code> عند إنهاء الاتصال بقاعدة البيانات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_1276_116" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="kwd">end</span><span class="pun">()</span></pre>

<p>
	يعمل ذلك على التأكد من إرسال أي استعلام مُعلَّق وإنهاء الاتصال بأمان.
</p>

<h2>
	وحدات مخصصة
</h2>

<p>
	إذا لم تعثر على الوحدات المناسبة لك ضمن الوحدات الأساسية، فيمكنك بناء وحدة مخصَّصة تخدم غرضك بالاعتماد على الوحدات الأساسية ويمكنك أن تصدِّرها وتستوردها حتى أنه يمكنك بناء مكتبة كاملة، حيث سنتعرّف فيما يلي على كيفية استخدام واجهة module.exports البرمجية لتصدير بياناتك إلى ملفات أخرى في تطبيقك أو إلى تطبيقات أخرى.
</p>

<p>
	يمتلك نود نظام وحدات مبنيّ مسبقًا، إذ يمكن لملف Node.js استيراد العمليات التي تصدّرها <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-nodejs-r1469/" rel="">ملفات Node.js</a> الأخرى، فإذا أردت استيراد شيءٍ ما، فاستخدم ما يلي لاستيراد العمليات الظاهرة في ملف <code>library.js</code> الموجود في مجلد الملف الحالي، إذ يجب إظهار العمليات في هذا الملف قبل أن تستوردها ملفات أخرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_120" style="">
<span class="kwd">const</span><span class="pln"> library </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./library'</span><span class="pun">)</span></pre>

<p>
	يكون أيّ كائن أو متغير آخر مُعرَّف في الملف خاصًا private افتراضيًا ولا يظهر لأيّ شيء خارجي، وهذا ما تسمح لنا به واجهة برمجة تطبيقات <code>module.exports</code> التي يوفِّرها [نظام module<code>](https://nodejs.org/api/modules.html</code>)، وإذا أسندتَ كائنًا أو دالةً مثل خاصية <code>exports</code> جديدة، فهذا هو الشيء الذي يظهر، ويمكن استيراده على هذا النحو في أجزاء أخرى من تطبيقك أو في تطبيقات أخرى أيضًا، حيث يمكنك تطبيق ذلك بطريقتين، الأولى هي إسناد كائن لوحدة <code>module.exports</code>، وهو كائن خارجي يوفّره نظام module، وبالتالي سيصدّر ملفك هذا الكائن فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_122" style="">
<span class="kwd">const</span><span class="pln"> car </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  brand</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ford'</span><span class="pun">,</span><span class="pln">
  model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Fiesta'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> car

</span><span class="com">//في الملف الآخر‫..</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> car </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./car'</span><span class="pun">)</span></pre>

<p>
	أما الطريقة الثانية فهي إضافة الكائن المُصدَّر على أساس خاصية <code>exports</code>، حيث تتيح لك هذه الطريقة تصدير كائنات أو دوال أو بيانات متعددة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_124" style="">
<span class="kwd">const</span><span class="pln"> car </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  brand</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ford'</span><span class="pun">,</span><span class="pln">
  model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Fiesta'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">car </span><span class="pun">=</span><span class="pln"> car</span></pre>

<p>
	أو مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_126" style="">
<span class="kwd">const</span><span class="pln"> car </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  brand</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ford'</span><span class="pun">,</span><span class="pln">
  model</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Fiesta'</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما ستستخدمه في الملف الآخر من خلال الإشارة إلى خاصية الاستيراد كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_128" style="">
<span class="kwd">const</span><span class="pln"> items </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./items'</span><span class="pun">)</span><span class="pln">
items</span><span class="pun">.</span><span class="pln">car</span></pre>

<p>
	أو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1276_130" style="">
<span class="kwd">const</span><span class="pln"> car </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./items'</span><span class="pun">).</span><span class="pln">car</span></pre>

<p>
	هناك فرق بين <code>module.exports</code> و<code>exports</code>، فالأول يُظهِر الكائن الذي يؤشّر إليه، بينما يُظهِر الثاني خاصيات الكائن الذي يؤشّر إليه.
</p>

<p>
	ترجمة -وبتصرّف- للفصل Some essential core modules من كتاب The Node.js handbook لصاحبه Flavio Copes.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-nodejs-r1469/" rel="">التعامل مع الملفات في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-r1333/" rel="">البرمجة باستخدام الوحدات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r926/" rel="">مقدمة إلى الوحدات Modules في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1470</guid><pubDate>Tue, 22 Mar 2022 16:02:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x644;&#x641;&#x627;&#x62A; &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-nodejs-r1469/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/62038ef3b2089_---.png.48bfb683d55568c76551b3680c383346.png" /></p>

<p>
	سنتعرّف من خلال هذا المقال على كيفية التعامل مع الملفات والمجلدات في <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a> من خلال شرح واصفات الملفات وإحصائياتها ومساراتها وقراءتها وكتابتها، كما سنتعرّف على مفهوم المجاري streams وميّزاتها وأنواعها.
</p>

<h2>
	واصفات الملفات File descriptors
</h2>

<p>
	يمكن التفاعل مع واصفات الملفات باستخدام نود Node، إذ يجب أن تحصل على واصف ملف قبل تمكنك من التفاعل مع ملف موجود في نظام الملفات الخاص بك، وواصف الملف هو ما يُعاد عند فتح الملف باستخدام التابع <code>open()‎</code> الذي توفره وحدة <a href="https://wiki.hsoub.com/Node.js/fs" rel="external"><code>fs</code></a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_8" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'r'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> fd</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//‫fd هو واصف الملف</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	استخدمنا الراية <code>r</code> على أساس معامِل ثانٍ لاستدعاء <code>fs.open()‎</code>، إذ تعني هذه الراية أننا نفتح الملف للقراءة؛ أما الرايات الأخرى المُستخدَمة فهي:
</p>

<ul>
<li>
		<code>r+‎</code> فتح الملف للقراءة والكتابة.
	</li>
	<li>
		<code>w+‎</code> فتح الملف للقراءة والكتابة، مع وضع المجرى stream في بداية الملف وإنشاء الملف إذا لم يكن موجودًا مسبقًا.
	</li>
	<li>
		<code>a</code> فتح الملف للكتابة، مع وضع المجرى في نهاية الملف وإنشاء الملف إن لم يكن موجودًا مسبقًا.
	</li>
	<li>
		<code>a+‎</code> فتح الملف للقراءة والكتابة، مع وضع المجرى في نهاية الملف وإنشاء الملف إن لم يكن موجودًا مسبقًا.
	</li>
</ul>
<p>
	يمكنك فتح الملف باستخدام التابع <code>fs.openSync</code> الذي يعيد كائن واصف الملف بدلًا من توفيره في دالة رد نداء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_10" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> fd </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">openSync</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'r'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك تنفيذ جميع العمليات المطلوبة مثل استدعاء التابع <code>fs.open()‎</code> والعديد من العمليات الأخرى التي تتفاعل مع نظام الملفات بمجرد حصولك على واصف الملف بأيّ طريقة تختارها.
</p>

<h2>
	إحصائيات الملف
</h2>

<p>
	يأتي كل ملف مع مجموعة من التفاصيل التي يمكننا فحصها باستخدام <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">نود Node </a>باستخدام التابع <code>stat()‎</code> الذي توفِّره وحدة <code>fs</code>، حيث يمكنك استدعاؤه مع تمرير مسار ملف إليه، حيث سيستدعي نود بعد حصوله على تفاصيل الملف دالة رد النداء التي تمررها مع معاملين هما رسالة خطأ وإحصائيات الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_12" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">
fs</span><span class="pun">.</span><span class="pln">stat</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> stats</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="com">//يمكننا الوصول إلى إحصائيات الملف في‫ `stats`</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	كما يوفِّر نود تابعًا متزامنًا يوقِف الخيط thread إلى أن تصبح إحصائيات الملف جاهزةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_15" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> stats </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">stat</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُضمَّن معلومات الملف في المتغير <code>stats</code>، ويمكننا استخراج أنواع معلومات متعددة باستخدام توابع stats كما يلي:
</p>

<ul>
<li>
		استخدم التابع <code>stats.isFile()‎</code> والتابع <code>stats.isDirectory()‎</code> لمعرفة إذا كان الملف عبارة عن مجلد أو ملف.
	</li>
	<li>
		استخدم التابع <code>stats.isSymbolicLink()‎</code> لمعرفة إذا كان الملف وصلةً رمزيةً symbolic link.
	</li>
	<li>
		استخدم التابع <code>stats.size</code> لمعرفة حجم الملف مقدَّرًا بالبايت.
	</li>
</ul>
<p>
	هناك توابع متقدمة أخرى، ولكن الجزء الأكبر مما ستستخدمه هو التوابع السابقة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_17" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">
fs</span><span class="pun">.</span><span class="pln">stat</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> stats</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  stats</span><span class="pun">.</span><span class="pln">isFile</span><span class="pun">()</span><span class="pln"> </span><span class="com">//true</span><span class="pln">
  stats</span><span class="pun">.</span><span class="pln">isDirectory</span><span class="pun">()</span><span class="pln"> </span><span class="com">//false</span><span class="pln">
  stats</span><span class="pun">.</span><span class="pln">isSymbolicLink</span><span class="pun">()</span><span class="pln"> </span><span class="com">//false</span><span class="pln">
  stats</span><span class="pun">.</span><span class="pln">size </span><span class="com">//1024000 //= 1MB</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2>
	مسارات الملفات
</h2>

<p>
	سنتعرّف على كيفية التفاعل مع مسارات الملفات والتعامل معها في نود Node، فلكل ملف في النظام مسار، وقد يبدو المسار في نظامَي <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس Linux</a> و<a href="https://academy.hsoub.com/apps/operating-systems/macos/" rel="">macOS</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_21" style="">
<span class="str">/users/</span><span class="pln">flavio</span><span class="pun">/</span><span class="pln">file</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	بينما الحواسيب التي تعمل بنظام ويندوز Windows مختلفة، إذ يكون للمسار بنية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_23" style="">
<span class="pln">C</span><span class="pun">:</span><span class="pln">\users\flavio\file</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	يجب الانتباه عند استخدام المسارات في تطبيقاتك، إذ يجب مراعاة هذا الاختلاف، كما يمكنك تضمين وحدة المسار في ملفاتك كما ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_26" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">)</span></pre>

<p>
	ثم يمكنك البدء في استخدام توابعها، كما يمكنك استخراج معلومات من مسار باستخدام التوابع التالية:
</p>

<ul>
<li>
		<code>dirname</code>: للحصول على مجلد الملف الأب.
	</li>
	<li>
		<code>basename</code>: للحصول على جزء اسم الملف.
	</li>
	<li>
		<code>extname</code>: للحصول على امتداد الملف.
	</li>
</ul>
<p>
	إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_29" style="">
<span class="kwd">const</span><span class="pln"> notes </span><span class="pun">=</span><span class="pln"> </span><span class="str">'/users/flavio/notes.txt'</span><span class="pln">
path</span><span class="pun">.</span><span class="pln">dirname</span><span class="pun">(</span><span class="pln">notes</span><span class="pun">)</span><span class="pln"> </span><span class="com">// /users/flavio</span><span class="pln">
path</span><span class="pun">.</span><span class="pln">basename</span><span class="pun">(</span><span class="pln">notes</span><span class="pun">)</span><span class="pln"> </span><span class="com">// notes.txt</span><span class="pln">
path</span><span class="pun">.</span><span class="pln">extname</span><span class="pun">(</span><span class="pln">notes</span><span class="pun">)</span><span class="pln"> </span><span class="com">// .txt</span></pre>

<p>
	يمكنك الحصول على اسم الملف بدون امتداده عن طريق تحديد وسيط ثانٍ للتابع <code>basename</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_31" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">basename</span><span class="pun">(</span><span class="pln">notes</span><span class="pun">,</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">extname</span><span class="pun">(</span><span class="pln">notes</span><span class="pun">))</span><span class="pln"> </span><span class="com">//notes</span></pre>

<p>
	كما يمكنك ربط جزأين أو أكثر من المسار مع بعضها البعض باستخدام التابع <code>path.join()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_33" style="">
<span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'flavio'</span><span class="pln">
path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'users'</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="str">'notes.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//'/users/flavio/notes.txt'</span></pre>

<p>
	يمكنك حساب مسار الملف المطلق absolute path من مساره النسبي relative path باستخدام التابع <code>path.resolve()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_36" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="str">'flavio.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">//'/Users/flavio/flavio.txt' if run from my home folder</span></pre>

<p>
	سيُلحِق في هذه الحالة نود Node ببساطة المسارَ النسبي <code>‎/flavio.txt</code> بدليل أو مجلد العمل الحالي، فإذا حددت مجلدًا على أساس معامل آخر، فسيستخدِم تابع <code>resolve</code> المعامل الأول أساسًا للمعامل الثاني كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_38" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="str">'tmp'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'flavio.txt'</span><span class="pun">)</span><span class="com">//‫'‎/Users/flavio/tmp/flavio.txt' إذا شُغِّل من المجلد المحلي</span></pre>

<p>
	إذا بدأ المعامل الأول بشرطة مائلة، فهذا يعني أنه مسار مطلق مثل المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_40" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="str">'/etc'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'flavio.txt'</span><span class="pun">)</span><span class="com">//'/etc/flavio.txt'</span></pre>

<p>
	يُعَدّ <code>path.normalize()‎</code> تابعًا آخرًا مفيدًا يحسب المسار الفعلي عندما يحتوي على محددات نسبية مثل <code>.</code> أو <code>..</code> أو شرطة مائلة مزدوجة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_42" style="">
<span class="pln">path</span><span class="pun">.</span><span class="pln">normalize</span><span class="pun">(</span><span class="str">'/users/flavio/..//test.txt'</span><span class="pun">)</span><span class="pln"> </span><span class="com">///users/test.txt</span></pre>

<p>
	لن يتحقق التابعان <code>resolve</code> و<code>normalize</code> من وجود المسار، وإنما يحسبان المسار فقط بناءً على المعلومات المتاحة.
</p>

<h2>
	قراءة الملفات
</h2>

<p>
	أبسط طريقة لقراءة ملف في نود هي استخدام تابع <code>fs.readFile()‎</code>، حيث نمرِّر له مسار الملف ودالة رد النداء التي ستُستدعَى مع بيانات الملف ومع الخطأ كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_44" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يمكنك بدلًا من ذلك استخدام الإصدار المتزامن من التابع السابق وهو التابع <code>fs.readFileSync()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_46" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">readFileSync</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الترميز الافتراضي هو utf8، ولكن يمكنك تحديد ترميز مُخصَّص باستخدام معامل ثانٍ، كما يقرأ كل من التابعَين <code>fs.readFile()‎</code> و<code>fs.readFileSync()‎</code> محتوى الملف الكامل في الذاكرة قبل إعادة البيانات، وهذا يعني أن الملفات الكبيرة سيكون لها تأثير كبير على استهلاك الذاكرة وسرعة تنفيذ البرنامج، وبالتالي يكون الخيار الأفضل في هذه الحالة هو قراءة محتوى الملف باستخدام المجاري streams.
</p>

<h2>
	كتابة الملفات
</h2>

<p>
	أسهل طريقة للكتابة في الملفات في Node.js هي استخدام واجهة برمجة تطبيقات <code>fs.writeFile()‎</code>، وإليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_48" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> content </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Some content!'</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">//كُتِب الملف بنجاح</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يمكنك بدلًا من ذلك استخدام الإصدار المتزامن وهو <code>fs.writeFileSync()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_50" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> content </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Some content!'</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">writeFileSync</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">)</span><span class="pln">
  </span><span class="com">//كُتِب الملف بنجاح</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستبدّل واجهة برمجة التطبيقات هذه افتراضيًا محتويات الملف إذا كان موجودًا مسبقًا، ولكن يمكنك تعديل الإعداد الافتراضي عن طريق تحديد راية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_52" style="">
<span class="pln">fs</span><span class="pun">.</span><span class="pln">writeFile</span><span class="pun">(</span><span class="str">'/Users/flavio/test.txt'</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> flag</span><span class="pun">:</span><span class="pln"> </span><span class="str">'a+'</span><span class="pln"> </span><span class="pun">},</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{})</span></pre>

<p>
	الرايات التي يمكنك استخدامها هي:
</p>

<ul>
<li>
		<code>r+‎</code> لفتح الملف للقراءة والكتابة.
	</li>
	<li>
		<code>w+‎</code> لفتح الملف للقراءة والكتابة مع وضع المجرى في بداية الملف وإنشاء الملف إذا لم يكن موجودًا مسبقًا.
	</li>
	<li>
		<code>a</code> لفتح الملف للكتابة مع وضع المجرى في نهاية الملف وإنشاء الملف إذا لم يكن موجودًا مسبقًا.
	</li>
	<li>
		<code>a+‎</code> لفتح الملف للقراءة والكتابة، مع وضع المجرى في نهاية الملف، وإنشاء الملف إن لم يكن موجودًا مسبقًا.
	</li>
</ul>
<p>
	يمكنك العثور على المزيد من الرايات على <a href="https://wiki.hsoub.com/Node.js/fs" rel="external">/nodejs</a>.
</p>

<h2>
	إلحاق محتوى بملف
</h2>

<p>
	يمكنك إلحاق محتوى بنهاية الملف من خلال استخدام التابع <code>fs.appendFile()‎</code> ونسخته المتزامنة التابع <code>fs.appendFileSync()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_54" style="">
<span class="kwd">const</span><span class="pln"> content </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Some content!'</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">appendFile</span><span class="pun">(</span><span class="str">'file.log'</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">//done!</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2>
	استخدام المجاري streams
</h2>

<p>
	تكتب كل التوابع السابقة المحتوى الكامل في الملف قبل إعادة التحكم إلى برنامجك مرةً أخرى، أي تنفيذ دالة رد النداء في النسخة غير المتزامنة، وبالتالي الخيار الأفضل هو كتابة محتوى الملف باستخدام المجاري streams.
</p>

<p>
	لنتعرّف على الغرض الأساسي من المجاري streams وسبب أهميتها وكيفية استخدامها، حيث سنقدِّم مدخلًا بسيطًا إلى المجاري، ولكن هناك جوانب أكثر تعقيدًا لتحليلها.
</p>

<h3>
	مفهوم المجاري streams
</h3>

<p>
	تُعَدّ المجاري أحد المفاهيم الأساسية التي تعمل على تشغيل تطبيقات Node.js، وهي طريقة للتعامل مع ملفات القراءة/الكتابة أو اتصالات الشبكة أو أيّ نوع من تبادل المعلومات من طرف إلى طرف بطريقة فعالة، كما ليست المجاري مفهومًا خاصًا بنود Node.js، إذ توفّرت في <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a> يونيكس Unix منذ عقود، ويمكن للبرامج أن تتفاعل مع بعضها البعض عبر تمرير المجاري من خلال معامِل الشريط العمودي أو الأنبوب pipe operator (<code>|</code>).
</p>

<p>
	يُقرأ الملف في الذاكرة من البداية إلى النهاية ثم تعالجه، عندما تطلب من البرنامج قراءة ملف بالطريقة التقليدية على سبيل المثال، لكن يمكنك قراءة الملف قطعةً تلو الأخرى باستخدام المجاري، ومعالجة محتواه دون الاحتفاظ به بالكامل في الذاكرة، إذ توفِّر <a href="https://wiki.hsoub.com/Node.js/stream" rel="external">وحدة نود <code>stream</code></a> الأساس الذي يُبنَى عليه جميع واجهات برمجة التطبيقات ذات المجرى، كما توفِّر المجاري ميزتَين رئيسيتَين باستخدام طرق معالجة البيانات الأخرى هما:
</p>

<ul>
<li>
		<strong>فعالية الذاكرة Memory efficiency</strong>: لست بحاجة إلى تحميل كميات كبيرة من البيانات في الذاكرة قبل أن تكون قادرًا على معالجتها.
	</li>
	<li>
		<strong>فعالية الوقت Time efficiency</strong>: تستغرق وقتًا أقل لبدء معالجة البيانات بمجرد حصولك عليها، بدلًا من انتظار اكتمال حمولة البيانات للبدء.
	</li>
</ul>
<p>
	يوضِّح المثال التالي قراءة ملفات من القرص الصلب، حيث يمكنك باستخدام وحدة نود <code>fs</code> قراءة ملف وتقديمه عبر <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">بروتوكول HTTP </a>عند إنشاء اتصال جديد بخادم http:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_59" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  fs</span><span class="pun">.</span><span class="pln">readFile</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/data.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="lit">3000</span><span class="pun">)</span></pre>

<p>
	يقرأ التابع <code>readFile()‎</code> محتويات الملف الكاملة، ويستدعي دالة رد النداء callback function عند الانتهاء، بينما سيعيد التابع <code>res.end(data)‎</code> في دالة رد النداء محتويات الملف إلى عميل HTTP، فإذا كان الملف كبيرًا، فستستغرق العملية وقتًا طويلًا، ويمكن تطبيق الأمر نفسه باسخدام المجاري streams كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_61" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">((</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> stream </span><span class="pun">=</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">createReadStream</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/data.txt'</span><span class="pun">)</span><span class="pln">
  stream</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="lit">3000</span><span class="pun">)</span></pre>

<p>
	يمكننا بث الملف عبر المجاري إلى عميل HTTP بمجرد أن يكون لدينا مجموعة كبيرة من البيانات جاهزة للإرسال بدلًا من انتظار قراءة الملف بالكامل؛ ويستخدم المثال السابق <code>stream.pipe(res)‎</code>، أي استدعاء تابع <code>pipe()‎</code> في مجرى الملف، حيث يأخذ هذا التابع المصدر، ويضخّه إلى وجهة معينة، كما يُستدعَى هذا التابع على مجرى المصدر، وبالتالي يُضَخ مجرى الملف إلى استجابة HTTP في هذه الحالة، وتكون القيمة المُعادة من التابع <code>pipe()‎</code> هي مجرى الوجهة، وهذا أمر ملائم للغاية لربط استدعاءات <code>pipe()‎</code> متعددة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_63" style="">
<span class="pln">src</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">dest1</span><span class="pun">).</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">dest2</span><span class="pun">)</span></pre>

<p>
	الذي يكافئ ما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1185_65" style="">
<span class="pln">src.pipe(dest1)
dest1.pipe(dest2)</span></pre>

<p>
	تتوفَّر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهات برمجة تطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> الخاصة بنود Node التي تعمل باستخدام المجاري Streams، إذ توفِّر العديد من وحدات Node.js الأساسية إمكانات معالجة المجرى الأصيلة، ومن أبرزها:
</p>

<ul>
<li>
		<code>process.stdin</code> التي تعيد مجرًى متصلًا بمجرى stdin.
	</li>
	<li>
		<code>process.stdout</code> التي تعيد مجرًى متصلًا بمجرى stdout.
	</li>
	<li>
		<code>process.stderr</code> التي تعيد مجرًى متصلًا بمجرى stderr.
	</li>
	<li>
		<code>fs.createReadStream()‎</code> الذي ينشئ مجرًى قابلًا للقراءة إلى ملف.
	</li>
	<li>
		<code>fs.createWriteStream()‎</code> الذي ينشئ مجرًى قابلًا للكتابة إلى ملف.
	</li>
	<li>
		<code>net.connect()‎</code> الذي يبدأ اتصالًا قائمًا على مجرى.
	</li>
	<li>
		<code>http.request()‎</code> الذي يعيد نسخة من الصنف <code>http.ClientRequest</code>، وهو مجرى قابل للكتابة.
	</li>
	<li>
		<code>zlib.createGzip()‎</code> الذي يضغط البيانات باستخدام خوارزمية الضغط gzip في مجرى.
	</li>
	<li>
		<code>zlib.createGunzip()‎</code> الذي يفك ضغط مجرى gzip.
	</li>
	<li>
		<code>zlib.createDeflate()‎</code> الذي يضغط البيانات باستخدام خوارزمية الضغط deflate في مجرى.
	</li>
	<li>
		<code>zlib.createInflate()‎</code> الذي يفك ضغط مجرى deflate.
	</li>
</ul>
<h3>
	أنواع المجاري المختلفة
</h3>

<p>
	هناك أربع أصناف من المجاري هي:
</p>

<ul>
<li>
		<code>Readable</code>: هو مجرى يمكن الضخ pipe منه ولكن لا يمكن الضخ إليه، أي يمكنك تلقي البيانات منه ولكن لا يمكنك إرسال البيانات إليه، فإذا دفعتَ بيانات إلى مجرى قابل للقراءة، فستُخزَّن مؤقتًا حتى يبدأ المستهلك في قراءة البيانات.
	</li>
	<li>
		<code>Writable</code>: هو مجرى يمكن الضخ إليه، ولكن لا يمكن الضخ منه، أي يمكنك إرسال البيانات إليه، ولكن لا يمكنك تلقي البيانات منه.
	</li>
	<li>
		<code>Duplex</code>: هو مجرى يمكن الضخ منه وإليه، أي هو مزيج من مجرى <code>Readable</code> ومجرى <code>Writable</code>.
	</li>
	<li>
		<code>Transform</code>: مجرى التحويل مشابه للمجرى Duplex، ولكن خرجه هو تحويل لدخله.
	</li>
</ul>
<h3>
	كيفية إنشاء مجرى قابل للقراءة
</h3>

<p>
	يمكن الحصول على مجرى قابل للقراءة من وحدة <code>stream</code>، كما يمكن تهيئته كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_68" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Stream</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'stream'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> readableStream </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stream</span><span class="pun">.</span><span class="typ">Readable</span><span class="pun">()</span></pre>

<p>
	ثم يمكننا إرسال البيانات إليه بعد تهيئته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_71" style="">
<span class="pln">readableStream</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'hi!'</span><span class="pun">)</span><span class="pln">
readableStream</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'ho!'</span><span class="pun">)</span></pre>

<h3>
	كيفية إنشاء مجرى قابل للكتابة
</h3>

<p>
	يمكنك إنشاء مجرى قابل للكتابة من خلال وراثة كائن <code>Writable</code> الأساسي وتطبيق تابعه <code>‎_write()‎</code>.
</p>

<p>
	أنشئ أولًا كائن <code>Stream</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_73" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Stream</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'stream'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> writableStream </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stream</span><span class="pun">.</span><span class="typ">Writable</span><span class="pun">()</span></pre>

<p>
	ثم التابع <code>‎_write</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_75" style="">
<span class="pln">writableStream</span><span class="pun">.</span><span class="pln">_write </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">,</span><span class="pln"> encoding</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">())</span><span class="pln">
  next</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك الآن الضخ إلى مجرى قابل للقراءة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_77" style="">
<span class="pln">process</span><span class="pun">.</span><span class="pln">stdin</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">writableStream</span><span class="pun">)</span></pre>

<h3>
	كيفية الحصول على بيانات من مجرى قابل للقراءة
</h3>

<p>
	يمكنك قراءة البيانات من مجرًى قابل للقراءة باستخدام مجرى قابل للكتابة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_79" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Stream</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'stream'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> readableStream </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stream</span><span class="pun">.</span><span class="typ">Readable</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> writableStream </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stream</span><span class="pun">.</span><span class="typ">Writable</span><span class="pun">()</span><span class="pln">

writableStream</span><span class="pun">.</span><span class="pln">_write </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">,</span><span class="pln"> encoding</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">())</span><span class="pln">
  next</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

readableStream</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">writableStream</span><span class="pun">)</span><span class="pln">

readableStream</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'hi!'</span><span class="pun">)</span><span class="pln">
readableStream</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'ho!'</span><span class="pun">)</span></pre>

<p>
	كما يمكنك استهلاك مجرى قابل للقراءة مباشرةً باستخدام الحدث <code>readable</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_81" style="">
<span class="pln">readableStream</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'readable'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">readableStream</span><span class="pun">.</span><span class="pln">read</span><span class="pun">())</span><span class="pln">
</span><span class="pun">})</span></pre>

<h3>
	كيفية إرسال بيانات إلى مجرى قابل للكتابة
</h3>

<p>
	استخدم تابع المجرى <code>write()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_83" style="">
<span class="pln">writableStream</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'hey!\n'</span><span class="pun">)</span></pre>

<h3>
	إعلام مجرى قابل للكتابة بانتهاء الكتابة
</h3>

<p>
	استخدم التابع <code>end()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_85" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Stream</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'stream'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> readableStream </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stream</span><span class="pun">.</span><span class="typ">Readable</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> writableStream </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Stream</span><span class="pun">.</span><span class="typ">Writable</span><span class="pun">()</span><span class="pln">

writableStream</span><span class="pun">.</span><span class="pln">_write </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">,</span><span class="pln"> encoding</span><span class="pun">,</span><span class="pln"> next</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">())</span><span class="pln">
  next</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

readableStream</span><span class="pun">.</span><span class="pln">pipe</span><span class="pun">(</span><span class="pln">writableStream</span><span class="pun">)</span><span class="pln">
readableStream</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'hi!'</span><span class="pun">)</span><span class="pln">
readableStream</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'ho!'</span><span class="pun">)</span><span class="pln">

writableStream</span><span class="pun">.</span><span class="pln">end</span><span class="pun">()</span></pre>

<h2>
	التعامل مع المجلدات
</h2>

<p>
	توفِّر وحدة Node.js الأساسية <code>fs</code> توابعًا متعددةً مفيدةً يمكنك استخدامها للتعامل مع المجلدات.
</p>

<h3>
	التحقق من وجود مجلد
</h3>

<p>
	يُستخدَم التابع <code>fs.access()‎</code> للتحقق مما إذا كان المجلد موجودًا، ويمكن لنود الوصول إلى المجلد باستخدام أذوناته.
</p>

<h3>
	إنشاء مجلد جديد
</h3>

<p>
	يُستخدَم التابع <code>fs.mkdir()‎</code> أو التابع <code>fs.mkdirSync()‎</code> لإنشاء مجلد جديد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_87" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> folderName </span><span class="pun">=</span><span class="pln"> </span><span class="str">'/Users/flavio/test'</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">fs</span><span class="pun">.</span><span class="pln">existsSync</span><span class="pun">(</span><span class="pln">dir</span><span class="pun">)){</span><span class="pln">
    fs</span><span class="pun">.</span><span class="pln">mkdirSync</span><span class="pun">(</span><span class="pln">dir</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	قراءة محتوى مجلد
</h3>

<p>
	يُستخدَم التابع <code>fs.readdir()‎</code> أو التابع <code>fs.readdirSync</code> لقراءة محتويات مجلد، ويقرأ جزء الشيفرة التالية محتوى مجلد من ملفات ومجلدات فرعية، ويعيد مساراتها النسبية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_89" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> folderPath </span><span class="pun">=</span><span class="pln"> </span><span class="str">'/Users/flavio'</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">readdirSync</span><span class="pun">(</span><span class="pln">folderPath</span><span class="pun">)</span></pre>

<p>
	يمكنك الحصول على المسار الكامل من خلال ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_91" style="">
<span class="pln">fs</span><span class="pun">.</span><span class="pln">readdirSync</span><span class="pun">(</span><span class="pln">folderPath</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">fileName </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">folderPath</span><span class="pun">,</span><span class="pln"> fileName</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما يمكنك تصفية النتائج لإعادة الملفات فقط واستبعاد المجلدات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_95" style="">
<span class="kwd">const</span><span class="pln"> isFile </span><span class="pun">=</span><span class="pln"> fileName </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> fs</span><span class="pun">.</span><span class="pln">lstatSync</span><span class="pun">(</span><span class="pln">fileName</span><span class="pun">).</span><span class="pln">isFile</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">readdirSync</span><span class="pun">(</span><span class="pln">folderPath</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">fileName </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">folderPath</span><span class="pun">,</span><span class="pln"> fileName</span><span class="pun">)).</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">isFile</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إعادة تسمية مجلد
</h3>

<p>
	يُستخدَم التابع <code>fs.rename()‎</code> أو التابع <code>fs.renameSync()‎</code> لإعادة تسمية مجلد، حيث يكون المعامِل الأول هو المسار الحالي، والمعامِل الثاني هو المسار الجديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_97" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">rename</span><span class="pun">(</span><span class="str">'/Users/flavio'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'/Users/roger'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">//done</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	كما يمكنك استخدام التابع <code>fs.renameSync()‎</code> الذي هو النسخة المتزامنة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_99" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  fs</span><span class="pun">.</span><span class="pln">renameSync</span><span class="pun">(</span><span class="str">'/Users/flavio'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'/Users/roger'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إزالة مجلد
</h3>

<p>
	يُستخدَم التابع <code>fs.rmdir()‎</code> أو التابع <code>fs.rmdirSync()‎</code> لإزالة مجلد، ويمكن أن تكون إزالة مجلد أكثر تعقيدًا إذا تضمّن محتوىً، لذلك نوصي في هذه الحالة بتثبيت وحدة <code>fs-extra</code> التي تحظى بشعبية ودعم كبيرَين، وهي بديل سريع لوحدة <code>fs</code>، وبالتالي تضيف مزيدًا من الميزات عليها، كما ستحتاج استخدام التابع <code>remove()‎</code>.
</p>

<p>
	ثبّت وحدة <code>fs-extra</code> باستخدام الأمر: <code>npm install fs-extra</code>، واستخدمها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_101" style="">
<span class="kwd">const</span><span class="pln"> fs </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'fs-extra'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> folder </span><span class="pun">=</span><span class="pln"> </span><span class="str">'/Users/flavio'</span><span class="pln">

fs</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="pln">folder</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	كما يمكن استخدامها مع الوعود promises كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_103" style="">
<span class="pln">fs</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="pln">folder</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//done</span><span class="pln">
</span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	أو مع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/" rel="">صيغة async/await</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1185_105" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> removeFolder</span><span class="pun">(</span><span class="pln">folder</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    await fs</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="pln">folder</span><span class="pun">)</span><span class="pln">
    </span><span class="com">//done</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> folder </span><span class="pun">=</span><span class="pln"> </span><span class="str">'/Users/flavio'</span><span class="pln">
  removeFolder</span><span class="pun">(</span><span class="pln">folder</span><span class="pun">)</span></pre>

<p>
	للمزيد، يمكنك الرجوع إلى توثيق <a href="https://wiki.hsoub.com/Node.js/fs" rel="external">التعامل مع نظام الملفات في Node.js</a> في موسوعة حسوب.
</p>

<p>
	ترجمة -وبتصرّف- للفصل File System من كتاب The Node.js handbook لصاحبه Flavio Copes.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">تعرف على وحدات Node.js الأساسية</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-r1468/" rel="">التعامل مع الطلبيات الشبكية في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات في البرمجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1459/" rel="">مدخل إلى التعامل مع الملفات في جافا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r484/" rel="">كيفية التعامل مع الملفات النصية في بايثون 3</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c-sharp/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%BA%D8%A9-%D8%B3%D9%8A-%D8%B4%D8%A7%D8%B1%D8%A8-c-r358/" rel="">التعامل مع الملفات النصية في لغة سي شارب #C</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1469</guid><pubDate>Wed, 09 Feb 2022 11:41:05 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x637;&#x644;&#x628;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x634;&#x628;&#x643;&#x64A;&#x629; &#x641;&#x64A; Node.js</title><link>https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D9%8A%D8%A9-%D9%81%D9%8A-nodejs-r1468/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_02/6203824fc8e5f_----.png.a5ccdb671641f22e56a56e7d364040fe.png" /></p>

<p>
	سنتعرّف من خلال هذا المقال على طريقة إرسال واستقبال الطلبيات بين الخادم والعميل عبر الشبكة في <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a> باستخدام مكتبة Axios، ولكن سنبدأ بشرح وسيلة التواصل الأساسية بين الخادم والمتصفح وهو <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">بروتوكول HTTP</a>، وسنتعرّف على بديل اتصال HTTP في تطبيقات الويب الذي هو مقابس الويب WebSockets.
</p>

<h2>
	كيفية عمل بروتوكول HTTP
</h2>

<p>
	يُعَدّ بروتوكول نقل النص الفائق Hyper Text Transfer Protocol -أو HTTP اختصارًا- أحد بروتوكولات تطبيق <a href="https://academy.hsoub.com/devops/servers/%d8%aa%d8%b9%d8%b1%d9%81-%d8%b9%d9%84%d9%89-%d8%a8%d8%b1%d9%88%d8%aa%d9%88%d9%83%d9%88%d9%84-tcpip-%d9%88%d8%a8%d8%b9%d8%b6-%d9%85%d9%86-%d8%ae%d8%af%d9%85%d8%a7%d8%aa%d9%87-r169/" rel="">TCP/IP</a> وهي مجموعة البروتوكولات التي تشغّل شبكة الإنترنت، إذ يُعَدّ البروتوكول الأنجح والأكثر شعبية على الإطلاق، كما يُشغِّل هذا البروتوكول شبكة الويب العالمية World Wide Web، مما يمنح المتصفحات لغة للتواصل مع <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r574/" rel="">الخوادم </a>البعيدة التي تستضيف صفحات الويب.
</p>

<p>
	وُحِّد بروتوكول HTTP لأول مرة في عام 1991على أساس نتيجة لعمل تيم بيرنرز لي Tim Berners-Lee في المركز الأوروبي للأبحاث النووية European Center of Nuclear Research -أو CERN اختصارًا- منذ عام 1989، وكان الهدف هو السماح للباحثين بتبادل أبحاثهم بسهولة وربطهم ببعضهم بعضًا على أساس وسيلة تحسِّن عمل المجتمع العلمي، كما تكوّنت تطبيقات الإنترنت الرئيسية في ذلك الوقت من بروتوكول FTP أي بروتوكول نقل الملفات File Transfer Protocol والبريد الإلكتروني ونظام يوزنت Usenet أي مجموعات الأخبار newsgroups، ولكنها أصبحت غير مُستخدَمة حاليًا تقريبًا.
</p>

<p>
	صدر متصفح موزاييك Mosaic في عام 1993، وهو أول متصفح ويب رسومي، وتطورت الأمور عندها، إذ أصبح الويب التطبيق القاتل في شبكة الإنترنت، حيث سبّب ظهوره ضجةً كبيرةً، كما تطوّر الويب والنظام المجتمعي المحيط به تطوّرًا كبيرًا بمرور الوقت مع بقاء الأساسيات على حالها، وأحد الأمثلة على هذا التطور هو أنّ بروتوكول HTTP يشغّل حاليًا -بالإضافة إلى صفحات الويب- <a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%81%D9%84%D8%B3%D9%81%D9%84%D8%A9-restful-%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-rest-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r635/" rel="">واجهات برمجة تطبيقات REST</a>، وهي إحدى الطرق الشائعة للوصول إلى خدمة عبر الإنترنت برمجيًا.
</p>

<p>
	عُدِّل بروتوكول HTTP تعديلًا ثانويًا في عام 1997 في الإصدار HTTP/1.1، وخلفه الإصدار HTTP/2 الذي وُحِّد في عام 2015 ويُطبَّق الآن على خوادم الويب الرئيسية المُستخدَمة في جميع أنحاء العالم، كما يُعَدّ بروتوكول HTTP غير آمن مثل أيّ بروتوكول آخر غير مخدَّم عبر اتصال مشفَّر مثل بروتوكولات SMTP وFTP وغيرها، وهذا هو السبب في التوجّه الكبير حاليًا نحو استخدام بروتوكول HTTPS، وهو بروتوكول HTTP مخدَّم عبر بروتوكول <abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr>، ولكن بروتوكول HTTP هو حجر الأساس لبروتوكول HTTP/2 وHTTPS.
</p>

<h3>
	مستندات HTML
</h3>

<p>
	بروتوكول HTTP هو الطريقة التي تتواصل بها متصفحات الويب web browsers مثل Chrome وFirefox وEdge ومتصفحات أخرى سنسمّيها عملاء clients مع خوادم الويب web servers، كما اُشتق الاسم بروتوكول نقل النص الفائق Hyper Text Transfer Protocol من الحاجة إلى نقل الملفات كما هو الحال في بروتوكول FTP والذي يشير إلى بروتوكول نقل الملفات File Transfer Protocol، بالإضافة إلى النصوص الفائقة hypertexts التي ستُكتَب باستخدام <a href="https://wiki.hsoub.com/HTML" rel="external">لغة HTML</a>، ثم تُمثَّل رسوميًا باستخدام المتصفح مع عرض جميل وروابط تفاعلية، وساهمت الروابط بقوة في اعتماد بروتوكول HTTP إلى جانب سهولة إنشاء صفحات ويب جديدة، حيث ينقل هذا البروتوكول ملفات النصوص الفائقة بالإضافة إلى الصور وأنواع الملفات الأخرى عبر الشبكة.
</p>

<h3>
	الروابط والطلبيات
</h3>

<p>
	يمكن أن يؤشّر مستند إلى مستند آخر باستخدام الروابط ضمن متصفح الويب، حيث يحدِّد جزء الرابط الأول كل من البروتوكول وعنوان الخادم من خلال إما اسم نطاق domain name أو <a href="https://academy.hsoub.com/certificates/cisco/ccna/%d8%a8%d9%86%d8%a7%d8%a1-%d9%85%d8%ae%d8%b7%d8%b7-%d9%84%d8%b9%d9%86%d8%a7%d9%88%d9%8a%d9%86-ip-%d8%b9%d8%a8%d8%b1-%d8%a7%d9%84%d8%b4%d8%a8%d9%83%d8%a7%d8%aa-%d8%a7%d9%84%d9%81%d8%b1%d8%b9%d9%8a%d8%a9-%d8%b9%d9%86%d8%af-%d8%a8%d9%86%d8%a7%d8%a1-%d8%a7%d9%84%d8%b4%d8%a8%d9%83%d8%a7%d8%aa-r23/" rel="">عنوان IP</a>، وليس هذا الجزء خاصًا ببروتوكول HTTP؛ أما الجزء الثاني فهو جزء المستند الذي يتبع جزء العنوان ويمثِّل مسار المستند مثل <code><a href="https://flaviocopes.com/http/%E2%80%8E" ipsnoembed="true" rel="external nofollow">https://flaviocopes.com/http/‎</a></code> الذي يتكوّن مما يلي:
</p>

<ul>
<li>
		<code>https</code> هو البروتوكول.
	</li>
	<li>
		<code>flaviocopes.com</code> هو اسم النطاق الذي يؤشر إلى الخادم.
	</li>
	<li>
		<code>/http/</code> هو <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%88-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r1435/" rel="">عنوان URL</a> النسبي للمستند إلى مسار الخادم الجذر.
	</li>
</ul>
<p>
	يمكن أن يتداخل المسار مثل <code><a href="https://academy.hsoub.com/files/c5-programming/%E2%80%8E" ipsnoembed="true" rel="">https://academy.hsoub.com/files/c5-programming/‎</a></code>، حيث يكون عنوان URL للمستند هو <code>‎/files/c5-programming</code>؛ أما خادم الويب فيُعَدّ مسؤولًا عن تفسير الطلب وتقديم الاستجابة الصحيحة بعد تحليل الطلب، كما يمكن أن يكون الطلب عنوان URL الذي رأيناه سابقًا، فإذا أدخلنا عنوانًا وضغطنا <code>Enter</code> من لوحة المفاتيح في المتصفح، فسيرسل الخادم طلبًا في الخلفية إلى عنوان IP الصحيح مثل الطلب التالي:
</p>

<pre class="ipsCode">
GET /a-page
</pre>

<p>
	حيث ‎‎<code>‎‎/a-page</code>‎ هو عنوان URL الذي طلبته، كما يمكن أن يكون الطلب تابع HTTP ويُسمّى فعلًا verb أيضًا، حيث حدّد بروتوكول HTTP سابقًا ثلاثةً من هذه التوابع وهي:
</p>

<ul>
<li>
		<code>GET</code>.
	</li>
	<li>
		<code>POST</code>.
	</li>
	<li>
		<code>HEAD</code>.
	</li>
</ul>
<p>
	وقدّم الإصدار HTTP/1.1 التوابع:
</p>

<ul>
<li>
		<code>PUT</code>.
	</li>
	<li>
		<code>DELETE</code>.
	</li>
	<li>
		<code>OPTIONS</code>.
	</li>
	<li>
		<code>TRACE</code>.
	</li>
</ul>
<p>
	سنتحدّث عنها لاحقًا، وقد يكون الطلب مجموعة ترويسات HTTP، فالترويسات Headers هي مجموعة من أزواج المفتاح-القيمة <code>key: value</code> التي تُستخدَم للتواصل مع المعلومات الخاصة بالخادم المُحدَّدة مسبقًا ليتمكّن الخادم من فهم ما نعنيه، كما أنّ جميع الترويسات اختيارية باستثناء الترويسة <code>Host</code>.
</p>

<h3>
	توابع HTTP
</h3>

<p>
	أهم توابع HTTP هي:
</p>

<ul>
<li>
		<code>GET</code>: هو التابع الأكثر استخدامًا، وهو الخيار الذي يُستخدَم عند كتابة عنوان URL في شريط عنوان المتصفح، أو عند النقر على رابط، كما يطلب هذا التابع من الخادم إرسال المورد المطلوب على أساس استجابة.
	</li>
	<li>
		<code>HEAD</code>: يتشابه هذا التابع مع التابع <code>GET</code> تمامًا، ولكن <code>HEAD</code> يخبر الخادم بعدم إرسال جسم الاستجابة response body، بل إرسال الترويسات فقط.
	</li>
	<li>
		<code>POST</code>: يستخدِم العميل هذا التابع لإرسال البيانات إلى الخادم، حيث يُستخدَم عادةً في النماذج forms مثلًا، وعند التفاعل مع واجهة برمجة تطبيقات REST.
	</li>
	<li>
		<code>PUT</code>: يهدف هذا التابع إلى إنشاء مورد في عنوان URL المحدَّد باستخدام المعاملات المُمرَّرة في جسم الطلب، كما يُستخدم استخدامًا رئيسيًا في واجهات برمجة تطبيقات REST.
	</li>
	<li>
		<code>DELETE</code>: يُستدعَى هذا التابع مع عنوان URL لطلب حذف المورد المقابل لهذا العنوان، كما يُستخدَم استخدامًا رئيسيًا في واجهات برمجة تطبيقات REST.
	</li>
	<li>
		<code>OPTIONS</code>: يجب أن يرسِل الخادم قائمة توابع HTTP المسموح بها إلى عنوان URL المحدَّد عندما يتلقى طلب OPTIONS.
	</li>
	<li>
		<code>TRACE</code>: يعيد هذا التابع إلى العميل الطلب المُستلَم، حيث يُستخدَم هذا التابع لتنقيح الأخطاء debugging أو لأغراض التشخيص.
	</li>
</ul>
<h3>
	اتصال HTTP خادم/عميل
</h3>

<p>
	<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">بروتوكول HTTP</a> هو بروتوكول عديم الحالة stateless مثل معظم البروتوكولات التي تنتمي إلى مجموعة بروتوكولات TCP/IP، حيث ليس لدى الخوادم أيّ فكرة عن حالة العميل الحالية، فكل ما يهم الخوادم هو أن تتلقى طلبات ثم تلبيتها، كما لا يكون لطلب مسبق أيّ معنى في هذا السياق، وبالتالي يمكن أن يكون خادم الويب سريعًا جدًا، مع وجود قليل من المعالجة وحيز نطاق تراسلي bandwidth مناسب لمعالجة كثير من الطلبات المتزامنة.
</p>

<p>
	يُعَدّ بروتوكول HTTP مرنًا واتصاله سريعًا جدًا اعتمادًا على حِمل الشبكة، وهذا يتناقض مع البروتوكولات الأكثر استخدامًا في وقت صدوره مثل TCP وPOP/SMTP وبروتوكولات البريد التي تتضمن كثيرًا من عمليات المصافحة handshaking والتأكيدات على النهايات المُستقبَلة، كما تجرِّد المتصفحات الرسومية هذا الاتصال، ولكن يمكن توضيحه كما يلي، إذ يبدأ سطر الرسالة الأول بتابع HTTP ثم مسار المَورد النسبي وإصدار البروتوكول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2738_17" style="">
<span class="pln">GET /a-page HTTP/1.1</span></pre>

<p>
	ثم يجب إضافة ترويسات طلبات HTTP، إذ توجد هناك ترويسات متعددة، ولكن الترويسة الإلزامية الوحيدة هي <code>Host</code>:
</p>

<pre class="ipsCode">
GET /a-page HTTP/1.1
Host: flaviocopes.com
</pre>

<p>
	يمكنك اختبار ذلك باستخدام أداة telnet، وهي أداة سطر أوامر تتيح لنا الاتصال بأي خادم وإرسال الأوامر إليه، والآن افتح طرفيتك terminal واكتب <code>telnet flaviocopes.com 80</code> مثلًا، حيث سيؤدي ذلك إلى فتح طرفية تعرض ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_19" style="">
<span class="typ">Trying</span><span class="pln"> </span><span class="lit">178.128</span><span class="pun">.</span><span class="lit">202.129</span><span class="pun">...</span><span class="pln">
</span><span class="typ">Connected</span><span class="pln"> to flaviocopes</span><span class="pun">.</span><span class="pln">com</span><span class="pun">.</span><span class="pln">
</span><span class="typ">Escape</span><span class="pln"> character is </span><span class="str">'^]'</span><span class="pun">.</span></pre>

<p>
	أنت الآن متصل بخادم الويب Netlify، ثم اكتب ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2738_26" style="">
<span class="pln">GET </span><span class="pun">/</span><span class="pln">axios</span><span class="pun">/</span><span class="pln"> HTTP</span><span class="pun">/</span><span class="lit">1.1</span><span class="pln">
</span><span class="typ">Host</span><span class="pun">:</span><span class="pln"> flaviocopes</span><span class="pun">.</span><span class="pln">com</span></pre>

<p>
	اضغط بعد ذلك على زر <code>Enter</code> في سطر فارغ لتشغيل الطلب، وستكون الاستجابة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_28" style="">
<span class="pln">HTTP</span><span class="pun">/</span><span class="lit">1.1</span><span class="pln"> </span><span class="lit">301</span><span class="pln"> </span><span class="typ">Moved</span><span class="pln"> </span><span class="typ">Permanently</span><span class="pln">
</span><span class="typ">Cache</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pun">,</span><span class="pln"> max</span><span class="pun">-</span><span class="pln">age</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> must</span><span class="pun">-</span><span class="pln">revalidate
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Length</span><span class="pun">:</span><span class="pln"> </span><span class="lit">46</span><span class="pln">
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">:</span><span class="pln"> text</span><span class="pun">/</span><span class="pln">plain
</span><span class="typ">Date</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Sun</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">2018</span><span class="pln"> </span><span class="lit">14</span><span class="pun">:</span><span class="lit">07</span><span class="pun">:</span><span class="lit">07</span><span class="pln"> GMT
</span><span class="typ">Location</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//flaviocopes.com/axios/</span><span class="pln">
</span><span class="typ">Age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="typ">Connection</span><span class="pun">:</span><span class="pln"> keep</span><span class="pun">-</span><span class="pln">alive
</span><span class="typ">Server</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Netlify</span><span class="pln">
</span><span class="typ">Redirecting</span><span class="pln"> to https</span><span class="pun">:</span><span class="com">//flaviocopes.com/axios/</span></pre>

<p>
	وهذه هي <a href="https://academy.hsoub.com/programming/general/%D8%B1%D9%85%D9%88%D8%B2-%D8%A7%D9%84%D8%A5%D8%AC%D8%A7%D8%A8%D8%A9-%D9%81%D9%8A-http-r75/" rel="">استجابة HTTP</a> التي حصلنا عليها من الخادم، وهي طلب 301‎ Moved Permanently الذي يخبرنا بانتقال المَورد إلى موقع آخر انتقالًا دائمًا، وذلك لأننا اتصلنا بالمنفذ 80 وهو المنفذ الافتراضي لبروتوكول HTTP، ولكننا ضبطنا الخادم على إعادة التوجيه التلقائي إلى HTTPS، كما حُدِّد الموقع الجديد في ترويسة استجابة HTTP التي هي <code>Location</code>، وهناك ترويسات استجابة أخرى سنتحدث عنها لاحقًا، كما يفصل سطر فارغ ترويسة الطلب عن جسمه في كل من الطلب والاستجابة، حيث يحتوي جسم الطلب في مثالنا على السلسلة النصية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_30" style="">
<span class="typ">Redirecting</span><span class="pln"> to https</span><span class="pun">:</span><span class="com">//flaviocopes.com/axios/</span></pre>

<p>
	يبلغ طول هذه السلسلة النصية 46 بايتًا كما هو محدَّد في ترويسة <code>Content-Length</code>، إذ تظهر هذه السلسلة في المتصفح عند فتح الصفحة ريثما يُعاد توجيهك إلى الموقع الصحيح تلقائيًا، كما نستخدم أداة telnet في مثالنا، وهي أداة منخفضة المستوى يمكننا استخدامها للاتصال بأي خادم، لذلك لا يمكننا الحصول على أي نوع من إعادة التوجيه التلقائي، فلنتصل الآن بالمنفذ 443 وهو المنفذ الافتراضي لبروتوكول HTTPS، حيث لا يمكننا استخدام أداة telnet بسبب مصافحة <a href="https://academy.hsoub.com/devops/servers/%d9%83%d9%8a%d9%81%d9%8a%d8%a9-%d8%aa%d8%ab%d8%a8%d9%8a%d8%aa-%d8%b4%d9%87%d8%a7%d8%af%d8%a9-ssl-%d9%85%d9%86-%d8%b3%d9%84%d8%b7%d8%a9-%d8%b4%d9%87%d8%a7%d8%af%d8%a7%d8%aa-%d8%aa%d8%ac%d8%a7%d8%b1%d9%8a%d8%a9-%d8%a7%d9%84%d9%85%d9%81%d8%a7%d9%87%d9%8a%d9%85-%d8%a7%d9%84%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a9-r146/" rel=""><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></a> التي يجب أن تحدث،فولنستخدم الآن أداة <code>curl</code> وهي أداة <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر أوامر</a> أخرى، إذ لا يمكننا كتابة طلب HTTP مباشرةً، لكننا سنرى الاستجابة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_34" style="">
<span class="pln">curl </span><span class="pun">-</span><span class="pln">i https</span><span class="pun">:</span><span class="com">//flaviocopes.com/axios/</span></pre>

<p>
	سنحصل في المقابل على ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_36" style="">
<span class="pln">HTTP</span><span class="pun">/</span><span class="lit">1.1</span><span class="pln"> </span><span class="lit">200</span><span class="pln"> OK
</span><span class="typ">Cache</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pun">,</span><span class="pln"> max</span><span class="pun">-</span><span class="pln">age</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> must</span><span class="pun">-</span><span class="pln">revalidate
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">:</span><span class="pln"> text</span><span class="pun">/</span><span class="pln">html</span><span class="pun">;</span><span class="pln"> charset</span><span class="pun">=</span><span class="pln">UTF</span><span class="pun">-</span><span class="lit">8</span><span class="pln">
</span><span class="typ">Date</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Sun</span><span class="pun">,</span><span class="pln"> </span><span class="lit">29</span><span class="pln"> </span><span class="typ">Jul</span><span class="pln"> </span><span class="lit">2018</span><span class="pln"> </span><span class="lit">14</span><span class="pun">:</span><span class="lit">20</span><span class="pun">:</span><span class="lit">45</span><span class="pln"> GMT
</span><span class="typ">Etag</span><span class="pun">:</span><span class="pln"> </span><span class="str">"de3153d6eacef2299964de09db154b32-<abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">ssl</abbr>"</span><span class="pln">
</span><span class="typ">Strict</span><span class="pun">-</span><span class="typ">Transport</span><span class="pun">-</span><span class="typ">Security</span><span class="pun">:</span><span class="pln"> max</span><span class="pun">-</span><span class="pln">age</span><span class="pun">=</span><span class="lit">31536000</span><span class="pln">
</span><span class="typ">Age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">152</span><span class="pln">
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Length</span><span class="pun">:</span><span class="pln"> </span><span class="lit">9797</span><span class="pln">
</span><span class="typ">Connection</span><span class="pun">:</span><span class="pln"> keep</span><span class="pun">-</span><span class="pln">alive
</span><span class="typ">Server</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Netlify</span><span class="pln">

</span><span class="pun">&lt;!</span><span class="pln">DOCTYPE html</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">html prefix</span><span class="pun">=</span><span class="str">"og: http://ogp.me/ns#"</span><span class="pln"> lang</span><span class="pun">=</span><span class="str">"en"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">head</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">meta charset</span><span class="pun">=</span><span class="str">"utf-8"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">meta http</span><span class="pun">-</span><span class="pln">equiv</span><span class="pun">=</span><span class="str">"X-UA-Compatible"</span><span class="pln"> content</span><span class="pun">=</span><span class="str">"IE=edge"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">HTTP requests using </span><span class="typ">Axios</span><span class="pun">&lt;/</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">....</span></pre>

<p>
	لن ينقل خادم HTTP ملفات HTML فقط، وإنما يمكنه نقل ملفات أخرى مثل ملفات CSS و JS و SVG و PNG و JPG وأنواع ملفات متعددة أخرى، إذ يعتمد ذلك على الإعداد، فبروتوكول HTTP قادر تمامًا على نقل هذه الملفات، وسيعرف العميل نوع الملف، وبالتالي سيفسرها بالطريقة الصحيحة، وهذه هي الطريقة التي يعمل بها الويب عند استرداد صفحة HTML بواسطة المتصفح، إذ تُفسَّر هذه الصفحة وأي مَورد آخر يحتاجه المتصفح لعرض خاصية (<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و <a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a> والصور وغير ذلك) مُسترَدّة عبر طلبات HTTP إضافية إلى الخادم نفسه.
</p>

<h3>
	بروتوكول HTTPS والاتصالات الآمنة
</h3>

<p>
	يُعَدّ بروتوكول HTTPS امتدادًا لبروتوكول HTTP -أي بروتوكول نقل النص الفائق- والذي يوفِّر اتصالًا آمنًا، فبروتوكول HTTP غير آمن في تصميمه، فإذا فتحتَ متصفحك وطلبتَ من خادم الويب إرسال صفحة ويب لك، فستسير بياناتك ضمن رحلتين تكون الأولى من المتصفح إلى خادم الويب، والأخرى من خادم الويب إلى المتصفح، وقد تحتاج بعد ذلك إلى مزيد من الاتصالات -اعتمادًا على محتوى صفحة الويب- للحصول على ملفات CSS وملفات JavaScript والصور وما إلى ذلك، كما يمكن فحص بياناتك والتلاعب بها خلال مرورها في الشبكة أثناء أيّ من هذه الاتصالات.
</p>

<p>
	قد تكون العواقب وخيمةً، فقد يراقب ويسجّل طرف ثالث كل أنشطة شبكتك دون علمك، وقد تحقن بعض الشبكات إعلانات، وقد تكون عرضةً لهجوم الوسيط man-in-the-middle، وهو تهديد أمني يستطيع المهاجم من خلاله التلاعب ببياناتك وحتى انتحال شخصية حاسوبك عبر الشبكة، إذ يمكن لأي شخص الاستماع بسهولة إلى حزم HTTP المُرسَلة عبر شبكة واي فاي Wi-Fi عامة وغير مشفَّرة، حيث يهدف بروتوكول HTTPS إلى حل هذه المشكلة من خلال تشفير الاتصال الكامل بين متصفحك وخادم الويب.
</p>

<p>
	تُعَدّ كل الخصوصية والأمن مصدر قلق كبير في شبكة الإنترنت حاليًا، فقد كان الأمر مختلفًا قبل بضع سنوات، حيث كان بإمكانك توفير الأمن من خلال استخدام اتصال مشفر فقط في الصفحات المحمية بتسجيل الدخول أو أثناء عمليات الدفع في المتاجر الإلكترونية، كما أنّ معظم مواقع الويب قد استخدَمت بروتوكول HTTP بسبب أسعار <a href="https://academy.hsoub.com/devops/servers/web/apache/%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D8%B4%D8%A6-%D8%B4%D9%87%D8%A7%D8%AF%D8%A9-ssl-%D9%85%D9%88%D9%91%D9%8E%D9%82%D8%B9%D8%A9-%D8%B0%D8%A7%D8%AA%D9%8A%D9%91%D9%8B%D8%A7-%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%85%D8%B9-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A7%D9%84%D9%88%D9%90%D8%A8-apache-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1604-r325/" rel="">شهادات <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></a> وتعقيداتها.
</p>

<p>
	يُعَدّ استخدام HTTPS إلزاميًا على جميع المواقع في الوقت الحالي، إذ يستخدِمه حاليًا أكثر من 50% من مواقع الويب، وقد بدأ Google Chrome مؤخرًا في تمييز مواقع HTTP بأنها غير آمنة، لمنحك سببًا وجيهًا في جعل بروتوكول HTTPS إلزاميًا على جميع مواقع الويب الخاصة بك.
</p>

<p>
	يكون منفذ الخادم الافتراضي هو 80 عند استخدام بروتوكول HTTP، في حين يكون 443 عند استخدام بروتوكول HTTPS، وليست إضافته بصورة صريحة أمرًا إلزاميًا إذا استخدَم الخادم المنفذ الافتراضي، كما يُطلق على بروتوكول HTTPS أحيانًا اسم HTTP عبر <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr> أو HTTP عبر <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B6%D8%A8%D8%B7-%D8%AA%D8%B4%D9%81%D9%8A%D8%B1-ssltls-%D9%84%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84%D8%A7%D8%AA-%D8%A5%D9%84%D9%89-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-%D8%B9%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1604-r341/" rel=""><abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr></a>، حيث يكون بروتوكول <abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr> خلَفًا لبروتوكول <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr>؛ أما الشيء الوحيد غير المشفَّر عند استخدام بروتوكول HTTPS، فهو نطاق خادم الويب ومنفذ الخادم، بينما تُشفَّر كل المعلومات الأخرى بما في ذلك مسار المَورد والترويسات وملفات تعريف الارتباط cookies ومعامِلات الاستعلام.
</p>

<p>
	لن نشرح تفاصيل تحليل كيفية عمل بروتوكول <abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr> الداخلي، لكنك قد تعتقد أنه يضيف قدرًا كبيرًا من الحِمل على الشبكة، وربما هذا صحيح، إذ تتسبب أيّ عملية حسابية مُضافَة إلى معالجة موارد الشبكة في زيادة الحِمل على العميل والخادم وحجم الرُزم المرسَلة على حد سواء.
</p>

<p>
	يتيح HTTPS استخدام أحدث بروتوكول وهو HTTP/2 الذي يحتوي على ميزة إضافية يتفوق بها على الإصدار HTTP/1.1، وهذه الميزة هي أنه أسرع لعدة أسباب مثل ضغط الترويسة وتعدُّد الموارد، كما يمكن للخادم زجّ مزيد من الموارد عند طلب أحدها، فإذا طلب المتصفح صفحةً، فسيتلقى جميع الموارد اللازمة مثل الصور وملفات CSS وJS، كما يُعَدّ HTTP/2 تحسّنًا كبيرًا على HTTP/1.1 ويتطلب بروتوكول HTTPS، وهذا يعني أنّ HTTPS أسرع من HTTP بكثير إذا ضُبِط كل شيء ضبطًا صحيحًا باستخدام إعداد حديث على الرغم من وجود عبء التشفير الإضافي.
</p>

<h2>
	كيفية عمل طلبات HTTP
</h2>

<p>
	سنشرح ما يحدث عند كتابة عنوان URL في المتصفح من البداية إلى النهاية، حيث سنوضّح كيف تطبّق المتصفحات طلبات الصفحة باستخدام بروتوكول HTTP/1.1، إذ ذكرنا HTTP على وجه الخصوص لأنه يختلف عن اتصال HTTPS.
</p>

<p>
	إذا أجريت مقابلةً عمل من قبل، فقد تُسأَل ماذا يحدث عندما تكتب شيئًا ما في مربع بحث جوجل ثم تضغط مفتاح <code>Enter</code>؟ فهو أحد الأسئلة الأكثر شيوعًا التي ستُطرَح عليك، لمعرفة ما إذا كان بإمكانك شرح بعض المفاهيم الأساسية وما إذا كان لديك أيّ فكرة عن كيفية عمل الإنترنت، حيث سنحلّل ما يحدث عندما تكتب عنوان URL في شريط عنوان متصفحك ثم تضغط على <code>Enter</code>، وتُعَدّ هذه التقنية نادرة التغيّر وتشغّل أحد أكثر الأنظمة المجتمعية التي بناها الإنسان تعقيدًا واتساعًا.
</p>

<h3>
	تحليل طلبات URL
</h3>

<p>
	تملك المتصفحات الحديثة القدرة على معرفة ما إذا كان الشيء الذي كتبته في شريط العناوين هو عنوان URL فعلي أو مصطلح بحث، حيث سيستخدم المتصفح محرّك البحث الافتراضي إذا لم يكن عنوان URL صالحًا، فلنفترض أنك كتبتَ عنوان URL فعليًا، حيث ينشئ المتصفح أولًا عنوان URL الكامل عند إدخال العنوان ثم الضغط على مفتاح <code>Enter</code>، فإذا أدخلت نطاقًا مثل <code>flaviocopes.com</code>، فسيضيف المتصفح إلى بدايته <code>HTTP://‎</code> افتراضيًا اعتمادًا على بروتوكول HTTP.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: يجب عليك معرفة أنّ نظام ويندوز Windows قد يطبّق بعض الأشياء بطريقة مختلفة قليلًا عن نظامَي macOS ولينكس Linux.
	</p>
</blockquote>

<h3>
	مرحلة بحث DNS
</h3>

<p>
	يبدأ المتصفح عملية بحث <a href="https://academy.hsoub.com/devops/servers/%D9%85%D9%82%D8%AF%D9%91%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%8F%D8%B5%D8%B7%D9%8E%D9%84%D8%AD%D8%A7%D8%AA-%D9%88%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D8%B3%D9%85%D8%A7%D8%A1-%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82%D8%A7%D8%AA-r5/" rel="">DNS</a> للحصول على <a href="https://academy.hsoub.com/certificates/cisco/ccna/%d8%a8%d9%86%d8%a7%d8%a1-%d9%85%d8%ae%d8%b7%d8%b7-%d9%84%d8%b9%d9%86%d8%a7%d9%88%d9%8a%d9%86-ip-%d8%b9%d8%a8%d8%b1-%d8%a7%d9%84%d8%b4%d8%a8%d9%83%d8%a7%d8%aa-%d8%a7%d9%84%d9%81%d8%b1%d8%b9%d9%8a%d8%a9-%d8%b9%d9%86%d8%af-%d8%a8%d9%86%d8%a7%d8%a1-%d8%a7%d9%84%d8%b4%d8%a8%d9%83%d8%a7%d8%aa-r23/" rel="">عنوان IP</a> الخادم، ويُعَدّ اسم النطاق اختصارًا مفيدًا للبشر، ولكن الإنترنت منظَّم بطريقة تمكّن الحواسيب من البحث عن موقع الخادم الدقيق من خلال عنوان IP الخاص به، وهو عبارة عن مجموعة من الأعداد مثل <code>222.324.3.1</code> في الإصدار IPv4، حيث يتحقق المتصفح أولًا من ذاكرة DNS المخبئية المحلية، للتأكد من أن النطاق قد جرى تحليله resolved مؤخرًا، كما يحتوي كروم Chrome على عارض مفيد لذاكرة DNS المخبئية الذي يمكنك رؤيته من خلال <code>chrome://net-internals/#dns</code>، فإذا لم تعثر على أي شيء هناك، فهذا يعني استخدام المتصفح محلّل DNS عن طريق استدعاء نظام ‎<code>gethostbyname</code> POSIX لاسترداد معلومات المضيف.
</p>

<h4>
	gethostbyname
</h4>

<p>
	يبحث استدعاء النظام <code>gethostbyname</code> أولًا في ملف المضيفِين hosts المحلي، والذي يوجد في نظامَي macOS أو لينكس Linux ضمن <code>‎/etc/hosts</code>، للتأكد من أن النظام يوفِّر المعلومات محليًا، فإذا لم يقدّم ملف المضيفِين المحلي أيّ معلومات عن النطاق، فسيقدّم النظام طلبًا إلى خادم DNS، حيث يُخزَّن عنوان خادم DNS في تفضيلات النظام، كما يُعَدّ الخادمان التاليان خادمي DNS شهيرين:
</p>

<ul>
<li>
		<code>8.8.8.8</code>: خادم DNS العام الخاص بجوجل.
	</li>
	<li>
		<code>1.1.1.1</code>: خادم CloudFlare DNS.
	</li>
</ul>
<p>
	يستخدِم معظم الأشخاص خادم DNS الذي يوفِّره مزوّد خدمة الإنترنت الخاص بهم، كما يطبّق المتصفح طلب DNS باستخدام بروتوكول <a href="https://academy.hsoub.com/devops/servers/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-tcpip-%D9%88%D8%A8%D8%B9%D8%B6-%D9%85%D9%86-%D8%AE%D8%AF%D9%85%D8%A7%D8%AA%D9%87-r169/" rel="">UDP</a>، فالبروتوكولان TCP وUDP من بروتوكولات الشبكات الحاسوبية الأساسية ويتواجدان بالمستوى نفسه، لكن بروتوكول TCP موجَّه بالاتصال، بينما بروتوكول UDP عديم الاتصال وأخف، ويُستخدَم لإرسال الرسائل مع قليل من الحِمل على الشبكة.
</p>

<p>
	قد يحتوي خادم DNS على عنوان IP النطاق في الذاكرة المخبئية، فإذا لم يكن كذلك، فسيسأل خادم DNS الجذر، إذ يتكون هذا النظام من 13 خادم حقيقي موزع في أنحاء العالم، حيث يقود هذا النظام شبكة الإنترنت بأكملها، كما لا يعرف خادم DNS عنوان كل اسم نطاق على هذا الكوكب، ولكن يكفي معرفة مكان وجود محلّلي DNS من المستوى الأعلى، إذ يُعَدّ نطاق المستوى الأعلى top-level domain امتداد النطاق مثل <code>‎.com</code> و<code>‎.it</code> و<code>‎.pizza</code> وغير ذلك.
</p>

<p>
	يَعيد خادم DNS توجيه الطلب عند تلقّيه إلى خادم DNS الخاص بنطاق المستوى الأعلى TLD، ولنفترض أنك تبحث عن موقع <code>flaviocopes.com</code>، حيث يعيد خادم DNS الخاص بالنطاق الجذر عنوان IP الخاص بخادم نطاق المستوى الأعلى <code>‎.com</code>، ويخزّن بعدها محلّل DNS الخاص بنا عنوان IP لخادم نطاق المستوى الأعلى، بحيث لا يتعيّن عليه أن يسأل خادم DNS الجذر مرةً أخرى عنه.
</p>

<p>
	سيمتلك خادم DNS الخاص بنطاق المستوى الأعلى عناوين IP لخوادم الأسماء الرسمية الخاصة بالنطاق الذي نبحث عنه، إذ عند شرائك لنطاقٍ يرسل مسجل النطاق domain registrar نطاق المستوى الأعلى المناسب TDL إلى خوادم الأسماء.، فإذا حدّثتَ خوادم الأسماء عند تغيير مزود الاستضافة مثلًا، فسيُحدِّث مسجّل النطاق الخاص بك هذه المعلومات تلقائيًا، ونوضِّح فيما يلي أمثلةً عن خوادم DNS لمزود الاستضافة التي تكون أكثر من خادم عادةً لاستخدامها على أساس نسخة احتياطية:
</p>

<ul>
<li>
		<code>ns1.dreamhost.com</code>
	</li>
	<li>
		<code>ns2.dreamhost.com</code>
	</li>
	<li>
		<code>ns3.dreamhost.com</code>
	</li>
</ul>
<p>
	يبدأ محلل DNS بالخادم الأول، ويحاول طلب عنوان IP الخاص بالنطاق مع النطاق الفرعي أيضًا الذي تبحث عنه، وهو المصدر النهائي لعنوان IP.
</p>

<h3>
	إنشاء اتصال/مصافحة handshaking طلب TCP
</h3>

<p>
	يمكن للمتصفح الآن بدء اتصال TCP عند توفر عنوان IP الخادم، حيث يتطلب اتصال TCP عملية مصافحة handshaking قبل تهيئته بالكامل والبدء بإرسال البيانات، إذ يمكننا إرسال الطلب بعد إنشاء الاتصال.
</p>

<h3>
	إرسال الطلب
</h3>

<p>
	يكون الطلب عبارةً عن مستند نصي منظَّم بطريقة دقيقة يحدّدها بروتوكول الاتصال، ويتكون من 3 أجزاء هي:
</p>

<ul>
<li>
		سطر الطلب request line.
	</li>
	<li>
		ترويسة الطلب request header.
	</li>
	<li>
		جسم الطلب request body.
	</li>
</ul>
<p>
	يضبط سطر الطلب ما يلي في سطر واحد:
</p>

<ul>
<li>
		تابع HTTP.
	</li>
	<li>
		موقع المَورد.
	</li>
	<li>
		إصدار البروتوكول.
	</li>
</ul>
<p>
	إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_45" style="">
<span class="pln">GET </span><span class="pun">/</span><span class="pln"> HTTP</span><span class="pun">/</span><span class="lit">1.1</span></pre>

<p>
	تتكون ترويسة الطلب من مجموعة من أزواج الحقل-القيمة <code>field: value</code> التي تحدِّد قيمًا معينةً، وهناك حقلان إلزاميان هما <code>Host</code> و<code>Connection</code>، بينما جميع الحقول الأخرى اختيارية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_47" style="">
<span class="typ">Host</span><span class="pun">:</span><span class="pln"> flaviocopes</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Connection</span><span class="pun">:</span><span class="pln"> close</span></pre>

<p>
	يشير الحقل <code>Host</code> إلى اسم النطاق الذي نريد الوصول إليه، بينما يُضبَط الحقل <code>Connection</code> على القيمة <code>close</code> دائمًا إلّا في حالة إبقاء الاتصال مفتوحًا، وبعض حقول الترويسة الأكثر استخدامًا هي:
</p>

<ul>
<li>
		<code>Origin</code>
	</li>
	<li>
		<code>Accept</code>
	</li>
	<li>
		<code>Accept-Encoding</code>
	</li>
	<li>
		<code>Cookie</code>
	</li>
	<li>
		<code>Cache-Control</code>
	</li>
	<li>
		<code>Dnt</code>
	</li>
</ul>
<p>
	وهناك غيرها الكثير، ويُنهَى جزء الترويسة بسطر فارغ.
</p>

<p>
	أما جسم الطلب فهو اختياري ولا يُستخدَم في طلبات GET، ولكنه يُستخدَم بكثرة في طلبات POST وفي أفعال أخرى في بعض الأحيان، كمايمكن أن يحتوي على بيانات بتنسيق JSON، وبما أننا الآن نحلّل طلب GET، فإن الجسم فارغ.
</p>

<h3>
	الاستجابة Response
</h3>

<p>
	يعالِج الخادم الطلب بعد إرساله ويرسل استجابةً، حيث تبدأ الاستجابة برمز الحالة status code ورسالة الحالة status message، فإذا كان الطلب ناجحًا ويعيد القيمة 200، فستبدأ الاستجابة بما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_49" style="">
<span class="lit">200</span><span class="pln"> OK</span></pre>

<p>
	قد يعيد الطلب رمز ورسالة حالة مختلفَين مثل الأمثلة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_51" style="">
<span class="lit">404</span><span class="pln"> </span><span class="typ">Not</span><span class="pln"> </span><span class="typ">Found</span><span class="pln">
</span><span class="lit">403</span><span class="pln"> </span><span class="typ">Forbidden</span><span class="pln">
</span><span class="lit">301</span><span class="pln"> </span><span class="typ">Moved</span><span class="pln"> </span><span class="typ">Permanently</span><span class="pln">
</span><span class="lit">500</span><span class="pln"> </span><span class="typ">Internal</span><span class="pln"> </span><span class="typ">Server</span><span class="pln"> </span><span class="typ">Error</span><span class="pln">
</span><span class="lit">304</span><span class="pln"> </span><span class="typ">Not</span><span class="pln"> </span><span class="typ">Modified</span><span class="pln">
</span><span class="lit">401</span><span class="pln"> </span><span class="typ">Unauthorized</span></pre>

<p>
	تحتوي الاستجابة بعد ذلك على قائمة بترويسات HTTP وجسم الاستجابة الذي سيكون HTML لأننا ننفّذ الطلب في المتصفح.
</p>

<h3>
	تحليل HTML
</h3>

<p>
	تلقّى المتصفح الآن ملف HTML وبدأ في تحليله، وسيكرّر العملية نفسها بالضبط على جميع الموارد التي تطلبها الصفحة مثل:
</p>

<ul>
<li>
		ملفات CSS.
	</li>
	<li>
		الصور.
	</li>
	<li>
		الأيقونة المفضلة أو رمز الموقع favicon.
	</li>
	<li>
		ملفات جافا سكريبت.
	</li>
	<li>
		وغير ذلك.
	</li>
</ul>
<p>
	الطريقة التي تصيّر render بها المتصفحاتُ الصفحةَ خارج نطاق مناقشتنا، ولكن يجب فهم أن العملية التي شرحناها غير مقتصرة على صفحات HTML فقط، بل يمكن تطبيقها على أيّ عنصر مُقدَّم عبر بروتوكول HTTP.
</p>

<h3>
	بناء خادم HTTP باستخدام Node.js
</h3>

<p>
	خادم ويب HTTP الذي سنستخدِمه هو الخادم نفسه الذي استخدمناه سابقًا مثل تطبيق Node Hello World.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_53" style="">
<span class="kwd">const</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> port </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3000</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">((</span><span class="pln">req</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">=</span><span class="pln"> </span><span class="lit">200</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'text/plain'</span><span class="pun">)</span><span class="pln">
  res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="str">'Hello World\n'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Server</span><span class="pln"> running at http</span><span class="pun">:</span><span class="com">//${hostname}:${port}/`)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	لنحلّل المثال السابق بإيجاز:
</p>

<p>
	ضمّنا <a href="https://wiki.hsoub.com/Node.js/http" rel="external">وحدة <code>http</code></a> التي نستخدمها لإنشاء خادم HTTP، وضُبِط الخادم للاستماع على المنفذ المحدَّد <code>3000</code>، حيث تُستدعَى دالة رد النداء <code>listen</code> عندما يكون الخادم جاهزًا، فدالة رد النداء التي نمررها هي الدالة التي ستُنفَّذ عند وصول كل طلب، ويُستدَعى <a href="https://wiki.hsoub.com/Node.js/http#.D8.A7.D9.84.D8.AD.D8.AF.D8.AB_.27request.27" rel="external">حدث <code>request</code></a> عند تلقّي طلب جديد، مما يوفّر كائنين هما: طلب (كائن <a href="https://wiki.hsoub.com/Node.js/http#.D8.A7.D9.84.D8.B5.D9.86.D9.81_http.IncomingMessage" rel="external"><code>http.IncomingMessage</code></a>) واستجابة ( كائن <a href="https://wiki.hsoub.com/Node.js/http#.D8.A7.D9.84.D8.B5.D9.86.D9.81_http.ServerResponse" rel="external"><code>http.ServerResponse</code></a>).
</p>

<p>
	يوفّر الطلب <code>request</code> تفاصيل الطلب، حيث نصل من خلاله إلى ترويسات الطلبات وبياناتها؛ أما الاستجابة <code>response</code> فتُستخدَم لتوفير البيانات التي سنعيدها إلى العميل، كما ضبطنا خاصية statusCode على القيمة 200 في مثالنا، للإشارة إلى استجابة ناجحة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_55" style="">
<span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode </span><span class="pun">=</span><span class="pln"> </span><span class="lit">200</span></pre>

<p>
	وضبطنا ترويسة <code>Content-Type</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_57" style="">
<span class="pln">res</span><span class="pun">.</span><span class="pln">setHeader</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'text/plain'</span><span class="pun">)</span></pre>

<p>
	ثم أغلقنا الاستجابة في النهاية بإضافة المحتوى على أساس وسيط للتابع <code>end()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_59" style="">
<span class="pln">res</span><span class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span class="str">'Hello World\n'</span><span class="pun">)</span></pre>

<h3>
	إجراء طلبات HTTP
</h3>

<p>
	سنشرح كيفية إجراء طلبات HTTP في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">Node.js</a> باستخدام GET و POST و PUT و DELETE.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: سنستخدم مصطلح HTTP، ولكن HTTPS هو ما يجب استخدامه في كل مكان، لذلك تستخدِم هذه الأمثلة بروتوكول HTTPS بدلًا من HTTP.
	</p>
</blockquote>

<h4>
	إجراء طلب GET
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_62" style="">
<span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  hostname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'flaviocopes.com'</span><span class="pun">,</span><span class="pln">
  port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">443</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/todos'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'GET'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> req </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">statusCode</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}`)</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    process</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

req</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

req</span><span class="pun">.</span><span class="pln">end</span><span class="pun">()</span></pre>

<h4>
	إجراء طلب POST
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_64" style="">
<span class="kwd">const</span><span class="pln"> https </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'https'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">
  todo</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Buy the milk'</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> options </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  hostname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'flaviocopes.com'</span><span class="pun">,</span><span class="pln">
  port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">443</span><span class="pun">,</span><span class="pln">
  path</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/todos'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
    </span><span class="str">'Content-Length'</span><span class="pun">:</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">length
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> req </span><span class="pun">=</span><span class="pln"> https</span><span class="pun">.</span><span class="pln">request</span><span class="pun">(</span><span class="pln">options</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">statusCode</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">res</span><span class="pun">.</span><span class="pln">statusCode</span><span class="pun">}`)</span><span class="pln">

  res</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    process</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

req</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'error'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

req</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
req</span><span class="pun">.</span><span class="pln">end</span><span class="pun">()</span></pre>

<h4>
	PUT وDELETE
</h4>

<p>
	تستخدِم طلبات PUT وDELETE تنسيق طلب POST نفسه مع تغيير قيمة <code>options.method</code> فقط.
</p>

<h2>
	مكتبة Axios
</h2>

<p>
	تُعَدّ Axios مكتبة جافاسكربت يمكنك استخدامها لإجراء طلبات HTTP، وتعمل في المنصَّتين المتصفح Browser ونود Node.js.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91612" href="https://academy.hsoub.com/uploads/monthly_2022_02/01_Axios.png.0ded5c51a9e1ec4f4f443e9c60d15668.png" rel=""><img alt="01_Axios.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91612" data-unique="mra7fgcl7" src="https://academy.hsoub.com/uploads/monthly_2022_02/01_Axios.png.0ded5c51a9e1ec4f4f443e9c60d15668.png" style="width: 550px; height: auto;"></a>
</p>

<p>
	تدعم هذه المكتبة جميع المتصفحات الحديثة بما في ذلك الإصدار IE8 والإصدارات الأحدث، كما تستند على الوعود، وهذا يتيح لنا كتابة شيفرة صيغة عدم التزامن/الانتظار async/await لإجراء طلبات XHR بسهولة، كما يتمتع استخدام مكتبة Axios ببعض المزايا بالموازنة مع واجهة Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> الأصيلة، وهذه المزايا هي:
</p>

<ul>
<li>
		تدعم المتصفحات القديمة، حيث تحتاج <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-javascript-r739/" rel="">Fetch</a> إلى تعويض نقص دعم المتصفحات polyfill.
	</li>
	<li>
		لديها طريقة لإبطال طلب.
	</li>
	<li>
		لديها طريقة لضبط مهلة الاستجابة الزمنية.
	</li>
	<li>
		تحتوي على حماية CSRF مبنية مسبقًا.
	</li>
	<li>
		تدعم تقدّم التحميل.
	</li>
	<li>
		تجري تحويل بيانات <a href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-json-%D9%81%D9%8A-javascript-r548/" rel="">JSON</a> تلقائيًا.
	</li>
	<li>
		تعمل في Node.js.
	</li>
</ul>
<h3>
	تثبيت Axios
</h3>

<p>
	يمكن تثبيت Axios باستخدام <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">npm</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_71" style="">
<span class="pln">npm install axios</span></pre>

<p>
	أو باستخدام yarn:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_73" style="">
<span class="pln">yarn add axios</span></pre>

<p>
	أو يمكنك تضمينها ببساطة في صفحتك باستخدام unpkg.com كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_75" style="">
<span class="pun">&lt;</span><span class="pln">script src</span><span class="pun">=</span><span class="str">"https://unpkg.com/axios/dist/axios.min.js"</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<h3>
	واجهة برمجة تطبيقات Axios
</h3>

<p>
	يمكنك بدء طلب HTTP من كائن <code>axios</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_77" style="">
<span class="pln">axios</span><span class="pun">({</span><span class="pln">
  url</span><span class="pun">:</span><span class="pln"> </span><span class="str">'https://dog.ceo/api/breeds/list/all'</span><span class="pun">,</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'get'</span><span class="pun">,</span><span class="pln">
  data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    foo</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bar'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	لكنك ستستخدم التوابع التالية كما هو الحال في <a href="https://wiki.hsoub.com/jQuery" rel="external">jQuery</a>، حيث يمكنك استخدام <code>‎$.get()‎</code> و<code>‎$.post()‎</code> بدلًا من <code>‎$.ajax()‎</code>:
</p>

<ul>
<li>
		<code>axios.get()‎</code>
	</li>
	<li>
		<code>axios.post()‎</code>
	</li>
</ul>
<p>
	توفِّر مكتبة Axios توابعًا لجميع أفعال HTTP، والتي تُعَدّ أقل شيوعًا ولكنها لا تزال مُستخدَمة:
</p>

<ul>
<li>
		<code>‎axios.delete()‎</code>
	</li>
	<li>
		<code>axios.put()‎</code>
	</li>
	<li>
		<code>axios.patch()‎</code>
	</li>
	<li>
		<code>axios.options()‎</code>
	</li>
	<li>
		<code>axios.head()‎</code>: وهو تابع يُستخدَم للحصول على ترويسات HTTP لطلب ما مع تجاهل الجسم.
	</li>
</ul>
<h3>
	إرسال واستقبال الطلبات
</h3>

<p>
	إحدى الطرق الملائمة لاستخدام مكتبة Axios هي استخدام صيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/" rel="">async/await</a> الحديثة في الإصدار ES2017، حيث يستعلم مثال Node.js التالي عن <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهة</a> Dog <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> لاسترداد قائمة بجميع سلالات الكلاب dogs breeds باستخدام التابع <code>axios.get()‎</code>، ويحصي هذه السلالات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_81" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getBreeds </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> await axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://dog.ceo/api/breeds/list/all'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> countBreeds </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> breeds </span><span class="pun">=</span><span class="pln"> await getBreeds</span><span class="pun">()</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">breeds</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> $</span><span class="pun">{</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">entries</span><span class="pun">(</span><span class="pln">breeds</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">message</span><span class="pun">).</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> breeds</span><span class="pun">`)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

countBreeds</span><span class="pun">()</span></pre>

<p>
	إذا لم ترغب في استخدام صيغة async/await، فيمكنك استخدام صيغة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/" rel="">الوعود Promises</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_83" style="">
<span class="kwd">const</span><span class="pln"> axios </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'axios'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getBreeds </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://dog.ceo/api/breeds/list/all'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> countBreeds </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> breeds </span><span class="pun">=</span><span class="pln"> getBreeds</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">
          </span><span class="pun">`</span><span class="typ">Got</span><span class="pln"> $</span><span class="pun">{</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">entries</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">message</span><span class="pun">).</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> breeds</span><span class="pun">`</span><span class="pln">
        </span><span class="pun">)</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

countBreeds</span><span class="pun">()</span></pre>

<p>
	يمكن أن تحتوي استجابة GET على معامِلات في عنوان URL مثل <code><a href="https://site.com/?foo=bar" ipsnoembed="true" rel="external nofollow">https://site.com/?foo=bar</a></code>، حيث يمكنك تطبيق ذلك في مكتبة Axios عن طريق استخدام عنوان URL كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_88" style="">
<span class="pln">axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://site.com/?foo=bar'</span><span class="pun">)</span></pre>

<p>
	أو يمكنك استخدام خاصية <code>params</code> في الخيارات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_90" style="">
<span class="pln">axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'https://site.com/'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  params</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    foo</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bar'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	يشبه إجراء طلب POST تمامًا إجراء طلب GET مع استخدم <code>axios.post</code> بدلًا من <code>axios.get</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_92" style="">
<span class="pln">axios</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'https://site.com/'</span><span class="pun">)</span></pre>

<p>
	الكائن الذي يحتوي على معامِلات POST هو الوسيط الثاني:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_94" style="">
<span class="pln">axios</span><span class="pun">.</span><span class="pln">post</span><span class="pun">(</span><span class="str">'https://site.com/'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  foo</span><span class="pun">:</span><span class="pln"> </span><span class="str">'bar'</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2>
	مقابس الويب Websockets
</h2>

<p>
	مقابس الويب WebSockets هي بديل لاتصال HTTP في تطبيقات الويب، إذ توفِّر قناة اتصال ثنائية الاتجاه طويلة الأمد بين العميل والخادم، كما تبقى القناة مفتوحة بمجرد إنشائها، مما يوفر اتصالًا سريعًا جدًا مع زمن انتقال وحِمل منخفضَين، كما تدعم جميع المتصفحات الحديثة مقابس WebSockets.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91613" href="https://academy.hsoub.com/uploads/monthly_2022_02/02_BrowserSupportForWebSockets.png.bcdb00c7d8ed5087ab4569e6e6450ff7.png" rel=""><img alt="02_BrowserSupportForWebSockets.png" class="ipsImage ipsImage_thumbnailed" data-fileid="91613" data-unique="c10bjsoec" src="https://academy.hsoub.com/uploads/monthly_2022_02/02_BrowserSupportForWebSockets.png.bcdb00c7d8ed5087ab4569e6e6450ff7.png" style="width: 500px; height: auto;"></a>
</p>

<p>
	قد تتساءل، ما وجه الاختلاف بين WebSockets وبين HTTP؟ حسنًا، يُعَدّ HTTP بروتوكولًا وطريقة تواصل مختلفة تمامًا، فهو بروتوكول طلب/استجابة request/response، إذ يعيد الخادم البيانات التي يطلبها العميل، بينما تفيد مقابس WebSockets فيما يلي:
</p>

<ul>
<li>
		يمكن للخادم إرسال رسالة إلى العميل دون أن يطلب العميل صراحةً شيئًا ما.
	</li>
	<li>
		يمكن للعميل والخادم التحدث مع بعضهما البعض في الوقت نفسه.
	</li>
	<li>
		في حالة تبادل كمية قليلة من البيانات الإضافية لإرسال الرسائل، وهذا يعني اتصالًا ذو زمن انتقال منخفض.
	</li>
</ul>
<p>
	تُعَدّ مقابس WebSockets مناسبةً للاتصالات طويلة الأمد في الوقت الحقيقي، بينما يُعَدّ بروتوكول HTTP مفيدًا لتبادل البيانات والتفاعلات المؤقتة التي يبدأها العميل، كما يُعَدّ بروتوكول HTTP أبسط بكثير في التطبيق، بينما تتطلب مقابس WebSockets مزيدًا من العبء الإضافي.
</p>

<h3>
	مقابس الويب الآمنة
</h3>

<p>
	استخدم دائمًا البروتوكول الآمن والمشفّر لمقابس الويب أي <code>wss://‎</code>، ويشير <code>ws://‎</code> إلى إصدار مقابس WebSockets غير الآمن -مثل <code>http://‎</code> في مقابس WebSockets- الذي يجب تجنبه.
</p>

<h3>
	إنشاء اتصال WebSockets جديد
</h3>

<p>
	إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_97" style="">
<span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">'wss://myserver.com/something'</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> connection </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">WebSocket</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span></pre>

<p>
	يُعَدّ <code>connection</code> كائن <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket" rel="external nofollow">WebSocket</a>، كما يُشغَّل حدث <code>open</code> عند إنشاء الاتصال بنجاح، ويمكنك الاستماع إلى الاتصال عن طريق إسناد دالة رد نداء callback إلى خاصية <code>onopen</code> الخاصة بكائن <code>connection</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_99" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="pln">onopen </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا كان هناك أي خطأ، فستُشغَّل دالة رد النداء <code>onerror</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_101" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">WebSocket</span><span class="pln"> error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	إرسال البيانات إلى الخادم باستخدام WebSockets
</h3>

<p>
	يمكنك إرسال البيانات إلى الخادم بمجرد فتح الاتصال، حيث يمكنك إرسال البيانات بسهولة ضمن دالة رد النداء <code>onopen</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_103" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="pln">onopen </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  connection</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">'hey'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	استقبال البيانات من الخادم باستخدام WebSockets
</h3>

<p>
	استمع إلى الاتصال باستخدام دالة رد النداء <code>onmessage</code> التي تُستدعَى عند تلقي حدث <code>message</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_105" style="">
<span class="pln">connection</span><span class="pun">.</span><span class="pln">onmessage </span><span class="pun">=</span><span class="pln"> e </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	تطبيق خادم WebSockets في Node.js
</h3>

<p>
	تُعَدّ مكتبة <a href="https://github.com/websockets/ws" rel="external nofollow">ws</a> مكتبة WebSockets شائعةً ومُستخدَمةً مع Node.js، كما سنستخدمها لبناء خادم WebSockets، ويمكن استخدامها أيضًا لتطبيق العميل مع استخدام مقابس WebSockets للتواصل بين خدمَتين من الخدمات الخلفية، كما يمكنك تثبيت هذه المكتبة بسهولة باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_107" style="">
<span class="pln">yarn init
yarn add ws</span></pre>

<p>
	ليست الشيفرة التي تحتاج إلى كتابتها كبيرةً كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2738_109" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">WebSocket</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'ws'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> wss </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">WebSocket</span><span class="pun">.</span><span class="typ">Server</span><span class="pun">({</span><span class="pln"> port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8080</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
wss</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connection'</span><span class="pun">,</span><span class="pln"> ws </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  ws</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> message </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Received</span><span class="pln"> message </span><span class="pun">=&gt;</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">message</span><span class="pun">}`)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  ws</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">'ho!'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	تُنشئ الشيفرة السابقة خادمًا جديدًا على المنفذ 8080 وهو المنفذ الافتراضي لمقابس الويب WebSockets-، وتضيف دالة رد نداء عند إنشاء اتصال، مما يؤدي إلى إرسال <code>ho!‎</code> إلى العميل، وتسجيل الرسائل التي يتلقاها.
</p>

<p>
	شاهد مثالًا حيًا <a href="https://glitch.com/edit/#!/flavio-websockets-server-example" rel="external nofollow">لخادم مقابس الويب WebSockets</a> ومثالًا حيًا <a href="https://glitch.com/edit/#!/flavio-websockets-client-example" rel="external nofollow">لعميل WebSockets يتفاعل مع الخادم</a> على Glitch.
</p>

<p>
	ترجمة -وبتصرّف- للفصل Networking من كتاب The Node.js handbook لصاحبه Flavio Copes.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-nodejs-r1469/" rel="">التعامل مع الملفات في Node.js</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-nodejs-r1467/" rel="">البرمجة غير المتزامنة في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/networking/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%8A%D8%AF-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-r547/" rel="">تطبيقات الشبكات الحاسوبية: البريد الإلكتروني</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/certificates/cisco/ccna/%d8%b7%d8%a8%d9%82%d8%a9-%d8%a7%d9%84%d9%86%d9%82%d9%84-%d9%81%d9%8a-%d8%a8%d8%b1%d9%88%d8%aa%d9%88%d9%83%d9%88%d9%84-tcpip-r6/" rel="">طبقة النقل في بروتوكول TCP/IP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/certificates/cisco/ccna/%d8%a7%d8%b3%d8%aa%d9%83%d8%b4%d8%a7%d9%81-%d8%b9%d9%85%d9%84%d9%8a%d8%a9-%d8%aa%d9%88%d8%b5%d9%8a%d9%84-%d8%a7%d9%84%d8%b1%d8%b2%d9%85-%d8%b9%d9%86%d8%af-%d8%a8%d9%86%d8%a7%d8%a1-%d8%a7%d9%84%d8%b4%d8%a8%d9%83%d8%a7%d8%aa-r12/" rel="">استكشاف عملية توصيل الرزم عند بناء الشبكات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1468</guid><pubDate>Wed, 09 Feb 2022 11:40:05 +0000</pubDate></item></channel></rss>
