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": "[email protected]",
"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:[email protected]/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: "[email protected]",
age: 25,
});
// عدة مستندات
const users = await User.insertMany([
{ name: "أحمد", email: "[email protected]" },
{ name: "محمد", email: "[email protected]" },
]);
```
### 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: "[email protected]" });
// عدد المستندات
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: "[email protected]" },
{ $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: "[email protected]" });
// حذف متعدد
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. أتقنها جيداً.