نشر تطبيقات MERN على Heroku باستخدام MongoDB Atlas: دليل شامل
مقدمة إلى حزمة MERN Stack وخدمات النشر السحابي
في هذا الدليل الشامل، سنتعمق في بناء ونشر تطبيق متكامل يعتمد على حزمة MERN Stack إلى منصة Heroku السحابية. تُعد MERN اختصارًا للحروف الأولى من MongoDB، Express.js، React، و Node.js، وهي من الحزم التقنية الأكثر شيوعًا وفعالية في بناء تطبيقات الويب الحديثة. تتضمن هذه الحزمة العمل على الواجهة الأمامية (باستخدام React)، والواجهة الخلفية (باستخدام Express و Node.js)، بالإضافة إلى قاعدة بيانات قوية (باستخدام MongoDB).
من ناحية أخرى، تُعد Heroku منصة كخدمة (PaaS) تمكّن المطورين من بناء وتشغيل وإدارة تطبيقاتهم بشكل كامل في البيئة السحابية، مما يسهل عملية النشر والصيانة. أما بالنسبة لقاعدة البيانات، فسنستخدم MongoDB Atlas، وهي خدمة قواعد بيانات سحابية عالمية مصممة خصيصًا للتطبيقات الحديثة. يوفر هذا الخيار أمانًا فائقًا مقارنةً بتثبيت MongoDB محليًا على الخادم، كما يتيح مرونة أكبر في توسيع الموارد.
سنقوم ببناء تطبيق React بسيط في الواجهة الأمامية، والذي سيرسل طلبات POST إلى واجهة برمجة التطبيقات (API) لإضافة مستخدم جديد، ويمكنه أيضًا إرسال طلبات GET لجلب جميع المستخدمين المسجلين.
لنبدأ بالبناء: إعداد الواجهة الأمامية (React App)
قبل الشروع في بناء مشروعنا، تأكد من تثبيت Node.js على جهاز الكمبيوتر الخاص بك. يوفر Node.js أيضًا أداة إدارة الحزم npm، والتي سنستخدمها لتثبيت الحزم الضرورية.
تثبيت create-react-app
تُستخدم أداة create-react-app لإنشاء تطبيق React أولي جاهز للعمل. إذا لم تكن مثبتة لديك، قم بتشغيل الأمر التالي في سطر الأوامر:
npm i create-react-app -g
يشير العلم -g إلى تثبيت الحزمة بشكل عام (globally) على نظامك.
إنشاء دليل المشروع
الآن، لنقم بإنشاء دليل المشروع الخاص بنا وتثبيت الاعتمادات الأولية:
create-react-app my-project
cd my-project
يقوم الأمر الأول بإنشاء دليل باسم 'my-project' وتثبيت جميع الاعتمادات التي سيحتاجها تطبيق React الأولي. بعد اكتمال التثبيت، يقوم الأمر الثاني بالانتقال إلى دليل المشروع الذي تم إنشاؤه.
تشغيل التطبيق وإجراء التعديلات اللازمة
لتشغيل تطبيق React والبدء في التعديلات، استخدم الأمر التالي:
npm start
يقوم هذا الأمر بتشغيل تطبيق React، ويوفر لك عنوان URL يمكنك من خلاله معاينة مشروعك في المتصفح. يمكنك بعد ذلك إجراء التعديلات الضرورية مثل تغيير الصور أو النصوص.
تثبيت axios لإدارة طلبات HTTP
axios هي مكتبة JavaScript تُستخدم لتسهيل إرسال طلبات HTTP. سنستخدمها لإرسال الطلبات من الواجهة الأمامية (React) إلى واجهات برمجة التطبيقات (APIs) التي ستوفرها الواجهة الخلفية.
npm i axios --save
بناء الواجهة الخلفية (Backend)
تتولى الواجهة الخلفية إدارة واجهات برمجة التطبيقات (APIs)، وتتعامل مع الطلبات الواردة، وتتصل بقاعدة البيانات.
تثبيت حزم الواجهة الخلفية
لنقم بتثبيت الحزم الأساسية التي ستحتاجها واجهتنا الخلفية:
npm i express cors mongoose body-parser --save
express: إطار عمل لتطبيقات الويب لـNode.jsيوفر مجموعة قوية من الميزات.cors: حزمة لـNode.jsتوفر وسيطًا لـConnect/ExpressلتمكينCORS(Cross-Origin Resource Sharing) بخيارات متنوعة.mongoose: أداة نمذجة كائنات لـMongoDBمصممة للعمل في بيئة غير متزامنة، وتدعم الوعود (promises) والدوال المرتدة (callbacks).body-parser: وسيط لـNode.jsلتحليل نصوص الطلبات (body parsing middleware).
إنشاء مجلد الواجهة الخلفية
سنقوم بإنشاء مجلد خاص بالواجهة الخلفية والانتقال إليه:
mkdir backend
cd backend
تهيئة الواجهة الخلفية: ملف server.js
أولاً، قم بإنشاء ملف server.js، والذي سيكون نقطة الدخول الرئيسية للواجهة الخلفية:
touch server.js
في ملف server.js، أضف الكود التالي:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const path = require('path')
const app = express();
require('./database');
app.use(bodyParser.json());
app.use(cors());
// API
const users = require('./api/users');
app.use('/api/users', users);
app.use(express.static(path.join(__dirname, '../build')))
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../build'))
})
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});
في هذا الكود، تقوم الدالة express.static بتقديم الملفات الثابتة التي يتم إنشاؤها عند تشغيل الأمر npm run build في مشروع React. تذكر أن الملفات المبنية توجد في مجلد build. ووفقًا لتهيئة خادمنا، سيتم توجيه أي طلب يتم إرساله إلى المسار /api/users إلى API المستخدمين الذي سنقوم بتهيئته لاحقًا. كما نستخدم process.env.PORT لتحديد منفذ الخادم، مما يجعله مرنًا للاستضافة على منصات مثل Heroku.
تهيئة API المستخدمين
لنقم بإنشاء مجلد api وملف users.js داخله لتعريف واجهة برمجة التطبيقات الخاصة بالمستخدمين:
mkdir api
touch api/users.js
في ملف api/users.js، أضف الكود التالي:
const express = require('express');
const router = express.Router()
const User = require('../models/User');
router.get('/', (req, res) => {
User.find()
.then(users => res.json(users))
.catch(err => console.log(err))
})
router.post('/', (req, res) => {
const { name, email } = req.body;
const newUser = new User({
name: name,
email: email
})
newUser.save()
.then(() => res.json({ message: "Created account successfully" }))
.catch(err => res.status(400).json({ "error": err, "message": "Error creating account" }))
})
module.exports = router
في الكود أعلاه، نقوم بإنشاء معالجات لطلبات GET و POST التي تقوم بجلب جميع المستخدمين وإضافة مستخدمين جدد على التوالي. يتم تسهيل عملية جلب وإضافة المستخدمين إلى قاعدة البيانات بواسطة نموذج User الذي سنقوم بإنشائه لاحقًا.
إنشاء نموذج User
لنقم بإنشاء مجلد models وملف user.js داخله لتعريف نموذج المستخدم:
mkdir models
touch models/user.js
في ملف models/user.js، أضف الكود التالي:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
}
})
module.exports = mongoose.model("User", userSchema, "users")
في هذا الكود، يتم إنشاء مخطط (schema) للمستخدم يحتوي على حقول الاسم والبريد الإلكتروني. في نهاية الملف، يتم تصدير النموذج ("User") مع المخطط والمجموعة ("users") الخاصة به في قاعدة البيانات.
ربط قاعدة البيانات: MongoDB Atlas
وفقًا للوثائق الرسمية، "MongoDB Atlas هي خدمة قواعد البيانات السحابية العالمية للتطبيقات الحديثة."
أولاً، نحتاج إلى التسجيل في Mongo Cloud. اتبع هذه الوثائق لإنشاء حساب Atlas وإنشاء مجموعة (cluster) خاصة بك. من الأمور الهامة جدًا التي يجب الانتباه إليها هي إضافة عنوان IP الخاص بك إلى القائمة البيضاء للاتصال (whitelisting your connection IP address). إذا تجاهلت هذه الخطوة، فلن تتمكن من الوصول إلى المجموعة، لذا تأكد من إكمالها.
المجموعة (cluster) هي خادم صغير سيدير مجموعاتنا (collections) التي تشبه الجداول في قواعد بيانات SQL.
لربط واجهتك الخلفية بالمجموعة، قم بإنشاء ملف database.js، والذي كما رأيت يتم استدعاؤه في server.js. ثم أدخل الكود التالي:
const mongoose = require('mongoose');
const connection = "mongodb+srv://username:<password>@<cluster>/<database>?retryWrites=true&w=majority";
mongoose.connect(connection, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false })
.then(() => console.log("Database Connected Successfully"))
.catch(err => console.log(err));
في المتغير connection، أدخل username (لحساب MongoDB Cloud الخاص بك)، و password (كلمة مرور المجموعة)، و cluster (عنوان مجموعتك)، و database (اسم قاعدة البيانات الخاصة بك). يمكن اكتشاف كل هذه التفاصيل بسهولة إذا اتبعت الوثائق بدقة.
استدعاء واجهات برمجة التطبيقات (APIs) في الواجهة الأمامية
ستكون جميع واجهات برمجة التطبيقات متاحة على localhost:5000 محليًا، تمامًا كما قمنا بتهيئتها في server.js. عند النشر إلى Heroku، سيستخدم الخادم المنفذ الذي توفره البيئة (process.env.PORT).
لتسهيل الأمور، يسمح لنا React بتحديد وكيل (proxy) سيتم إرسال الطلبات إليه. افتح ملف package.json وقبل القوس المعقوف الأخير مباشرة، أضف ما يلي:
"proxy": "http://localhost:5000"
بهذه الطريقة، يمكننا إرسال الطلبات مباشرة إلى api/users. وعندما يتم نشر موقعنا وبناؤه، سيتم استخدام المنفذ الافتراضي لتطبيقنا مع نفس واجهة برمجة التطبيقات.
افتح ملف App.js لتطبيق React وأضف الكود التالي:
import React, { useState, useEffect } from 'react'
import axios from 'axios';
const App = function () {
const [users, setUsers] = useState(null);
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
useEffect(() => {
axios
.get("/api/users")
.then((users) => setUsers(users))
.catch((err) => console.log(err));
}, []);
function submitForm() {
if (username === "") {
alert("Please fill the username field");
return;
}
if (email === "") {
alert("Please fill the email field");
return;
}
axios
.post("/api/users", {
username: username,
email: email,
})
.then(function () {
alert("Account created successfully");
window.location.reload();
})
.catch(function () {
alert("Could not creat account. Please try again");
});
}
return (
<>
<h1>My Project</h1>
{users === null ? (
<p>Loading...</p>
) : users.length === 0 ? (
<p>No user available</p>
) : (
<>
<h2>Available Users</h2>
<ol>
{users.map((user, index) => (
<li key={index}>Name: {user.name} - Email: {user.email}</li>
))}
</ol>
</>
)}
<form onSubmit={submitForm}>
<input onChange={(e) => setUsername(e.target.value)} type="text" placeholder="Enter your username" />
<input onChange={(e) => setEmail(e.target.value)} type="text" placeholder="Enter your email address" />
<input type="submit" />
</form>
</>
);
};
export default App
تُستخدم خطافات useState و useEffect للتعامل مع الحالة (state) والآثار الجانبية (sideEffects). ما يحدث أساسًا هو أن الحالة الأولية للمستخدمين هي null ويتم عرض رسالة ‘Loading…’ في المتصفح. في useEffect، يتم استخدام [] لتحديد أنه في مرحلة componentDidMount (عند تحميل المكون)، يتم إرسال طلب Axios إلى API الذي يعمل على localhost:5000. إذا تم الحصول على النتيجة ولم يكن هناك مستخدمون، يتم عرض ‘No user available’. وإلا، يتم عرض قائمة مرقمة بالمستخدمين.
باستخدام النموذج المتاح، يمكن إرسال طلب POST لإضافة مستخدم جديد. يتم التحكم في حالة المدخلات وإرسالها إلى API على localhost:5000 عند الإرسال. بعد ذلك، يتم تحديث الصفحة وعرض المستخدم الجديد.
نشر التطبيق على Heroku
لنشر تطبيقك على Heroku، يجب أن يكون لديك حساب Heroku. انتقل إلى صفحتهم لإنشاء حساب. ثم، راجع وثائقهم حول كيفية إنشاء تطبيق Heroku، وكذلك وثائق Heroku CLI (واجهة سطر الأوامر).
إنشاء تطبيق Heroku
أولاً، قم بتسجيل الدخول إلى Heroku من خلال سطر الأوامر:
heroku login
سيقوم هذا الأمر بإعادة توجيهك إلى عنوان URL في المتصفح حيث يمكنك تسجيل الدخول. بمجرد الانتهاء، يمكنك المتابعة في الطرفية. في نفس دليل مشروع React، قم بتشغيل ما يلي:
heroku create
سيؤدي هذا إلى إنشاء تطبيق Heroku وسيزودك أيضًا بعنوان URL للوصول إلى التطبيق.
تهيئة ملف package.json
يستخدم Heroku ملف package.json الخاص بك لمعرفة أي السكربتات يجب تشغيلها وأي الاعتمادات يجب تثبيتها لكي يعمل مشروعك بنجاح. في ملف package.json الخاص بك، أضف ما يلي:
{
...
"scripts": {
...
"start": "node backend/server.js",
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client"
},
...
"engines": {
"node": "14.x"
}
}
في هذا التكوين، قمنا بتعديل heroku-postbuild ليعمل بشكل صحيح مع بنية مشروع MERN حيث الواجهة الأمامية (client) والواجهة الخلفية (backend) في مجلدات منفصلة. يقوم Heroku بتشغيل عملية ما بعد البناء (heroku-postbuild)، والتي تقوم بتثبيت الاعتمادات وتشغيل بناء مشروع React الخاص بك. ثم يبدأ مشروعك باستخدام سكربت start الذي يقوم أساسًا بتشغيل خادم الواجهة الخلفية. بعد ذلك، يجب أن يعمل مشروعك بشكل جيد. تحدد engines إصدارات المحركات مثل node و npm التي سيتم تثبيتها. لقد قمت بتحديث إصدار node إلى 14.x ليتوافق مع الإصدارات الحديثة.
الدفع إلى Heroku
git push heroku master
يقوم هذا الأمر بدفع الكود الخاص بك إلى Heroku. تذكر تضمين الملفات غير الضرورية في ملف .gitignore لتجنب دفعها. بعد بضع ثوانٍ، سيكون موقعك جاهزًا. إذا كانت هناك أي أخطاء، يمكنك التحقق من الطرفية أو الانتقال إلى لوحة التحكم الخاصة بك في المتصفح لعرض سجلات البناء. الآن يمكنك معاينة موقعك على عنوان URL الذي أرسله Heroku عند تشغيل heroku create.
الخلاصة التقنية
لقد قمنا بتغطية الخطوات الأساسية والضرورية لنشر تطبيق MERN Stack بنجاح على منصة Heroku، مع الاستفادة من قوة وأمان MongoDB Atlas كقاعدة بيانات سحابية. هذه العملية تتجاوز مجرد تشغيل التطبيق محليًا، حيث تتطلب فهمًا لكيفية تفاعل المكونات المختلفة (الواجهة الأمامية، الواجهة الخلفية، قاعدة البيانات) في بيئة إنتاج سحابية. إن استخدام Heroku يبسط بشكل كبير عملية النشر بفضل ميزاته مثل buildpacks وتكوين البيئة عبر package.json. بينما يضمن MongoDB Atlas قابلية التوسع والمرونة والأمان اللازمين لأي تطبيق حديث. على الرغم من أن هذا الدليل لم يتعمق في جوانب متقدمة مثل المصادقة أو إدارة الجلسات، إلا أنه يوفر أساسًا متينًا لأي مطور يرغب في إطلاق تطبيق MERN الخاص به في العالم الحقيقي.