Node.js: Mongoose ODM

آخر تحديث: 2026-08-26

Bob’s data validation code, written using the native MongoDB driver, was scattered across various routes—the registration endpoint checked email format, the publish-article endpoint validated タイトル length, and the change-password endpoint verified that the new and old passwords were different. Every time a new フィールド was added, he had to add a piece of if (!フィールド) logic to the corresponding controller, resulting in the same email validation regex being repeated across three routes. After switching to Mongoose, Bob centralized all validation rules in the schema definition—defining them once and applying them everywhere—which reduced the controller code by 60%.

ستتعلم:


1. البنية الأساسية لـ Mongoose

Mongoose هي مكتبة لتعيين الكائنات إلى المستندات (ODM) مخصصة لـ MongoDB، توفر طبقة لنمذجة البيانات تعتمد على المخطط فوق برنامج التشغيل الأصلي. وينقسم المفهوم الأساسي إلى ثلاث طبقات: المخطط يحدد البنية → يتم ترجمة النموذج إلى مُنشئ → المستند هو مثيل للنموذج.

▶ مثال:(1) العلاقة بين المخطط → النموذج → المستند

100%
graph LR
    A["Schema<br/>Defining Structures and Verification"] -->|mongoose.model() Compilation| B["Model<br/>Constructor + Query Interface"]
    B -->|new Model() Instantiation| C["Document<br/>Examples of Verified Documents"]
    C -->|.save() Persistence| D[("MongoDB<br/>Gathering")]
    B -->|Model.find() etc.| D
    D -->|Back| C

(2) التركيب والتوصيل

▶ مثال: تثبيت Mongoose والاتصال بـ MongoDB

BASH
npm install mongoose
JAVASCRIPT
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/myapp')
  .then(() => console.log('MongoDB connected'))
  .catch(err => console.error('Connection error:', err));

(3) المخطط والنموذج البسيطان

▶ مثال: تعريف مخطط المستخدم وإنشاء نموذج

JAVASCRIPT
const userSchema = new mongoose.Schema({
  name: String,
  email: String,
  age: Number
});

const User = mongoose.model('User', userSchema);
▶ جرّب الكود
المفهوم الدور التشبيه
المخطط المخطط التفصيلي / التعريف الهيكلي الرسومات المعمارية
النموذج المُصمم + واجهة تشغيل قاعدة البيانات فريق البناء
الوثيقة أمثلة على الوثائق التي تم التحقق منها المنازل المكتملة


2. أنواع حقول المخطط

يوفر Mongoose تعيينات أنواع غنية لكل حقل، وهو ما يتجاوز بكثير النقص في قيود الأنواع الموجودة في برنامج التشغيل الأصلي.

(1) مرجع سريع لأنواع الحقول

نوع Mongoose النوع المقابل في JavaScript مثال الوصف
String سلسلة name: String القص التلقائي (يتطلب التهيئة)
Number الرقم age: Number يدعم الحد الأدنى / الحد الأقصى
Boolean منطقية active: Boolean يتم تحويلها تلقائيًا إلى 0/1/"true"
Date التاريخ createdAt: Date طرق التاريخ المدمجة
ObjectId معرّف الكائن author: mongoose.Schema.Types.ObjectId المراجع إلى الوثائق الأخرى
Array مصفوفة tags: [String] مصفوفة من المستندات الفرعية أو مصفوفة من الأنواع
Mixed كائن meta: mongoose.Schema.Types.Mixed أي نوع، بدون تحقق من الصحة
Buffer المخزن المؤقت avatar: Buffer البيانات الثنائية
Map Map prefs: { type: Map, of: String } ES6 Map structure
Decimal128 Decimal128 price: mongoose.Schema.Types.Decimal128 High-precision decimal

(2) The complete syntax for field definitions

▶ Example: Detailed Explanation of Field Options

JAVASCRIPT
const productSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'The product name cannot be left blank.'],
    trim: true,
    minlength: 2,
    maxlength: 100
  },
  price: {
    type: Number,
    required: true,
    min: [0, 'Prices cannot be negative.'],
    default: 0
  },
  category: {
    type: String,
    enum: ['electronics', 'books', 'clothing', 'food'],
    lowercase: true
  },
  tags: [String],
  metadata: {
    type: mongoose.Schema.Types.Mixed,
    default: {}
  }
});
▶ جرّب الكود

3. Validator

Validation is at the heart of Mongoose—it frees data validation from controllers and centralizes it in schema declarations.

(1) Validator Quick Reference

Validator Applicable Type Description Example
required All Required fields required: [true, 'Cannot be empty']
enum String Enum value constraint enum: ['A', 'B', 'C']
min Number / Date Minimum value min: 0
max الرقم / التاريخ القيمة القصوى max: 150
minlength سلسلة الحد الأدنى للطول minlength: 6
maxlength سلسلة الطول الأقصى maxlength: 200
match سلسلة مطابقة التعبير النمطي match: [/^\S+@\S+\.\S+$/, 'Invalid email format']
validate الكل وظائف التحقق المخصصة validate: v => v > 0

(2) أدوات التحقق المخصصة

▶ مثال: أدوات التحقق المخصصة ورسائل الخطأ

JAVASCRIPT
const userSchema = new mongoose.Schema({
  password: {
    type: String,
    required: true,
    validate: {
      validator: function(v) {
        return /^(?=.*[A-Z])(?=.*\d).{8,}$/.test(v);
      },
      message: props => `${props.value} Password does not meet requirements: at least 8 characters, includes uppercase and digits`
    }
  },
  phone: {
    type: String,
    validate: {
      validator: function(v) {
        return /^1[3-9]\d{9}$/.test(v);
      },
      message: 'The phone number format is incorrect'
    }
  }
});
▶ جرّب الكود

(3) التحقق من توقيت الزناد

يتم تشغيل عملية التحقق تلقائيًا في الأوقات التالية: new Model().save() وModel.create(). يمكنك تشغيلها يدويًّا باستخدام الطريقة validate(). أما updateOne() / updateMany()، وما إلى ذلك، فلا تؤدي إلى تشغيل عملية التحقق تلقائيًّا؛ بل يجب عليك تكوين الخيار runValidators: true.

▶ مثال: تمكين التحقق من صحة عمليات التحديث

JAVASCRIPT
User.updateOne(
  { email: 'bob@test.com' },
  { age: -5 },
  { runValidators: true }
);
▶ جرّب الكود

4. الخصائص الافتراضية

لا يتم تخزين السمات الافتراضية في قاعدة البيانات؛ بل يتم حسابها ديناميكيًا فقط أثناء عمليات الاستعلام، مما يجعلها مناسبة للحقول المشتقة.

(1) التعريف والاستخدام

▶ مثال: السمة الافتراضية «الاسم الكامل للمستخدم»

JAVASCRIPT
const userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String,
  email: String
});

userSchema.virtual('fullName')
  .get(function() {
    return `${this.firstName} ${this.lastName}`;
  })
  .set(function(v) {
    const parts = v.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1] || '';
  });

const User = mongoose.model('User', userSchema);
const user = new User({ firstName: 'Bob', lastName: 'Smith' });
console.log(user.fullName);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Bob Smith

(2) virtual و toJSON

لا يتم تضمين السمات الافتراضية بشكل افتراضي في مخرجات toJSON() وtoObject(). ويجب تمكينها صراحةً في خيارات المخطط:

JAVASCRIPT
const userSchema = new mongoose.Schema({
  firstName: String,
  lastName: String
}, {
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});


5. الخطافات (البرامج الوسيطة)

يتم تنفيذ «الهوكات» (البرامج الوسيطة) تلقائيًا في مراحل محددة من دورة حياة المستند، وتُستخدم في المعالجة المسبقة للبيانات، والتسجيل، والعمليات المتتالية، وغيرها.

(1) أنواع الخطافات وشروط التشغيل

نوع الخطاف شروط التشغيل الاستخدامات الشائعة
pre('save') قبل الحفظ تجزئة كلمة المرور، تنسيق البيانات، طابع زمني للتحديث
post('save') بعد الحفظ إرسال الإشعارات، تسجيل المدخلات
pre('remove') قبل الحذف الحذف التسلسلي للوثائق المرتبطة
post('remove') بعد الحذف تنظيف الموارد والسجلات
pre('find') قبل الاستعلام معايير التصفية الافتراضية (مثل الحذف المؤقت)
post('find') بعد الاستعلام إخفاء هوية البيانات
pre('updateOne') قبل التحديث طابع زمني للتحديث
post('aggregate') بعد التجميع التسجيل

(2) بالنسبة إلى «هوكس»

▶ مثال: إجراء التجزئة التلقائية لكلمات المرور قبل الحفظ

JAVASCRIPT
const bcrypt = require('bcrypt');

userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 10);
  next();
});
▶ جرّب الكود

(3) ربطات المنشورات

▶ مثال: إرسال رسالة بريد إلكتروني ترحيبية بعد الحفظ

JAVASCRIPT
userSchema.post('save', function(doc, next) {
  console.log(`User ${doc.email} Saved`);
  next();
});
▶ جرّب الكود

(4) ربط الاستعلامات

▶ مثال: تصفية المستندات المحذوفة تلقائيًا أثناء إجراء الاستعلام

JAVASCRIPT
userSchema.pre('find', function() {
  this.where({ deletedAt: null });
});
▶ جرّب الكود

6. أداة إنشاء الاستعلامات

يدعم مُنشئ الاستعلامات في Mongoose الاستدعاءات المتسلسلة، وهي أكثر سهولة في الاستخدام مقارنة بالمعلمات الكائنية التي يستخدمها برنامج التشغيل الأصلي.

(1) طريقة الاستعلام المتسلسل

▶ مثال: استدعاءات متسلسلة لمنشئ الاستعلامات

JAVASCRIPT
const users = await User.find()
  .where('age').gte(18).lte(65)
  .where('role').equals('admin')
  .sort({ createdAt: -1 })
  .select('name email age')
  .limit(10)
  .skip(0);

console.log(users);
▶ جرّب الكود

(2) Comparison of Common Query Methods

تنفيذ برنامج التشغيل الأصلي أداة إنشاء الاستعلامات في Mongoose الوصف
db.users.find({ age: { $gte: 18 } }) User.find().where('age').gte(18) البحث حسب الحالة
db.users.find().sort({ name: 1 }) User.find().sort({ name: 1 }) فرز
db.users.find().limit(10) User.find().limit(10) عدد الإدخالات محدود بـ
db.users.find().skip(20) User.find().skip(20) تخطي
db.users.find({}, { name: 1 }) User.find().select('name') مرشح الحقل

(3) تغليف الاستعلامات المقسمة إلى صفحات

▶ مثال: الوظائف المساعدة للاستعلامات المقسمة إلى صفحات

JAVASCRIPT
async function paginate(Model, filter = {}, page = 1, limit = 10) {
  const skip = (page - 1) * limit;
  const [docs, total] = await Promise.all([
    Model.find(filter).skip(skip).limit(limit).sort({ createdAt: -1 }),
    Model.countDocuments(filter)
  ]);
  return {
    data: docs,
    total,
    page,
    totalPages: Math.ceil(total / limit)
  };
}

const result = await paginate(User, { role: 'user' }, 2, 10);
▶ جرّب الكود

7. الفهرس

تعد الفهارس عنصراً أساسياً في أداء استعلامات قاعدة البيانات. ويدعم Mongoose التعريف التصريحي للفهارس في المخطط.

(1) الفهارس أحادية العمود والفهارس المركبة

▶ مثال: تعريف فهرس في المخطط

JAVASCRIPT
const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true
  },
  username: {
    type: String,
    index: true
  },
  region: String,
  status: String
});

userSchema.index({ region: 1, status: 1 });
▶ جرّب الكود
نوع الفهرس التعريف الوصف
فهرس فريد unique: true يجب أن تكون قيم الحقول فريدة
الفهرس العادي index: true البحث السريع
المؤشر المركب schema.index({ a: 1, b: -1 }) المؤشر المركب متعدد المجالات
فهرس النص schema.index({ title: 'text' }) البحث عن النص الكامل

▶ مثال:(2) الإنشاء التلقائي للفهرس في بيئة التطوير

JAVASCRIPT
mongoose.connect(uri, { autoIndex: true });
▶ جرّب الكود

في بيئة الإنتاج، يُوصى بتعطيل autoIndex وإنشاء الفهارس يدويًّا باستخدام البرنامج النصي للترحيل لتجنب تأخيرات بدء التشغيل.



8. طرق المثيل والطرق الثابتة

تتيح لك Mongoose توسيع نطاق المخطط باستخدام طرق مخصصة، والتي تنقسم إلى فئتين: طرق المثيل والطرق الثابتة.

(1) طرق الكائنات

تعمل طرق المثيل على مستند واحد؛ استخدم this للوصول إلى المستند الحالي.

▶ مثال: طريقة مقارنة كلمات المرور

JAVASCRIPT
userSchema.methods.comparePassword = function(candidate) {
  return bcrypt.compare(candidate, this.password);
};

const user = await User.findOne({ email: 'bob@test.com' });
const isMatch = await user.comparePassword('mypassword');
▶ جرّب الكود

(2) الطرق الثابتة

يتم تعريف الطرق الثابتة في النموذج (Model) ولا تعتمد على مثيلات المستندات، مما يجعلها مناسبة لدعم الاستعلامات.

▶ مثال: البحث عن الطرق الثابتة حسب الدور

JAVASCRIPT
userSchema.statics.findByRole = function(role) {
  return this.find({ role }).sort({ createdAt: -1 });
};

const admins = await User.findByRole('admin');
▶ جرّب الكود
النوع التعريف الاستدعاء هذا المرجع
طرق المثيل schema.methods.xxx = function doc.xxx() مثيل المستند
طريقة ثابتة schema.statics.xxx = function Model.xxx() النموذج


9. ملء الاستعلامات المدمجة

تقوم مونجوس populate() بتنفيذ عملية مطابقة المراجع بين مستندات MongoDB، على غرار عملية JOIN في SQL.

(1) التعريفات المرجعية وعمليات الربط

▶ مثال: المقالات المرتبطة بالمؤلفين

JAVASCRIPT
const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  }
});

const Post = mongoose.model('Post', postSchema);

const posts = await Post.find().populate('author', 'firstName lastName email');
▶ جرّب الكود

(2) التعبئة متعددة المستويات والتصفية الشرطية

▶ مثال: عمليات الربط والفلترة متعددة المستويات

JAVASCRIPT
const commentSchema = new mongoose.Schema({
  content: String,
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  post: { type: mongoose.Schema.Types.ObjectId, ref: 'Post' }
});

const Comment = mongoose.model('Comment', commentSchema);

const comments = await Comment.find()
  .populate('author', 'firstName lastName')
  .populate({
    path: 'post',
    select: 'title content',
    match: { status: 'published' }
  });
▶ جرّب الكود

(3) Performance Considerations for populate

populate() في الأساس، يتضمن هذا إصدار استعلامات إضافية ثم دمج النتائج؛ وهو ليس عملية «JOIN» حقيقية. ولا تزال مشكلة «N+1» قائمة — فإذا كانت هناك 100 مقالة مرتبطة بـ 100 مؤلف مختلف، فسيؤدي ذلك إلى إصدار 101 استعلامًا. بالنسبة للسيناريوهات التي تتضمن ارتباطات متكررة، يُنصح بالنظر في تضمين المستندات أو $lookup التجميع.



10. مقارنة بين Mongoose وبرنامج التشغيل الأصلي

البعد برنامج التشغيل الأصلي لـ MongoDB Mongoose ODM
التحقق من صحة البيانات عبارات if/else مكتوبة يدويًّا ومتناثرة في جميع أنحاء وحدة التحكم التحقق من الصحة التصريحي القائم على المخطط، والمُدار مركزيًّا
قيود الأنواع لا توجد؛ يمكن لأي حقل تخزين أي قيمة أنواع مفروضة بموجب المخطط؛ تحويل تلقائي
الاستعلامات المجمعة التجميع اليدوي $lookup populate() يتم تنفيذه في سطر واحد
ربطات دورة الحياة لا شيء ربطات ما قبل/بعد
السمة الافتراضية لا شيء الحقل الافتراضي المحسوب ديناميكيًا
إدارة الفهرس الدليل createIndex() إعلان المخطط + الإنشاء التلقائي
واجهة برمجة التطبيقات للاستعلام معلمات الكائنات find({ age: { $gte: 18 } }) مُنشئ متسلسل + معلمات الكائنات
منحنى التعلم منخفض؛ يشبه إلى حد كبير بناء الجملة الأصلي لـ MongoDB متوسط؛ يتطلب فهمًا للمخطط والنموذج والوثيقة
المرونة عالية، تحكم كامل متوسطة، يتم تجاهل الحقول خارج المخطط افتراضيًّا
الأداء أفضل قليلاً، بدون طبقة وسيطة أقل قليلاً، بسبب العبء الإضافي الناتج عن التحقق من الصحة والخطافات


11. مثال شامل: نماذج بيانات المستخدم والمقالات

ادمج المخططات، وعمليات التحقق من الصحة، والخطافات، والسمات الافتراضية، وطرق المثيلات، واستعلامات الربط لتشكيل نظام نموذج بيانات متكامل.

▶ مثال: models/User.js

JAVASCRIPT 📖 للعرض فقط
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const userSchema = new mongoose.Schema({
  firstName: {
    type: String,
    required: [true, 'The last name cannot be left blank.'],
    trim: true
  },
  lastName: {
    type: String,
    required: [true, 'The name cannot be empty'],
    trim: true
  },
  email: {
    type: String,
    required: [true, 'The email address cannot be left blank.'],
    unique: true,
    lowercase: true,
    match: [/^\S+@\S+\.\S+$/, 'The email address format is incorrect.']
  },
  password: {
    type: String,
    required: [true, 'The password cannot be empty.'],
    minlength: 8,
    validate: {
      validator: function(v) {
        return /^(?=.*[A-Z])(?=.*\d).{8,}$/.test(v);
      },
      message: 'Password must be at least 8 characters, must include uppercase and digits'
    }
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user'
  },
  age: {
    type: Number,
    min: [0, 'Age cannot be a negative number.'],
    max: [150, 'Age must not exceed150']
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
}, {
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

userSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
});

userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 10);
  next();
});

userSchema.methods.comparePassword = function(candidate) {
  return bcrypt.compare(candidate, this.password);
};

userSchema.statics.findByRole = function(role) {
  return this.find({ role }).sort({ createdAt: -1 });
};

module.exports = mongoose.model('User', userSchema);
64 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: models/Post.js

JAVASCRIPT 📖 للعرض فقط
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: {
    type: String,
    required: [true, 'The title cannot be left blank.'],
    trim: true,
    minlength: [2, 'Title must be at least2characters'],
    maxlength: [200, 'Most Titles200characters']
  },
  content: {
    type: String,
    required: [true, 'Content cannot be empty']
  },
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  status: {
    type: String,
    enum: ['draft', 'published', 'archived'],
    default: 'draft'
  },
  tags: [{
    type: String,
    lowercase: true
  }],
  viewCount: {
    type: Number,
    default: 0,
    min: 0
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: Date.now
  }
});

postSchema.index({ status: 1, createdAt: -1 });
postSchema.index({ tags: 1 });

postSchema.virtual('excerpt').get(function() {
  return this.content.substring(0, 100) + '...';
});

postSchema.pre('save', function(next) {
  if (this.isModified('content')) {
    this.updatedAt = new Date();
  }
  next();
});

postSchema.post('remove', async function(doc) {
  await mongoose.model('Comment').deleteMany({ post: doc._id });
});

postSchema.statics.findPublished = function() {
  return this.find({ status: 'published' })
    .populate('author', 'firstName lastName email')
    .sort({ createdAt: -1 });
};

module.exports = mongoose.model('Post', postSchema);
61 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: الاستعلامات وعمليات الربط

JAVASCRIPT
const mongoose = require('mongoose');
const User = require('./models/User');
const Post = require('./models/Post');

async function main() {
  await mongoose.connect('mongodb://localhost:27017/blog');

  const user = await User.create({
    firstName: 'Bob',
    lastName: 'Smith',
    email: 'bob@example.com',
    password: 'Secure123',
    role: 'admin',
    age: 28
  });

  const post = await Post.create({
    title: 'Mongoose Getting Started Guide',
    content: 'Mongoose is MongoDB ODM library, providing schema-driven data modeling...',
    author: user._id,
    status: 'published',
    tags: ['mongodb', 'mongoose', 'nodejs']
  });

  const published = await Post.findPublished();
  console.log(published[0].excerpt);
  console.log(published[0].author.fullName);

  const match = await user.comparePassword('Secure123');
  console.log('Password match:', match);

  await mongoose.connection.close();
}

main();
▶ جرّب الكود
BASH
node app.js
TEXT 📖 للعرض فقط
Mongoose is MongoDB ODM library, providing schema-driven data modeling......
Bob Smith
Password match: true


❓ أسئلة شائعة

س كيف أختار بين Mongoose والبرنامج التشغيلي الأصلي؟
ج اختر البرنامج التشغيلي الأصلي للمشاريع الصغيرة أو عندما تحتاج إلى أقصى قدر من المرونة؛ واختر Mongoose للمشاريع المتوسطة إلى الكبيرة، أو للتعاون بين أعضاء الفريق، أو عندما تحتاج إلى التحقق من الصحة و«الهوكات». يمكنك أيضًا استخدام كليهما معًا — يدعم Mongoose الوصول إلى المجموعات الأصلية عبر Model.collection.
س ما الفرق بين «المخطط» و«النموذج»؟
ج المخطط هو مخطط تفصيلي يحدد أنواع الحقول وقواعد التحقق من الصحة والخطافات؛ أما النموذج فهو مُنشئ يتم إنشاؤه من المخطط ويوفر واجهة لأساليب CRUD. ولا يمكن لمخطط واحد أن يُنشئ سوى نموذج واحد فقط.
س ألا يتم تخزين الحقول الافتراضية في قاعدة البيانات؟
ج نعم، يتم حساب الحقول الافتراضية في الذاكرة فقط ولا يتم تسجيلها في MongoDB. إذا كنت بحاجة إلى الاحتفاظ بها، فاستخدم حقلًا عاديًا ورابط pre-save لحساب القيمة وتعيينها. بشكل افتراضي، لا تظهر الحقول الافتراضية في مخرجات JSON؛ لذا يجب عليك تعيين toJSON: { virtuals: true }.
س ما هو أداء populate؟
ج populate ليس عملية ربط (JOIN) في SQL؛ فهو يتضمن أساسًا تنفيذ استعلامات إضافية ثم دمج النتائج. يكون الأداء جيدًا عندما يكون عدد المستندات ذات الصلة قليلًا؛ ومع ذلك، فإن وجود عدد كبير من المستندات ذات الصلة المختلفة قد يتسبب في مشكلة استعلام N+1. بالنسبة للسيناريوهات عالية التكرار، يُنصح بالنظر في تضمين المستندات، أو استخدام $lookup يدويًّا، أو التخزين المؤقت.
س كيف أتعامل مع أخطاء التحقق من الصحة؟
ج يُطلق Mongoose استثناءً ValidationError عند فشل التحقق من الصحة، ويحتوي الكائن errors على تفاصيل الخطأ لكل حقل. يمكنك استخدام Object.values(err.errors).map(e => e.message) لاستخراج جميع الرسائل، وبالاقتران مع برمجيات Express الوسيطة لمعالجة الأخطاء، إرجاع رمز حالة موحد هو 400.
س ما الذي يشير إليه this في دالة الـ hook pre-save؟
ج يشير this إلى مثيل Document الذي على وشك الحفظ. ملاحظة: سيؤدي استخدام الدوال السهمية إلى فقدان ربط this؛ يجب أن تكون دالة الخطاف دالة عادية. يمكن استخدام this.isModified('field') لتحديد ما إذا كان الحقل قد تم تعديله أم لا.
س هل يمكنني إضافة حقول بعد تعريف المخطط؟
ج نعم، يمكنك استخدام schema.add({ newField: String }) لإضافتها ديناميكيًا. ومع ذلك، لن يتم تحديث النماذج المُجمَّعة تلقائيًّا؛ فستحتاج إلى إعادة تجميعها أو استخدام الملحق schema.plugin(). نوصي بالتخطيط لهيكل الحقول بعناية في بداية المشروع.

📖 ملخص


📝 تمارين

  1. أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
  2. قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
  3. راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
  4. التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
  5. حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%