Node.js

MongoDB مع Node.js — دليل شامل 2026

📅 2026-11-09⏱ 10 دقائق قراءة
في المقال السابق، بنيت REST API مع تخزين في الذاكرة. الآن سنتعلم **MongoDB** — قاعدة البيانات الحقيقية التي ستحفظ بياناتك بشكل دائم. في هذا الدليل العملي، سنأخذك خطوة بخطوة لاستخدام MongoDB مع Node.js، مع تمارين وحلول. ## ما هو MongoDB؟ **MongoDB** هي **قاعدة بيانات NoSQL** تُخزّن البيانات في **مستندات** (Documents) بدلاً من جداول. **تشبيه بسيط:** - **SQL (MySQL, PostgreSQL):** جداول، صفوف، أعمدة. - **MongoDB:** مجموعات (Collections)، مستندات (Documents). **مثال مستند:** ```json { "_id": "507f1f77bcf86cd799439011", "name": "أحمد", "email": "ahmed@example.com", "age": 25, "createdAt": "2026-11-09T10:00:00.000Z" } ``` ## لماذا MongoDB؟ قبل أن نبدأ، دعنا نتفق على الأسباب: - **مرنة:** لا تحتاج Schema مسبق. - **سريعة:** أداء عالٍ. - **JSON أصلي:** لا تحويلات معقدة. - **قابلة للتوسع:** تعمل مع البيانات الضخمة. - **شائعة:** في MEAN/MERN stacks. - **مجانية:** مفتوحة المصدر. ## SQL vs MongoDB <table> <thead> <tr> <th>المعيار</th> <th>SQL</th> <th>MongoDB</th> </tr> </thead> <tbody> <tr> <td><strong>النوع</strong></td> <td>Relational (جداول)</td> <td>Document (مستندات)</td> </tr> <tr> <td><strong>Schema</strong></td> <td>محدد مسبقاً</td> <td>مرن</td> </tr> <tr> <td><strong>الاستعلام</strong></td> <td>SQL</td> <td>JavaScript Objects</td> </tr> <tr> <td><strong>العلاقات</strong></td> <td>JOINs</td> <td>Embedding / Referencing</td> </tr> <tr> <td><strong>التوسع</strong></td> <td>عمودي (Vertical)</td> <td>أفقي (Horizontal)</td> </tr> </tbody> </table> ## MongoDB Atlas (الطريقة السحابية) ### الخطوة 1: إنشاء حساب 1. اذهب إلى [mongodb.com/atlas](https://www.mongodb.com/atlas) 2. أنشئ حساباً مجانياً. 3. اختر **Free Tier** (M0 Cluster). ### الخطوة 2: إنشاء Cluster 1. اختر **Shared** (مجاني). 2. اختر منطقة قريبة (Frankfurt, Ireland). 3. انتظر 1-3 دقائق. ### الخطوة 3: إعداد المستخدم 1. **Database Access** → **Add New Database User**. 2. اختر **Username & Password**. 3. احفظ اسم المستخدم وكلمة المرور. ### الخطوة 4: السماح بالاتصال 1. **Network Access** → **Add IP Address**. 2. اختر **Allow Access from Anywhere** (للتطوير فقط). 3. اضغط **Confirm**. ### الخطوة 5: الحصول على Connection String 1. **Clusters** → **Connect**. 2. اختر **Connect your application**. 3. انسخ الرابط: ``` mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority ``` ## تثبيت Mongoose **Mongoose** هي **ODM** (Object Data Modeling) لـ MongoDB. ```bash npm install mongoose ``` **الفرق:** - **mongodb:** Driver الرسمي (أقل مستوى). - **Mongoose:** مكتبة أعلى مستوى مع Schema و Validation. ## الاتصال بـ MongoDB **`.env`:** ``` MONGODB_URI=mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net/mydb?retryWrites=true&w=majority ``` **`src/config/database.js`:** ```javascript const mongoose = require("mongoose"); async function connectDB() { try { const conn = await mongoose.connect(process.env.MONGODB_URI); console.log(`✅ MongoDB متصل: ${conn.connection.host}`); } catch (error) { console.error(`❌ خطأ MongoDB: ${error.message}`); process.exit(1); } } module.exports = connectDB; ``` **`server.js`:** ```javascript require("dotenv").config(); const connectDB = require("./src/config/database"); const app = require("./src/app"); const PORT = process.env.PORT || 3000; // الاتصال بقاعدة البيانات connectDB().then(() => { app.listen(PORT, () => { console.log(`🚀 السيرفر على http://localhost:${PORT}`); }); }); ``` ## Schema و Model ### 1. تعريف Schema **`src/models/User.js`:** ```javascript const mongoose = require("mongoose"); const userSchema = new mongoose.Schema( { name: { type: String, required: [true, "الاسم مطلوب"], trim: true, minlength: [2, "الاسم قصير جداً"], maxlength: [50, "الاسم طويل جداً"], }, email: { type: String, required: [true, "البريد مطلوب"], unique: true, lowercase: true, trim: true, match: [/^\S+@\S+\.\S+$/, "البريد غير صحيح"], }, age: { type: Number, min: [18, "العمر يجب أن يكون 18 أو أكثر"], max: [100, "العمر كبير جداً"], }, role: { type: String, enum: ["user", "admin"], default: "user", }, isActive: { type: Boolean, default: true, }, }, { timestamps: true, // يضيف createdAt و updatedAt } ); module.exports = mongoose.model("User", userSchema); ``` ### 2. أنواع البيانات في Schema <table> <thead> <tr> <th>النوع</th> <th>الوصف</th> <th>مثال</th> </tr> </thead> <tbody> <tr> <td><code>String</code></td> <td>نص</td> <td><code>"أحمد"</code></td> </tr> <tr> <td><code>Number</code></td> <td>رقم</td> <td><code>25</code></td> </tr> <tr> <td><code>Boolean</code></td> <td>منطقي</td> <td><code>true</code></td> </tr> <tr> <td><code>Date</code></td> <td>تاريخ</td> <td><code>new Date()</code></td> </tr> <tr> <td><code>Array</code></td> <td>مصفوفة</td> <td><code>[1, 2, 3]</code></td> </tr> <tr> <td><code>ObjectId</code></td> <td>معرف مرجعي</td> <td><code>ref: "User"</code></td> </tr> </tbody> </table> ## CRUD مع Mongoose ### 1. إنشاء (Create) ```javascript const User = require("../models/User"); // مستند واحد const user = await User.create({ name: "أحمد", email: "ahmed@example.com", age: 25, }); // عدة مستندات const users = await User.insertMany([ { name: "أحمد", email: "a@x.com" }, { name: "محمد", email: "m@x.com" }, ]); ``` ### 2. قراءة (Read) ```javascript // كل المستخدمين const users = await User.find(); // مع شرط const adults = await User.find({ age: { $gte: 18 } }); // مستند واحد بالمعرف const user = await User.findById("507f1f77bcf86cd799439011"); // أول مستند يطابق const user = await User.findOne({ email: "ahmed@example.com" }); // عدد المستندات const count = await User.countDocuments(); // مع Select (حقول محددة) const users = await User.find().select("name email"); // مع Sort const users = await User.find().sort({ createdAt: -1 }); // مع Limit و Skip (Pagination) const users = await User.find().limit(10).skip(20); ``` ### 3. تحديث (Update) ```javascript // بالمعرف const user = await User.findByIdAndUpdate( "507f1f77bcf86cd799439011", { name: "أحمد محمد" }, { new: true, runValidators: true } // ← يرجع المحدث ويفحص ); // بـ findOneAndUpdate const user = await User.findOneAndUpdate( { email: "ahmed@example.com" }, { $inc: { age: 1 } }, // زيادة العمر { new: true } ); // updateMany await User.updateMany( { role: "user" }, { $set: { isActive: true } } ); ``` ### 4. حذف (Delete) ```javascript // بالمعرف await User.findByIdAndDelete("507f1f77bcf86cd799439011"); // بشرط await User.findOneAndDelete({ email: "ahmed@example.com" }); // حذف متعدد await User.deleteMany({ isActive: false }); ``` ## عوامل الاستعلام (Query Operators) <table> <thead> <tr> <th>العامل</th> <th>المعنى</th> <th>مثال</th> </tr> </thead> <tbody> <tr> <td><code>$eq</code></td> <td>يساوي</td> <td><code>{ age: { $eq: 25 } }</code></td> </tr> <tr> <td><code>$ne</code></td> <td>لا يساوي</td> <td><code>{ age: { $ne: 25 } }</code></td> </tr> <tr> <td><code>$gt</code></td> <td>أكبر من</td> <td><code>{ age: { $gt: 18 } }</code></td> </tr> <tr> <td><code>$gte</code></td> <td>أكبر أو يساوي</td> <td><code>{ age: { $gte: 18 } }</code></td> </tr> <tr> <td><code>$lt</code></td> <td>أصغر من</td> <td><code>{ age: { $lt: 65 } }</code></td> </tr> <tr> <td><code>$in</code></td> <td>ضمن قائمة</td> <td><code>{ role: { $in: ["admin"] } }</code></td> </tr> <tr> <td><code>$or</code></td> <td>أو</td> <td><code>{ $or: [{ a: 1 }, { b: 2 }] }</code></td> </tr> <tr> <td><code>$and</code></td> <td>و</td> <td><code>{ $and: [{ a: 1 }, { b: 2 }] }</code></td> </tr> <tr> <td><code>$regex</code></td> <td>تعبير نمطي</td> <td><code>{ name: { $regex: "أحمد" } }</code></td> </tr> </tbody> </table> ## Middleware في Mongoose ```javascript // قبل الحفظ userSchema.pre("save", async function (next) { if (!this.isModified("password")) return next(); this.password = await bcrypt.hash(this.password, 10); next(); }); // بعد الحفظ userSchema.post("save", function (doc) { console.log("تم إنشاء مستخدم:", doc._id); }); // Method مخصص userSchema.methods.comparePassword = async function (password) { return bcrypt.compare(password, this.password); }; ``` ## Virtuals ```javascript userSchema.virtual("fullInfo").get(function () { return `${this.name} (${this.email})`; }); ``` ## تحديث Controllers لاستخدام Mongoose **`src/controllers/userController.js`:** ```javascript const User = require("../models/User"); const ApiError = require("../utils/ApiError"); const ApiResponse = require("../utils/ApiResponse"); // جلب كل المستخدمين exports.getAllUsers = async (req, res, next) => { try { const users = await User.find().select("-__v"); res.json(new ApiResponse(200, users)); } catch (error) { next(error); } }; // جلب مستخدم واحد exports.getUserById = async (req, res, next) => { try { const user = await User.findById(req.params.id); if (!user) return next(new ApiError(404, "المستخدم غير موجود")); res.json(new ApiResponse(200, user)); } catch (error) { next(error); } }; // إضافة مستخدم exports.createUser = async (req, res, next) => { try { const user = await User.create(req.body); res.status(201).json(new ApiResponse(201, user, "تم الإنشاء")); } catch (error) { // معالجة خطأ التكرار if (error.code === 11000) { return next(new ApiError(400, "البريد مستخدم بالفعل")); } next(error); } }; // تحديث مستخدم exports.updateUser = async (req, res, next) => { try { const user = await User.findByIdAndUpdate( req.params.id, req.body, { new: true, runValidators: true } ); if (!user) return next(new ApiError(404, "المستخدم غير موجود")); res.json(new ApiResponse(200, user, "تم التحديث")); } catch (error) { next(error); } }; // حذف مستخدم exports.deleteUser = async (req, res, next) => { try { const user = await User.findByIdAndDelete(req.params.id); if (!user) return next(new ApiError(404, "المستخدم غير موجود")); res.json(new ApiResponse(200, null, "تم الحذف")); } catch (error) { next(error); } }; ``` ## العلاقات (Relationships) ### 1. Referencing (مرجع) **`src/models/Post.js`:** ```javascript const mongoose = require("mongoose"); const postSchema = new mongoose.Schema( { title: { type: String, required: true }, content: { type: String, required: true }, author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, }, }, { timestamps: true } ); module.exports = mongoose.model("Post", postSchema); ``` **الاستعلام مع populate:** ```javascript const posts = await Post.find().populate("author", "name email"); ``` ### 2. Embedding (تضمين) ```javascript const userSchema = new mongoose.Schema({ name: String, address: { street: String, city: String, country: String, }, }); ``` **متى تستخدم:** - **Referencing:** للبيانات الكبيرة المشتركة. - **Embedding:** للبيانات الصغيرة المرتبطة. ## تمارين عملية ### تمرين 1: الاتصال اتصل بـ MongoDB Atlas. **الحل:** ```javascript const mongoose = require("mongoose"); async function connectDB() { await mongoose.connect(process.env.MONGODB_URI); console.log("✅ متصل"); } connectDB(); ``` ### تمرين 2: Schema أنشئ Schema للمنتجات. **الحل:** ```javascript const productSchema = new mongoose.Schema( { name: { type: String, required: true, minlength: 2 }, price: { type: Number, required: true, min: 0 }, description: String, category: { type: String, enum: ["electronics", "clothes", "books"] }, inStock: { type: Boolean, default: true }, }, { timestamps: true } ); module.exports = mongoose.model("Product", productSchema); ``` ### تمرين 3: Create أضف منتجاً جديداً. **الحل:** ```javascript const product = await Product.create({ name: "لابتوب", price: 5000, category: "electronics", }); ``` ### تمرين 4: Read اجلب كل المنتجات. **الحل:** ```javascript const products = await Product.find(); const electronics = await Product.find({ category: "electronics" }); ``` ### تمرين 5: Update حدّث سعر منتج. **الحل:** ```javascript const updated = await Product.findByIdAndUpdate( id, { price: 4500 }, { new: true } ); ``` ### تمرين 6: Delete احذف منتجاً. **الحل:** ```javascript await Product.findByIdAndDelete(id); ``` ### تمرين 7: العلاقات أنشئ علاقة بين Post و User. **الحل:** ```javascript // في Post.js author: { type: mongoose.Schema.Types.ObjectId, ref: "User" } // الاستعلام const posts = await Post.find().populate("author", "name email"); ``` ### تمرين 8: API كامل ابنِ API كامل مع MongoDB. **الحل:** (راجع المثال الكامل أعلاه) ## حل المشاكل الشائعة ### 🔴 المشكلة 1: `MongooseServerSelectionError` **السبب:** IP غير مسموح. **الحل:** أضف IP في **Network Access** بـ MongoDB Atlas. ### 🔴 المشكلة 2: `Authentication failed` **السبب:** اسم المستخدم أو كلمة المرور خاطئة. **الحل:** تحقق من `MONGODB_URI`، واستبدل `<password>` بكلمة مرورك. ### 🔴 المشكلة 3: `E11000 duplicate key error` **السبب:** انتهاك `unique`. **الحل:** ```javascript if (error.code === 11000) { return next(new ApiError(400, "البريد مستخدم")); } ``` ### 🔴 المشكلة 4: `CastError` **السبب:** ID غير صالح. **الحل:** ```javascript if (!mongoose.Types.ObjectId.isValid(id)) { return next(new ApiError(400, "معرف غير صالح")); } ``` ### 🔴 المشكلة 5: `ValidationError` **السبب:** البيانات لا تطابق Schema. **الحل:** افحص رسالة الخطأ: ```javascript catch (error) { if (error.name === "ValidationError") { const messages = Object.values(error.errors).map((e) => e.message); return next(new ApiError(400, messages.join(", "))); } } ``` ## جدول دوال Mongoose <table> <thead> <tr> <th>الدالة</th> <th>الوظيفة</th> </tr> </thead> <tbody> <tr> <td><code>Model.create()</code></td> <td>إنشاء مستند</td> </tr> <tr> <td><code>Model.find()</code></td> <td>جلب الكل</td> </tr> <tr> <td><code>Model.findById()</code></td> <td>جلب بالمعرف</td> </tr> <tr> <td><code>Model.findOne()</code></td> <td>جلب أول مطابق</td> </tr> <tr> <td><code>Model.findByIdAndUpdate()</code></td> <td>تحديث بالمعرف</td> </tr> <tr> <td><code>Model.findByIdAndDelete()</code></td> <td>حذف بالمعرف</td> </tr> <tr> <td><code>Model.deleteMany()</code></td> <td>حذف متعدد</td> </tr> <tr> <td><code>Model.countDocuments()</code></td> <td>عدد المستندات</td> </tr> <tr> <td><code>.populate()</code></td> <td>جلب العلاقات</td> </tr> </tbody> </table> ## قائمة تحقق نهائية <table> <thead> <tr> <th>المهمة</th> <th>الحالة</th> </tr> </thead> <tbody> <tr> <td>إنشاء MongoDB Atlas</td> <td>⬜</td> </tr> <tr> <td>تثبيت Mongoose</td> <td>⬜</td> </tr> <tr> <td>الاتصال بقاعدة البيانات</td> <td>⬜</td> </tr> <tr> <td>تعريف Schema</td> <td>⬜</td> </tr> <tr> <td>CRUD مع Mongoose</td> <td>⬜</td> </tr> <tr> <td>العلاقات (populate)</td> <td>⬜</td> </tr> <tr> <td>حل التمارين الثمانية</td> <td>⬜</td> </tr> </tbody> </table> ## ماذا بعد هذا المقال؟ الآن بعد أن أتقنت MongoDB، أنت جاهز للمقال الأخير: 1. **مشروع متكامل** — API كامل مع مصادقة. ## الخلاصة في هذا المقال، تعلمت: - ✅ ما هو MongoDB. - ✅ MongoDB Atlas (السحابي). - ✅ Mongoose و Schema. - ✅ CRUD مع Mongoose. - ✅ عوامل الاستعلام. - ✅ Middleware في Mongoose. - ✅ العلاقات (Referencing, Embedding). - ✅ حل 8 تمارين عملية. **تذكر:** MongoDB هي **قاعدة البيانات الأكثر استخداماً** مع Node.js. أتقنها جيداً.