اذهب إلى المحتوى

كل الأنشطة

تحدث تلقائيًا

  1. الساعة الماضية
  2. كان يتم الإعتماد على C++ فيما مضى بشكل كبير، لكن حاليًا أصبح التوجه نحو C# بسبب إطار .NET (مثل .NET Core و .NET 5/6) الذي تطور بسرعة كبيرة ومن خلاله تستطيع تطوير تطبيق لمختلف المنصات Cross-platform. وتُعتبر الخيار الرئيسي لتطوير تطبيقات Windows الحديثة باستخدام إطار العمل Windows Presentation Foundation (WPF) و Universal Windows Platform (UWP). أما C++ تُستخدم لتطوير تطبيقات ذات أداء عالي وتلك التي تحتاج إلى التفاعل المباشر مع نظام التشغيل، مثل مكتبات Windows API و DirectX لتطوير تطبيقات الألعاب وتطبيقات الرسوميات المكثفة. يوجد أيضًا إطار Eelectrone.js الخاص بلغة جافاسكريبت، لكن ذلك للتطبيقات والبرامج البسيطة التي لا تحتاج إلى أداء عالي واستقرار. أيضًا للتطبيقات البسيطة يوجد مكتبات مثل PyQt أو Tkinter الخاصة بلغة بايثون لتطوير تطبيقات سطح المكتب، لكنها غير مناسبة لتطبيقات Windows الكبيرة والمتقدمة.
  3. اليوم
  4. تعمل كل قاعدة بيانات بصورة مختلفة عن الأخرى للاختلاف في نوع قواعد البيانات التي يتعاملان معها ولكن يمكنك تحديد قاعدة البيانات المناسبة لك من خلال معرفة الفرق بينهما وبشكل مختصر إذا كانت البيانات لديك منظمة وتحتاج إلى استعلامات معقدة وعلاقات بين الجداول، فإن MySQL يكون الخيار الأفضل. أما إذا كانت البيانات غير منظمة وتحتاج إلى مرونة في تخزينها وتعديلها كما أن ، فإن MongoDB يكون الخيار الأنسب حيث تعتبر MongoDB أو قواعد البيانات الغير علائقية مناسبه للتطبيقات التي تتطلب مرونة وقابلية للتوسع و تستخدم بنية بيانات تعتمد على المستندات وتوفر سرعة عالية وسهولة الاستخدام.
  5. لا توجد لغة بعينها لبرمجة تطبيقات الوينداوز، لكن هنالك عدة لغات برمجة شائعة لتطوير تطبيقات Windows، كلغة سي شارب التي تستخدم مع .NET Framework أو .NET Core وهي تعتبر مثالية لتطوير تطبيقات بواجهة مستخدم رسومية باستخدام Windows Forms أو WPF. لغة C++ هي الأخرى تستخدم لتطوير تطبيقات ذات أداء عالي والمميز فيها أنها تعمل بشكل مباشر مع Windows API وتستخدم مكتبات مثل MFC. أما VB.NET فهي لغة برمجة مع .NET، سهلة التعلم وتستخدم في التطبيقات البسيطة والمتوسطة وحتى لغة بايثون يمكن استخدامها لتطوير تطبيقات Windows باستخدام مكتبات مثل PyQt أو Tkinter. حتى JavaScript/TypeScript تستخدمان مع تقنيات مثل Electron لتطوير تطبيقات سطح المكتب باستخدام تقنيات الويب. فكل لغة برمجة لها مميزاتها وتناسب أنواعا مختلفة من التطبيقات، والاختيار يعتمد على متطلبات المشروع وخبرة المطور أيضا. يمكنك التوسع أكثر من هنا:
  6. ما هي لغه برمجه تطبيقات windows ؟
  7. العامل الذي يمكن أن نحدد من خلاله الأفضل، هو ما إذا كان تطبيق الويب الذي نود إنشاءه يتعامل مع بيانات غير متجانسة ويحتاج إلى مرونة في بنية البيانات، فإن MongoDB سيكون مناسبا كما أنه يمكن استخدام خطة MongoDB Atlas المجانية للاستضافة. أما إذا كانت البيانات تحتاج إلى علاقات معقدة وبنية صارمة، فإن MySQL هو الخيار الأفضل ويمكنك استخدام استضافة GoDaddy التي تقدم MySQL مجانا. فمن مميزات MongoDB أنها تشمل تخزين البيانات بشكل مرن باستخدام الوثائق (documents) في صيغة JSON-like، مما يجعلها مناسبة للبيانات غير المتجانسة أو المتغيرة بسرعة، فهي تتصف بأن لها مرونة عالية في التوسع الأفقي أو ما يعرف ب (horizontal scaling) وعدم الحاجة إلى تحديد بنية البيانات مسبقا، مما يتيح تعديل البيانات بسهولة. كما يوفر أداءً جيدًا في العمليات التي تتطلب قراءة وكتابة بيانات كبيرة بشكل سريع. من العيوب أنها ليست مثالية للعلاقات المعقدة بين البيانات مثل العلاقات الموجودة في قواعد البيانات العلائقية. أما الأخرى فهي قاعد بيانات علائقية (Relational Database) مثالية للعلاقات المعقدة بين البيانات باستخدام الجداول والروابط، وتوفر معاملات ACID لضمان النزاهة والاتساق في البيانات. تمتلك نظامًا بيئيًا ناضجًا مع دعم واسع وأدوات متعددة، وتوفر أداءً جيدًا في التعامل مع البيانات العلائقية والطلبات المعقدة. من العيوب أنها تتطلب تحديد بنية البيانات مسبقا، مما قد يكون مقيدا إذا كانت البيانات تتغير بشكل مستمر، كما أن التوسع الرأسي أسهل من التوسع الأفقي. يمكنك التوسع في الموضوع من هنا:
  8. انا اقوم بعمل تطبيق ويب لحجز صالات المناسبات و اريد ان ابدا ببناء قاعدة البيانات و لكن الحيره هنا في ايهما سأستخدم mangodb و mysql و ميزات كل واحده منهما و ايضا اسعار الاستضافه مثل godaddy قواعد mysql مجانيه علي خلاف mangodb فهل سأستطيع ان اجد مشتضيف مجاني لقواعد mangodb
  9. طبيعي أن تنسى بعض المعلومات والأكواد وليست مطالباً بالحفظ في الأساس حيث أنه من المهم هو الفهم أولاً ثم التطبيق مع المدرب دائماً وليس المراجعة على الدروس كما لو أنها علوم نظرية إنما تعلم البرمجة يكون من خلال التطبيق دائماً ويفضل أيضاً تدوين بعض المعلومات الأساسية خلال التعلم للرجوع إليها إذا إنقطعت لفترة عن التعلم يفضل الإطلاع على النصائح التالية والمتابعة في الدورة بدون أي مشكلة وعند التطبيق مع المدرب إذا واجهت مشكلة في فهم جزء ما يمكنك إضافة سؤالك أسفل الدرس وسيتم توضيحه لك
  10. كما أخبرك خالد يمكنك القيام بما تريده من خلال javascript بواسطة الدالة window.location.href كما يوجد عدة طرق أخرى لكل منهما إختلاف بسيط مثلاً استخدم location.href وهي مطابقة ل window.location.href setTimeout(() => { location.href = "https://example.com"; }, 5000); وأيضاً يمكن استخدم location.replace() الفرق هو أن location.replace لا يترك سجل للصفحة الحالية في متصفح المستخدم فيصبح مفيداً عند العمل مع ال form setTimeout(() => { location.replace("https://example.com"); }, 5000); ويوجد أيضاً window.location.assign(): setTimeout(() => { window.location.assign("https://example.com"); }, 5000);
  11. لا يمكنك بشكل مباشر إجبار المستخدم على الانتقال إلى موقع آخر في وقت معين باستخدام HTML فقط. ومع ذلك، يمكنك استخدام JavaScript لتحقيق هذه الميزة. هنا كيفية القيام بذلك: 1. استخدم دالة setTimeout() في JavaScript لتحديد الوقت الذي تريد بعده إعادة توجيه المستخدم إلى موقع آخر. 2. داخل دالة setTimeout() استخدم داله window.location.href لإعادة توجيه المستخدم إلى عنوان URL الجديد. مثل هذا المثال للتوضيح: <!DOCTYPE html> <html> <head> <title>Redirect Example</title> <script> // تحديد الوقت الذي تريد بعده إعادة التوجيه (في هذا المثال، بعد 5 ثوانٍ) setTimeout(function() { // إعادة توجيه المستخدم إلى موقع آخر window.location.href = "https://example.com"; }, 5000); // 5000 مللي ثانية = 5 ثوانٍ </script> </head> <body> <h1>سيتم إعادة توجيهك إلى موقع آخر بعد 5 ثوانٍ...</h1> </body> </html> في هذا المثال، سيتم إعادة توجيه المستخدم إلى https://www.example.com بعد 5 ثوان من تحميل الصفحة. لاحظ أنه يمكنك تغيير قيمة 5000 (التي تمثل عدد المللي ثوانٍ) إلى الوقت الذي تريده قبل إعادة التوجيه.
  12. كيف اجبر المستخدم الانتقال الى موقع آخر في وقت معين
  13. تصميم المواقع الإلكترونية هو عملية إنشاء المظهر البصري والواجهة الأمامية لموقع الويب. يركز مصممو المواقع الإلكترونية على العناصر الجمالية وسهولة الاستخدام لخلق تجربة مستخدم إيجابية. يتضمن ذلك: اختيار الألوان والخطوط ورسومات الموقع إنشاء تخطيطات الصفحات وتصميم واجهة المستخدم (UI) ضمان توافق الموقع مع مختلف الأجهزة والشاشات التأكد من سهولة التنقل في الموقع ووضوحه تطوير المواقع الإلكترونية هو عملية تحويل تصميم الموقع الإلكتروني إلى موقع ويب وظيفي يعمل بكفاءة. يقوم مطورو المواقع الإلكترونية بكتابة التعليمات البرمجية اللازمة لجعل الموقع يعمل كما هو مخطط له. يتضمن ذلك: كتابة التعليمات البرمجية للواجهة الأمامية (HTML، CSS، JavaScript) إنشاء قاعدة بيانات لتخزين محتوى الموقع كتابة التعليمات البرمجية للواجهة الخلفية (PHP، Python، Java) ربط الموقع بمنصة استضافة الويب اختبار الموقع للتأكد من خلوه من الأخطاء ضمان أمان الموقع and حماية البيانات ببساطة، يمكن تشبيه تصميم المواقع الإلكترونية بإنشاء مخططات ورسومات للمبنى، بينما تطوير المواقع الإلكترونية هو بناء المبنى الفعلي.
  14. ما هي الـMicroservices؟ الـMicroservices هي نهج معماري في تطوير البرمجيات يقوم على تقسيم التطبيق إلى مجموعة من الخدمات الصغيرة والمستقلة، كل خدمة تعمل على عملية واحدة وتتواصل مع الخدمات الأخرى عبر واجهة برمجة التطبيقات (API). هذا النهج يُمكّن من تطوير ونشر وصيانة كل خدمة بشكل مستقل عن الخدمات الأخرى. عند جمع هذه الخدمات في واجهة واحدة لتكوين التطبيق بالكامل حيث يتعامل المستخدم مع واجهة التطبيق وحيدة لكن كل خدمة تنفذ بشكل مستقل عن الاخرى ويمكن ان يكون هناك تخاطب بين بعض هذه الخدمات وتتمثل بالشكل التالي: الفوائد: • الاستقلالية: كل خدمة في نموذج الـMicroservices تعمل بشكل مستقل عن الخدمات الأخرى، مما يعني أنه يمكن تحديثها، أو استبدالها، أو تطويرها دون التأثير على بقية النظام بحيث إذا فشلت خدمة ما، لا تتأثر الخدمات الأخرى. • التخصص: كل خدمة مصممة لأداء وظيفة محددة جداً، وهذا يساعد في الحفاظ على النظام نظيفًا ومنظمًا. • المرونة: يمكن توزيع الخدمات على عدة خوادم أو حتى مراكز بيانات، مما يزيد من مرونة النظام وقابليته للتوسع. • القابلية للتوسع: يسهل توسيع نطاق الخدمات حسب الحاجة. التحديات: • التعقيد: يمكن أن يزيد التعقيد في التواصل بين الخدمات. • إدارة البيانات: يجب التعامل مع تقسيم البيانات بين الخدمات المختلفة. • الأمان: يجب ضمان الأمان في التواصل بين الخدمات. مثال على التطبيق العملي لها: في تطبيق التجارة الإلكترونية، بدلاً من وجود خادم واحد كبير يحتوي على كل الوظائف، يمكن تقسيم الوظائف إلى خدمات مستقلة مثل: • خدمة المستخدمين (User Service): تتعامل مع بيانات المستخدمين. • خدمة الطلبات (Order Service): تتعامل مع عمليات الشراء والطلبات. • خدمة الاصناف (Catalog Service): تتعامل مع المنتجات والمخزون. • خدمة البحث (Search Service): تتعامل مع المنتجات والمخزون. التواصل بين الخدمات: يمكن للخدمات التواصل عبر: • رسائل الوسيط (Message Broker): حيث تُرسل الخدمات الطلبات عبر قناة رسائل مشتركة حيث الredis يعتبر نوع منه. • الاتصال المباشر: حيث تتواصل الخدمات مباشرةً مع بعضها البعض. الخلاصة: الـMicroservices تعتبر نهجاً مثالياً للمشاريع التي تتطلب مرونة عالية وسرعة في التطوير والتوسع ولكنها تأتي مع تحدياتها الخاصة. وهي ليست دائماً الحل الأمثل لكل مشروع حيث يجب تقييمها بناءً على متطلبات المشروع وقدرات الفريق أنه لديه الخبرة الكافية لإدارة هذا النوع من الأنظمة، فإنها تتطلب فهماً عميقاً للمبادئ والممارسات الجيدة، واستخدام الأدوات المناسبة للتغلب على التحديات المرتبطة بها.
  15. البارحة
  16. الطريقة الأولى من خلال تصميم تلك الصور والأيقونات على برنامج مثل الفوتوشوب واستخدامها في التصميم من خلال CSS. أما الأفضل هو الإعتماد على SVG وبالطبع الأمر ليس بالسهل للرسم من خلاله، لكن هناك أدوات توفر لك الرسم والتحميل مباشرًة مثل التالي: https://editor.method.ac/ https://vectorpaint.yaks.co.nz/ أو يمكنك استخدام برنامج مثل adobe illustrator لرسم الشكل الذي تريده وتصديره كـ SVG:
  17. السلام عليكم كيف يمكنني عمل مثل هذه الأشكال بالصورة ب Css
  18. وعليكم السلام ورحمة الله وبركاته . نعم الأمر بسيط إن شاء الله . أولا تاكد من العميل أى لغة يريد تنفيذ جزء الواجهة الخلفية سواء php او nodejs . ويمكنك البحث على اليوتيوب كيفية تسجيل مستخدم جديد و تسجيل الدخول وستجد العديد من الشروحات حول ذلك . واذا كان يريد إستخدام php فيمكنك إستخدام إطار عمل laravel حيث سيسهل الكثر من الأمور عليك . ويمكنك قراءة هذا المقال حول ذلك اما إذا وجدت الأمر صعب وسياخذ وقت طويل فيمكنك أن تستعين بمطور backend يساعدك فى ذلك
  19. السلام عليكم طلب مني عميل ان اقوم بعمل صفحه للشركه الخاصة به بعدها طلب مني انشاء قاعده بيانات لتسجيل الدخول وتسجيل دخول جديد وانا لست backend هل عمل هذا بسيط بحيث يمكن أن ابحث واقوم به ام استعين ب backend
  20. public_html.zipالسلام عليكم اني صممت هذا الموقع واريد https://samabludan.com. لكن الموقع جدا بطيء اتمنى واحد يطيني رائيه لان اهواي سئلت ومحد اطه جواب اني اشتركت بالكورس لهدف المساعده وايضا راح اضيف ملفات الموقع واتمنى اتشوفوه وتعرضون الموقع حتى اعرف وين خطاي واعالجه public_html.zip
  21. السلام عليكوم ورحمة الله و بركاته ،اخواني في الله احتاج كيفية برمجة سوفتوير الرسيفر علما اني امتلك التعريفات الازمة مثل البوت و الاشارة و المداخل إلخ... انا بعد البحث علمت ان السوفت يمكن تعديلة من خلال برنامج hex على الكمبيوتر لكن البرمجة لا اعرف كيف يمكنني البرمجة و دمج التعريفات و انا لا املك الخبرة الازمة فهل من مساعد.
  22. بالتأكيد استخدام الـ templates الجاهزة يكون مفيداً في بعض الحالات . حيث انه يوفر الوقت والجهد في انشاء الهيكل الاساسي للموقع/التطبيق. مع إجراء تعديلات وإضافات على الـ template حتى يناسب احتياجات العميل ولكن يعتمد على الإتفاق بينك وبين العميل فهناك عميل يعرف ما يريد تنفيذه ويخبرك بذلك إذا كان يحتاج بناء الموقع من الصفر وهناك ما يهمه التكلفه فقط فيمكنك توضيح ذلك له وهناك العديد من المواقع لشراء ال templates مثل بيكاليكا و ThemeForest
  23. السلام عليكم هل من المفضل استخدام Templates الجاهزة في مشاريع الفريلانس ؟ مثلا اعرض الtemplate على العميل فان اعجبه اقوم بشراءه (ان كان مدفوعا) واعدل عليه ! بالمناسبة أين اجدNextjs templates ?
  24. اطلعت على الشيفرات الخاص بك، وتبدو صحيحة، وأنت بالفعل قمت بعمل الاتصال مع قواعد البيانات، وإرسال البيانات إليها، المفترض أن يتم الاتصال ثم تسجيل البيانات. فإذا لم يحدث كما هو متوقع يرجى بحث النقاط التالية - أولاً: التأكد من الاتصال الصحيح بقواعد البيانات أنت تستخدم localhost, 8889 لذلك يرجى التأكد أن فعلًا قواعد البيانات متصلة بهذا الخادم على نفس المنفذ، وسنقوم بعمل تعديل صغير بالشيفرات، لأن السطر الخاص بالاتصال إذا حدث به مشكلة فلن يتم تنفيذ باقي الأسطر <?php $servername = "localhost:8889"; $username = "root"; $password = "root"; $dbname = "student_information2"; try { // نضع الشيفرات داخل كتلة // try-catch $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } if ($_SERVER["REQUEST_METHOD"] == "POST") { // باقي الأسطر تكتب بشكل طبيعي // ... if (mysqli_query($conn, $sql)) { echo "Data inserted successfully!"; } else { echo "Error inserting data: " . mysqli_error($conn); } } $conn->close(); } catch (Exception $e) { echo "An error occurred: " . $e->getMessage(); } ?> - ثانيًا: التأكد من استخدام أسماء الحقول الصحيحة عند القراءة باستخدام $_post
  25. كنت شوفت تعليقك هذا فى احد المنشورات وحاولت استخدام الطريقة على قدر فهمي ولكني لم استطيع لاني مبتدأ اعتقد ان elevenlabs.io الـ API الخاص بهم ليس مجاني ومحدود جدا صوت مايكروسوفت للعربية المصرية شاكر وسملي اعتقد موجود فى كذا مشروع على github ولكني لا استطيع معرفة مكانة فى المشاريع الموجودة هذا هو البرنامج الذي انشأتة لتوضيح الفكرة كنت اريد اضافة ميزة قراءة الصوتية مثل الموجودة فى متصفح ايدج
  26. أولاً لا يوجد مكتبة في اللغة العربية تدعم بشكل كامل تحويل النص إلى صوت في اللغة العربية (لكي ترتاح من عناء البحث عن مكتبة تقوم بذلك) اذا كان الهدف فقط التجربة تستطيع ذلك. يمكنك الاطلاع على هذا المشروع والاستفادة منه في تحويل النص إلى صوت text_to_speech. ومن الأفضل استخدام تعلم الآلة لفعل ذلك لديك مثلاً elevenlabs.io استخدم الـ API الخاص بهم. بخصوص pyttsx3 تأكد من أنك قمت بتحديد اللغة العربية كلغة النص الذي تريد تحويله إلى صوت باستخدام الأمر التالي: engine = pyttsx3.init() engine.setProperty('rate', 150) # سرعة النطق engine.setProperty('voice', 'com.apple.speech.synthesis.voice.sara') # اسم الصوت العربي وتستطيع استبدال 'com.apple.speech.synthesis.voice.sara' بالصوت العربي المناسب لجهازك. ستجد تفصيل أكثر حول تلك المشكلة هنا بخصوص gtts و pyttsx3 :
  27. قمت بإنشاء محرر لقراء الكتب والروايات وكنت اريد اضافة ميزة جديدة لقراءة الكتاب او الرواية بالتعليق الصوتي كنت اريد ان يتم قرائتها وهي اوفلاين واستخدمت مكتبة pyttsx3 وحاولت فيها كثير لاستخدام اللغة العربية ولكني لما انجح وحاولت استخدام مكتبة gTTS للقراءة المباشرة ولكنها لم تعمل هل هناك حل ؟ لا اعتقد اني استخدمهم بطريقة صحيحة او ان هذة الطريقة الصحيح لاستخدام ميزة القراءة كنت افضل استخدام مكتبة مايكروسوفت المفتوحة للقراءة ولكني لم استطيع ايضا
  28. نتحدث في هذه السلسلة من المقالات عن خطوات بناء تطبيق بسيط يمثل واجهة خلفية على هيئة واجهة برمجية API باستخدام إطار عمل Express.js ولغة TypeScript. كيف أكتب واجهة برمجية REST في بيئة Node.js غالبًا ما تكون المكتبة Express.js هي الخيار اﻷول من بين إطارات عمل Node.js عند كتابة واجهة خلفية لتكون واجهة برمجية REST. وعلى الرغم من أنها تدعم أيضًا بناء صفحات وقوالب HTML، لكننا سنركز على بناء واجهة خلفية باستخدام لغة TypeScript كي تتمكن أي واجهة أمامية أو واجهة خلفية خارجية (خادم آخر) من الاستعلام منها، لهذا عليك أن: تمتلك معرفة أساسية بلغة جافا سكريبت، وكذلك معرفة ببيئة عمل Node.js ومطلعًا على معمارية REST. نسخة مثبّتة وجاهزة من Node.js (يفضل النسخة 14 وما بعد). سنبدأ من الطرفية أو (محرر سطر اﻷوامر) لإنشاء مجلّد خاص بالمشروع، ثم ننفّذ اﻷمر run npm init يُنشئ هذا الأمر بعض الملفات الأساسية التي نحتاجها لمشروع Node.js. ثم نضيف بعد ذلك إطار العمل Express.js وبعض المكتبات المفيدة اﻷخرى لمشروعنا من خلال الأمر التالي: npm i express debug winston express-winston cors وبالطبع هناك أسباب وجيهة كي يفضّل مطوّرو Node.js المكتبات السابقة: debug: وهي وحدة برمجية تُستخدم لتفادي استخدام اﻷمر ()console.log أثناء تطوير التطبيقات. إذ تستخدم لترشيح العبارات التي نريد تنقيحها عند محاولة حل المشاكل التي تواجهنا. وباﻹمكان إيقافها كليًا في نسخة اﻹنتاج بدلًا من إزالتها يدويًا. winston: وستكون المسؤولة عن تسجيل الطلبات القادمة إلى الواجهة البرمجية والاستجابات (واﻷخطاء) التي تعيدها الواجهة. وتتكامل express-winston مباشرة مع Express.js لهذا ستكون شيفرة واجهة برمجية المتعلقة بعملية إدارة السجلات التي تؤديها winston جاهزة. cors: هي جزء من أداة Express.js الوسيطة والتي تسمح لنا بمشاركة الموارد ذات اﻷصول المختلطة cross-origin resource sharing. وبدون هذه المكتبة لن تتمكن الواجهة البرمجية من تخديم سوى الواجهة الأمامية الموجودة في نفس النطاق الفرعي الذي يحوي الواجهة الخلفية. تستخدم الواجهة الخلفية في تطبيقنا تلك الحزم عند يعمل التطبيق، لهذا علينا تثبيت بعض اعتماديات مرحلة التطوير لتهيئة TypeScript. لهذا ننفذ الأمر التالي: npm i --save-dev @types/cors @types/express @types/debug source-map-support tslint typescript نحتاج الاعتماديات السابقة لتفعيل TypeScript في شيفرة تطبيقنا، واﻷنواع التي تستخدمها Express.js والاعتماديات اﻷخرى. وسيوّفر هذا اﻷمر وقتك عند استخدام بيئات تطوير متكاملة مثل WebStorm أو VSCode، إذ تتيح لك ميزات اﻹكمال التلقائي لبعض التوابع أثناء كتابة الشيفرة. ينبغي أن تكون الاعتمادات بشكلها النهائي ضمن الملف package.json كالتالي: "dependencies": { "debug": "^4.2.0", "express": "^4.17.1", "express-winston": "^4.0.5", "winston": "^3.3.3", "cors": "^2.8.5" }, "devDependencies": { "@types/cors": "^2.8.7", "@types/debug": "^4.1.5", "@types/express": "^4.17.2", "source-map-support": "^0.5.16", "tslint": "^6.0.0", "typescript": "^3.7.5" } وهكذا تكون جميع الاعتماديات اللازمة لعمل تطبيقنا جاهزة! هيكيلية مشروع واجهة برمجية REST باستخدام TypeScript سنستخدم في مشروعنا ثلاث ملفات وهي: app.ts/. common/common.routes.config.ts/. users/users.routes.config.ts/. إن الفكرة من استخدام مجلدين (common و users) في مشروعنا هو تكوين وحدتين لكل منهما مسؤولياتها الخاصة. وبالتالي قد نعطي الوحدتين كل أو بعض الميزات التالية: تهيئة الوجهة Rourte: لتعريف الطلبات التي يمكن أن تتعامل معها الواجهة البرمجية. خدمات Services: لتنفيذ مهام مثل الاتصال بقاعدة البيانات وتنفيذ استعلامات أو الاتصال بخدمات خارجية ضرورية للاستعلام. وسيط أو برمجية وسيطة middleware: للتحقق من صلاحية طلب معيّن قبل أن يتعامل المتحكم النهائي بالمسار مع تفاصيل الاستعلام. وحدات Models: لتعريف وحدات البيانات التي تطابق تخطيط قاعدة بيانات محددة، لتسهيل تخزين البيانات واستعادتها. متحكّمات controllers: لفصل معلومات تهيئة المسار أو الوجهة التي سننتقل إليها route configuration عن الشيفرة التي تعالج في النهاية (بعد المرور على أية برامج وسيطة) طلب هذا المسار أو تستدعي دوال خدمة من مستوى أعلى عند الحاجة، وتعيد الاستجابة على هذا الطلب إلى العميل. للمجلد هيكلية ذات تصميم بسيط متوافق مع الواجهة البرمجية. ملفات مسارات Routes شائعة الاستخدام في TypeScript لننشئ الملف common.routes.config.ts في المجلّد common ونضع فيه الشيفرة التالية: import express from 'express'; export class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; } getName() { return this.name; } } إن الطريقة التي ننشئ فيها المسارات routes هنا اختيارية، لكن، وطالما أننا نعمل مع TypeScript، فمن الجيد أن نتدرب على بناء المسارات باستخدام الوراثة من خلال التعليمة extends كما سنرى بعد قليل. وسيكون لجميع الملفات في هذا المشروع السلوك ذاته، كما سيكون لها اسم وإمكانية الوصول إلى كائن Express.js اﻷساسي في تطبيقنا Application. لنبدأ اﻵن ملف الوجهة الخاص بالمستخدمين ضمن المجلد users، لهذا سننشئ الملف users.routes.config.ts: import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } } ندرج هنا الصنف CommonRoutesConfig ونوّسعه إلى صنف جديد ندعوه UsersRoutes. ونرسل من خلال الدالة البانية التطبيق (كائن express.Application الرئيسي) والاسم إلى الدالة البانية للصنف CommonRoutesConfig. قد يبدو المثال بسيطًا لكن عند توسيع الأمر ليشمل عدة ملفات وجهات، وسيساعدنا ذلك في تفادي تكرار الشيفرة. لنفترض أننا سنحتاج إلى إضافة ميزات جديدة في هذا الملف، مثل إدارة السجلات، عندها بإمكاننا إضافة الحقول الضرورية إلى الصنف CommonRoutesConfig وعندها ستتمكن جميع اﻷصناف المشتقة منه من الوصول إلى هذه الميزة. استخدام دوال TypeScript المجرّدة لتقديم وظائف متشابه بين اﻷصناف ماذا لو أردنا الحصول على وظائف متشابهة ضمن اﻷصناف المختلفة (مثل تهيئة نقاط الاتصال بالواجهات البرمجية)، على الرغم من اختلاف طرق تنفيذ هذه الوظائف من صنف ﻵخر؟ أحد الخيارات المتاحة هو استخدام ميزة تُدعى التجريد abstraction في TypeScript. لنحاول إنشاء دالة مجرّدة بسيطة جدًا يرثها الصنف UsersRoutes (وبقية أصناف التوجيه التي قد ننشئها لاحقًا) من الصنف CommonRoutesConfig. ولنفترض أننا سنجبر كل الوجهات على امتلاك دالة (حتى نتمكن من استدعائها من الدالة البانية المشتركة) تُدعى()configureRoutes، وفيها نصرّح عن نقاط الوصول الخاصة بكل مورد من موارد الصنف. ولتنفيذ اﻷمر، سنضيف هذه اﻷشياء إلى الملف common.routes.config.ts: الكلمة المحجوزة abstract إلى السطر الذي يضم الكلمة class كي نفعّل خاصية التجريد لهذا الصنف. تصريح عن دالة جديدة في نهاية الصنف abstract configureRoutes(): express.Application، تجبر أي صنف مشتق من الصنف CommonRoutesConfig على تقديم آلية تطابق توقيع الدالة function signature، وسيرمي مصرّف TypeScript خطأ إن لم يجد آلية كهذه. استدعاء الدالة ()this.configureRoutes في نهاية الدالة البانية طالما أننا متأكدين من وجود هذه الدالة. ستبدو الشيفرة اﻵن كالتالي: import express from 'express'; export abstract class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; this.configureRoutes(); } getName() { return this.name; } abstract configureRoutes(): express.Application; } وهكذا ينبغي على كل صنف مشتق من الصنف CommonRoutesConfig أن يمتلك دالة ()configureRoutes تُدعى تُعيد كائنًا من النوع express.Application، وبالتالي لا بد من تحديث الملف users.routes.config.ts: import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } configureRoutes() { // (we'll add the actual route configuration here next) return this.app; } } دعنا نراجع ما فعلناه حتى الآن: أدرجنا بداية الملف common.routes.config ومن ثم الوحدة البرمجية express، وعرفنا بعد ذلك الصنف UserRoutes الذي أردناه أن يرث الصنف اﻷساسي CommonRoutesConfig وبالتالي سيضمّ الدالة ()configureRoutes ويقدّم آلية لتنفيذها. وﻹرسال المعلومات عبر الصنف CommonRoutesConfig، نستخدم الدالة البانية للصنف التي تتوقع تمرير كائن express.Application إليها، وهذا ما سنشرحه بتفاصيل أكثر لاحقًا. نمرر من خلال الدالة ()super التطبيق إلى الدالة البانية للصنف CommonRoutesConfig واسم الوجهة (وهي في هذه الحالة UsersRoutes). وتستدعي الدالة ()super بدورها الدالة ()configureRoutes. تهيئة وجهات Express.js الخاصة بنقاط الوصول إلى المستخدمين ستكون الدالة المكان الذي ننشئ فيه نقاط الوصول بين المستخدم والواجهة البرمجية REST. وفيها نستخدم التطبيق مع وظائف التوجيه من خلال Express.js. تكمن الفكرة في استخدام الدالة ()app.route لتفادي تكرار الشيفرة، وهذا اﻷمر سهل نسبيًا طالما أننا ننشئ واجهة برمجية REST ذات موارد محددة تمامًا. إن المورد اﻷساسي في تطبيقنا هو users، ولدينا حالتان: عندما يريد مستدعي الواجهة البرمجية إنشاء مستخدم جديد أو الحصول على قائمة بالمستخدمين الموجودين، لا بد أن يكون اسم المورد فقط users في نهاية المسار إلى المورد (لا نريد الخوض في هذه الحالة في فلترة أو تنظيم نتائج الاستعلام أو غيرها من العمليات في هذا التطبيق). عندما يريد المستدعي أن ينفّذ عملية ما على سجل مستخدم، وعندها لابد أن يكون نمط المسار إلى المورد كالتالي: users/:userId. تتيح آلية عمل الدالة ()route. في Express.js التعامل مع طلبات HTTP بأسلوب متسلسل أنيق، لأن جميع التوابع ()get. و ()post. وغيرها، ستعيد نفس النسخة من الكائن التي يعيدها التابع ()route.. لهذا سننهي عملية التهيئة كالتالي: configureRoutes() { this.app.route(`/users`) .get((req: express.Request, res: express.Response) => { res.status(200).send(`List of users`); }) .post((req: express.Request, res: express.Response) => { res.status(200).send(`Post to users`); }); this.app.route(`/users/:userId`) .all((req: express.Request, res: express.Response, next: express.NextFunction) => { // /users/:userId يُنفّذ البرنامج الوسيط هذه الدالة قبل أي استعلام // لكنه لا ينفذ شيئًا اﻵن // next() بل يمرر ببساطة التحكم إلى الدالة التالية في التطبيق تحت next(); }) .get((req: express.Request, res: express.Response) => { res.status(200).send(`GET requested for id ${req.params.userId}`); }) .put((req: express.Request, res: express.Response) => { res.status(200).send(`PUT requested for id ${req.params.userId}`); }) .patch((req: express.Request, res: express.Response) => { res.status(200).send(`PATCH requested for id ${req.params.userId}`); }) .delete((req: express.Request, res: express.Response) => { res.status(200).send(`DELETE requested for id ${req.params.userId}`); }); return this.app; } تتيح الشيفرة السابقة لعميل الواجهة البرمجية المتوافقة مع REST استدعاء نقطة الوصول users باستخدام أحد الاستعلامين POST أو GET، وتتيح له بنفس اﻷسلوب استدعاء نقطة الوصول users/:userId من خلال استعلامات GET أو PUT أو PATCH أو DELETE. كما أضفنا إلى نقطة الوصول users/:userId برنامج وسيط يستخدم الدالة ()all التي تُنفَّذ قبل أي استدعاء للدوال ()get أو ()put أو ()patch أو ()delete. وسيكون لهذه الدالة أهميتها عندما ننشئ لاحقًا مسارات يصل إليها فقط المستخدمين المستوثقين. وقد تلاحظ أن جميع الدوال ()all -وأية أجزاء من البرنامج الوسيط- تمتلك ثلاثة أنواع من الحقول Request و Response و NextFunction: النوع Request هو طريقة Express.js لتقديم طلبات HTTP التي يعالجها. ويُحدّث هذا النوع ويوسّع نوع Node.js اﻷصلي الذي يتعامل مع الطلبات. النوع Response هو طريقة Express.js لتقديم استجابات HTTP التي يعالجها. ويُحدّث هذا النوع ويوسّع نوع Node.js اﻷصلي الذي يتعامل مع الطلبات. كما يستخدم الحقل NextFunction الذي لا يقل أهمية عن الاثنين السابقين كدالة استدعاء تسمح بتمرير التحكم إلى أية دوال أخرى يضمها الوسيط. وتتشارك جميع البرامج الوسيطة نفس كائنات الطلب والاستجابة قبل أن يُرسل المتحكم الاستجابة إلى صاحب الطلب في النهاية. الملف app.ts المدخل إلى Node.js بعد أن وضعنا هيكلية بسيطة للتوجّه في التطبيق، ننتقل إلى تهيئة مدخل entry point إليه، لهذا سننشئ الملف app.ts في المجلد الجذري للمشروع ونبدؤه بالشيفرة التالية: import express from 'express'; import * as http from 'http'; import * as winston from 'winston'; import * as expressWinston from 'express-winston'; import cors from 'cors'; import {CommonRoutesConfig} from './common/common.routes.config'; import {UsersRoutes} from './users/users.routes.config'; import debug from 'debug'; هناك إدراجان فقط جديدان في هذا الملف هما: http: وهو وحدة برمجية أصلية في Node.js، نحتاجها في تشغيل تطبيق Express.js. body-parser: وهو وسيط يأتي مع Express.js، ويفسّر الطلب (صيغة JSON في حالتنا) قبل وصول التحكم إلى معالج الطلب الذي حددناه. ننتقل بعد إدراج الملفات إلى التصريح عن المتغيرات التي نريد استخدامها: const app: express.Application = express(); const server: http.Server = http.createServer(app); const port = 3000; const routes: Array<CommonRoutesConfig> = []; const debugLog: debug.IDebugger = debug('app'); تعيد الدالة ()express كائن تطبيق Express.js اﻷساسي الذي نمرره عبر تطبيقنا، من خلال إضافته بدايةً إلى الكائن http.Server (نحتاج إلى تشغيله بعد تهيئة الكائن express.Application الخاص بتطبيقنا) نترقب اﻵن الطلبات إلى المنفذ 3000 الذي تفهمه TypeScript على أنه من النوع Number بدلًا من المنافذ المعيارية مثل 80 لطلبات HTTP و 443 لطلبات HTTPS التي تُستخدم نمطيًا للاتصال مع الواجهة الأمامية للتطبيق. لماذا المنفذ 3000؟ لا توجد قاعدة تنص على أن المنفذ يجب أن يكون 3000، وسيُختار رقم المنفذ اعتباطيًا إن لم نخصص واحدًا. لكن الرقم 3000 يستخدم بكثرة في أمثلة توثيق Node.js و Express.js لهذا أكملنا على هذا النحو! هل يمكن أن تتشارك Node.js المنفذ مع الواجهه اﻷمامية؟ يمكننا أن نشغّل التطبيق محليًا على منفذ مخصص حتى لو أردنا من الواجهة الخلفية أن تستجيب للطلبات على المنافذ المعيارية. يتطلب اﻷمر خادم وكيل عكسي reverse proxy له نطاق رئيسي أو فرعي يستقبل الطلبات على أحد المنفذين 80 أو 443 ثم يعيد توجيهها إلى المنفذ الداخلي 3000. تتبع المصفوفة routes ملفات التوجيه الخاصة بنا لأغراض التنقيح كما سنرى، ونرى أخيرا كيف ينتهي debugLog بدالة مشابهة للدالة console.log، لكنها أفضل من ناحية إمكانية الضبط الدقيق، إذ تغطي تلقائيًا ما نريد أن ندعو به ملفاتنا أو وحداتنا البرمجية (دعوناه في حالتنا "app" عندما مررناه كنص إلى الدالة البانية ()debug). أصبحنا اﻵن جاهزين لتهيئة جميع وحدات Express.js الوسيطة والوجهات إلى الواجهة البرمجية: // JSON نضيف هنا وسيط لتفسير كل الطلبات القادمة بصيغة app.use(express.json()); // CORS نضيف هنا وسيطًا للسماح بالطلبات مختلطة الأصول app.use(cors()); //expressWinston نحضّر هنا إعدادات الوحدة الوسيطة المخصصة ﻹدارة التسجيل //Exprress.js التي تعالجها HTTP والتي تسجّل جميع طلبات const loggerOptions: expressWinston.LoggerOptions = { transports: [new winston.transports.Console()], format: winston.format.combine( winston.format.json(), winston.format.prettyPrint(), winston.format.colorize({ all: true }) ), }; if (!process.env.DEBUG) { loggerOptions.meta = false; // سجل الطلب على سطر واحد إن لم يكن التنقيح مفعّلًا } // هيئ المسجل بالإعدادات السابقة app.use(expressWinston.logger(loggerOptions)); //Express.js إلى مصفوفتنا بعد إرسال كائن UserRoutes نضيف //كي تضاف الوجهات إلى التطبيق routes.push(new UsersRoutes(app)); // هذه وجهة بسيطة للتأكد أن كل شيء يعمل كما هو مطلوب const runningMessage = `Server running at http://localhost:${port}`; app.get('/', (req: express.Request, res: express.Response) => { res.status(200).send(runningMessage) }); يرتبط expressWinston.logger تلقائيًا بالمكتبة Express.js ويسجل التفاصيل من خلال نفس البنية التحتية التي يستخدمها debug، وذلك لكل طلب مكتمل. وستنسق الخيارات التي مررناها إليه وتلوّن خرج الطرفية التي تعرض السجلات، إضافة إلى عرض سجلات أكثر تفصيلًا (وهو الأمر الافتراضي) عندما نفعّل نمط التنقيح. وتجدر الملاحظة إلى ضرورة تعريف وجهاتنا بعد إعداد expressWinston.logger. وأخيرًا نأتي إلى اﻷمر اﻷكثر أهمية: server.listen(port, () => { routes.forEach((route: CommonRoutesConfig) => { debugLog(`Routes configured for ${route.getName()}`); }); //console.log الحالة الوحيدة التي لن تحاشى فيها استخدام // هو معرّفة متى ينتهي الخادم من عملية اﻹقلاع console.log(runningMessage); }); تشغّل الشيفرة السابقة الخادم فعليًا، وعندما يبدأ تنفيذها يشغّل Node.js توابع الاستدعاء الخاصة بنا والتي تسجّل في وضع التنقيح أسماء كل الوجهات routes التي أعددناها وهي UsersRoutes حتى اللحظة. تنبهنا دوال الاستدعاء بعد ذلك إلى أن الواجه الخلفية جاهزة لاستقبال الطلبات، حتى لو كانت تعمل في وضع الإنتاج. تحديث الملف package.json لنقل شيفرة TypeScript إلى جافا سكريبت وتشغيل التطبيق بعد إنجاز البنية اﻷساسية للتطبيق وتحضيره للتشغيل، نحتاج أولًا إلى بعض اﻹعدادات لتمكين نقل transpilation شيفرة TypeScript: { "compilerOptions": { "target": "es2016", "module": "commonjs", "outDir": "./dist", "strict": true, "esModuleInterop": true, "inlineSourceMap": true } } نضيف أخيرًا بعض اللمسات النهائية على الملف package.json على هيئة سكربتات: "scripts": { "start": "tsc && node --unhandled-rejections=strict ./dist/app.js", "debug": "export DEBUG=* && npm run start", "test": "echo \"Error: no test specified\" && exit 1" }, يعمل السكربت test كملف مؤقت سنستبدله لاحقًا. تنتمي الوحدة tsc في السكربت start إلى TypeScript، وهو المسؤول عن نقل شيفرة TypeScript إلى جافا سكريبت التي ستظهر في المجلد dist. ثم نشغّل النسخة المبنية من التطبيق باستخدام التعليمة node ./dist/app.js. نمرر الوسيط unhandled-rejections=strict-- إلى Node.js (حتى في النسخ 16 وأعلى) لإيقاف التنفيذ عند ظهور خطأ غير محسوب في الشيفرة، ويسهّل ذلك معرفة سبب الخطأ وتصحيحه وهذا اﻷسلوب أوضح من الخيار اﻵخر وهو الاعتماد على كائن السجلات expressWinston.errorLogger الذي يزوّدك بقائمة اﻷخطاء بعد توقف المصرّف. ومعنى ذلك أننا سنترك Node.js يعمل على الرغم من وجود خطأ غير محسوب في الشيفرة وقد يسبب ذلك سلوكًا غير متوقع للخادم وظهور أخطاء أخرى قد تكون أكثر تعقيدًا. يستدعي السكربت debug السكربت start لكنه يعرّف أولًا متغير البيئة DEBUG. ولهذا المتغير تأثير في تمكين جميع عبارات ()debugLog (إضافة إلى تلك التي تقدمها Express.js، والتي تستخدم نفس وحدة التنقيح debug التي نستخدمها) لعرض تفاصيل مفيدة على الطرفية، وإلا ستختفي هذه التفاصيل عند تشغيل الخادم في وضع اﻹنتاج باستخدام التعليمة npm start. جرّب تنفيذ اﻷمر npm run debug. بنفسك، وقارن نتائج الخرج على الطرفية مع تلك التي تنتج عن تنفيذ npm start. تلميح: بإمكانك تحديد خرج التنقيح ليعطي فقط عبارات ()debugLog الموجودة في الملف app.ts، وذلك باستخدام DEBUG=app بدلًا من *\=DEBUG. فالوحدة debug مرنة عمومًا، وهذه الميزة ليست استثناءً. قد يحتاج مستخدمي ويندوز استبدال export بالتعليمة SET لأن export هي الطريقة التي تعمل على لينكس و ماك أو إس. أما إن أردت دعم عدة بيئات تطوير في تطبيقك، جرّب الحزمةcross-env package التي تزوّدك بحلول واضحة لهذه المسألة. اختبار الواجهة الخلفية مع تنفيذ أحد اﻷمرين npm run debug أو npm start ستكون الواجهة الخلفية جاهزة لتلقي الطلبات على المنفذ 3000. يمكننا عندها استخدام أحد المكتبات cURL أو Postman أو Insomnia لاختبار الواجهة الخلفية. وطالما أننا لم ننشئ سوى هيكلية للمورد users، بإمكاننا ببساطة إرسال طلبات دون جسم للطلب لنتأكد أن كل شيئ يجري كما هو متوقع، فمثلًا: curl --request GET 'localhost:3000/users/12345' ستعيد عندها الواجهة الخلفية الاستجابة: GET requested for id 12345. وعند استخدام POST: curl --request POST 'localhost:3000/users' \ --data-raw '' وغيرها من أنواع الطلبات، ستعيد الواجهة الخلفية نفس الاستجابة. الخلاصة بدأنا في هذا المقال في إنشاء واجهة برمجية REST بتهيئة المشروع من الصفر ومن ثم دخلنا في أساسيات إطار العمل Express.js. خطونا بعد ذلك أولى خطواتنا في احتراف TypeScript عن طريق بناء نموذج UsersRoutesConfig يرث CommonRoutesConfig وسنعيد استخدام هذا النموذج في الجزء الثاني من هذه السلسلة. أنهينا العمل بعد ذلك بتهيئة ملف المدخل app.ts لاستخدام الوجهات، ومن ثم تهيئة ملف package.json بالسكربتات اللازمة لبناء وتشغيل التطبيق. وعلى الرغم من استخدام أساسيات الواجهة البرمجية REST مع Express.js و TypeScript في مقالنا، فسوف نركز في المقال التالي على بناء متحكمات مناسبة للموارد والتعرف على نماذج أخرى مثل الخدمات والوحدات الوسيطة والمتحكمات وغيرها من الوحدات البرمجية. ترجمة -وبتصرف- للمقال Building a Node.js TypeScript REST API Part1 Express.js اقرأ أيضًا مدخل إلى Node.js وExpress دليلك لربط واجهة OpenAI API مع Node.js شرح فلسفة RESTful - تعلم كيف تبني واجهات REST البرمجية إنشاء مدوّنة باستخدام Node.js و Express (الجزء الأول)
  1. عرض المزيد
×
×
  • أضف...