MongoDB مع Node.js — دليل شامل 2026
دليل عملي مفصل لاستخدام MongoDB مع Node.js — Mongoose، Schema، CRUD، العلاقات، مع تمارين وحلول
في المقال السابق، بنيت REST API مع تخزين في الذاكرة. الآن سنتعلم MongoDB — قاعدة البيانات الحقيقية التي ستحفظ بياناتك بشكل دائم.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لاستخدام MongoDB مع Node.js، مع تمارين وحلول.
ما هو MongoDB؟
MongoDB هي قاعدة بيانات NoSQL تُخزّن البيانات في مستندات (Documents) بدلاً من جداول.
تشبيه بسيط:
- SQL (MySQL, PostgreSQL): جداول، صفوف، أعمدة.
- MongoDB: مجموعات (Collections)، مستندات (Documents).
مثال مستند:
{
"_id": "507f1f77bcf86cd799439011",
"name": "أحمد",
"email": "[email protected]",
"age": 25,
"createdAt": "2026-11-09T10:00:00.000Z"
}
لماذا MongoDB؟
قبل أن نبدأ، دعنا نتفق على الأسباب:
- مرنة: لا تحتاج Schema مسبق.
- سريعة: أداء عالٍ.
- JSON أصلي: لا تحويلات معقدة.
- قابلة للتوسع: تعمل مع البيانات الضخمة.
- شائعة: في MEAN/MERN stacks.
- مجانية: مفتوحة المصدر.
SQL vs MongoDB
| المعيار | SQL | MongoDB |
|---|---|---|
| النوع | Relational (جداول) | Document (مستندات) |
| Schema | محدد مسبقاً | مرن |
| الاستعلام | SQL | JavaScript Objects |
| العلاقات | JOINs | Embedding / Referencing |
| التوسع | عمودي (Vertical) | أفقي (Horizontal) |
MongoDB Atlas (الطريقة السحابية)
الخطوة 1: إنشاء حساب
- اذهب إلى mongodb.com/atlas
- أنشئ حساباً مجانياً.
- اختر Free Tier (M0 Cluster).
الخطوة 2: إنشاء Cluster
- اختر Shared (مجاني).
- اختر منطقة قريبة (Frankfurt, Ireland).
- انتظر 1-3 دقائق.
الخطوة 3: إعداد المستخدم
- Database Access → Add New Database User.
- اختر Username & Password.
- احفظ اسم المستخدم وكلمة المرور.
الخطوة 4: السماح بالاتصال
- Network Access → Add IP Address.
- اختر Allow Access from Anywhere (للتطوير فقط).
- اضغط Confirm.
الخطوة 5: الحصول على Connection String
- Clusters → Connect.
- اختر Connect your application.
- انسخ الرابط:
mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority
تثبيت Mongoose
Mongoose هي ODM (Object Data Modeling) لـ MongoDB.
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:
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:
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:
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
| النوع | الوصف | مثال |
|---|---|---|
String | نص | "أحمد" |
Number | رقم | 25 |
Boolean | منطقي | true |
Date | تاريخ | new Date() |
Array | مصفوفة | [1, 2, 3] |
ObjectId | معرف مرجعي | ref: "User" |
CRUD مع Mongoose
1. إنشاء (Create)
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)
// كل المستخدمين
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)
// بالمعرف
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)
// بالمعرف
await User.findByIdAndDelete("507f1f77bcf86cd799439011");
// بشرط
await User.findOneAndDelete({ email: "[email protected]" });
// حذف متعدد
await User.deleteMany({ isActive: false });
عوامل الاستعلام (Query Operators)
| العامل | المعنى | مثال |
|---|---|---|
$eq | يساوي | { age: { $eq: 25 } } |
$ne | لا يساوي | { age: { $ne: 25 } } |
$gt | أكبر من | { age: { $gt: 18 } } |
$gte | أكبر أو يساوي | { age: { $gte: 18 } } |
$lt | أصغر من | { age: { $lt: 65 } } |
$in | ضمن قائمة | { role: { $in: ["admin"] } } |
$or | أو | { $or: [{ a: 1 }, { b: 2 }] } |
$and | و | { $and: [{ a: 1 }, { b: 2 }] } |
$regex | تعبير نمطي | { name: { $regex: "أحمد" } } |
Middleware في Mongoose
// قبل الحفظ
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
userSchema.virtual("fullInfo").get(function () {
return `${this.name} (${this.email})`;
});
تحديث Controllers لاستخدام Mongoose
src/controllers/userController.js:
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:
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:
const posts = await Post.find().populate("author", "name email");
2. Embedding (تضمين)
const userSchema = new mongoose.Schema({
name: String,
address: {
street: String,
city: String,
country: String,
},
});
متى تستخدم:
- Referencing: للبيانات الكبيرة المشتركة.
- Embedding: للبيانات الصغيرة المرتبطة.
تمارين عملية
تمرين 1: الاتصال
اتصل بـ MongoDB Atlas.
الحل:
const mongoose = require("mongoose");
async function connectDB() {
await mongoose.connect(process.env.MONGODB_URI);
console.log("✅ متصل");
}
connectDB();
تمرين 2: Schema
أنشئ Schema للمنتجات.
الحل:
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
أضف منتجاً جديداً.
الحل:
const product = await Product.create({
name: "لابتوب",
price: 5000,
category: "electronics",
});
تمرين 4: Read
اجلب كل المنتجات.
الحل:
const products = await Product.find();
const electronics = await Product.find({ category: "electronics" });
تمرين 5: Update
حدّث سعر منتج.
الحل:
const updated = await Product.findByIdAndUpdate(
id,
{ price: 4500 },
{ new: true }
);
تمرين 6: Delete
احذف منتجاً.
الحل:
await Product.findByIdAndDelete(id);
تمرين 7: العلاقات
أنشئ علاقة بين Post و User.
الحل:
// في 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.
الحل:
if (error.code === 11000) {
return next(new ApiError(400, "البريد مستخدم"));
}
🔴 المشكلة 4: CastError
السبب: ID غير صالح.
الحل:
if (!mongoose.Types.ObjectId.isValid(id)) {
return next(new ApiError(400, "معرف غير صالح"));
}
🔴 المشكلة 5: ValidationError
السبب: البيانات لا تطابق Schema.
الحل: افحص رسالة الخطأ:
catch (error) {
if (error.name === "ValidationError") {
const messages = Object.values(error.errors).map((e) => e.message);
return next(new ApiError(400, messages.join(", ")));
}
}
جدول دوال Mongoose
| الدالة | الوظيفة |
|---|---|
Model.create() | إنشاء مستند |
Model.find() | جلب الكل |
Model.findById() | جلب بالمعرف |
Model.findOne() | جلب أول مطابق |
Model.findByIdAndUpdate() | تحديث بالمعرف |
Model.findByIdAndDelete() | حذف بالمعرف |
Model.deleteMany() | حذف متعدد |
Model.countDocuments() | عدد المستندات |
.populate() | جلب العلاقات |
قائمة تحقق نهائية
| المهمة | الحالة |
|---|---|
| إنشاء MongoDB Atlas | ⬜ |
| تثبيت Mongoose | ⬜ |
| الاتصال بقاعدة البيانات | ⬜ |
| تعريف Schema | ⬜ |
| CRUD مع Mongoose | ⬜ |
| العلاقات (populate) | ⬜ |
| حل التمارين الثمانية | ⬜ |
ماذا بعد هذا المقال؟
الآن بعد أن أتقنت MongoDB، أنت جاهز للمقال الأخير:
- مشروع متكامل — API كامل مع مصادقة.
الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هو MongoDB.
- ✅ MongoDB Atlas (السحابي).
- ✅ Mongoose و Schema.
- ✅ CRUD مع Mongoose.
- ✅ عوامل الاستعلام.
- ✅ Middleware في Mongoose.
- ✅ العلاقات (Referencing, Embedding).
- ✅ حل 8 تمارين عملية.
تذكر: MongoDB هي قاعدة البيانات الأكثر استخداماً مع Node.js. أتقنها جيداً.
بناء REST API في Node.js — دليل شامل 2026
مشروع Node.js متكامل — API مع مصادقة JWT 2026
📚 مقالات ذات صلة
مشروع Node.js متكامل — API مع مصادقة JWT 2026
مشروع عملي شامل لبناء API متكامل بـ Node.js و Express و MongoDB — مع مصادقة JWT، رفع الملفات، والنشر
بناء REST API في Node.js — دليل شامل 2026
دليل عملي مفصل لبناء REST API احترافي — المبادئ، التنظيم، التحقق، والاختبار، مع تمارين وحلول
التوجيه و Middleware في Express.js — دليل شامل 2026
دليل عملي مفصل للتوجيه (Routing) و Middleware في Express.js — Router، تنظيم المسارات، مع تمارين وحلول