NestJS — Node.js للمؤسسات

المستودعات والعلاقات والاستعلام

18 دقيقة الدرس 21 من 48

المستودعات والعلاقات والاستعلام

المستودع (repository) هو واجهة TypeORM لقراءة وكتابة كيان واحد. يحقنه NestJS لك، وتستدعي دواله من خدمة. يغطّي هذا الدرس عمليات CRUD اليومية، وتعريف العلاقات بين الكيانات، وبناء استعلامات أكثر تقدّمًا.

حقن مستودع

استخدم @InjectRepository() للحصول على مستودع مكتوب النوع لكيان مُسجَّل بـ forFeature():

import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private readonly users: Repository<User>, ) {} }

عمليات CRUD اليومية

// إنشاء const user = this.users.create({ name: 'Sara', email: 's@x.com' }); await this.users.save(user); // قراءة await this.users.find(); // الكل await this.users.findOne({ where: { id: 1 } }); // واحد await this.users.findBy({ isActive: true }); // مُرشَّح // تحديث await this.users.update(1, { name: 'Sara A.' }); // حذف await this.users.delete(1);
create() مقابل save(): تبني create() نسخة كيان في الذاكرة فقط (وتُطبّق القيم الافتراضية) — لا تلمس قاعدة البيانات. وsave() هي ما يُثبّتها. نسيان save() خطأ كلاسيكي بعنوان "لماذا لا يُحفَظ؟".

تعريف العلاقات

تربط العلاقات الكيانات. يوفّر TypeORM مُزخرِفًا لكل تعدّدية. زوج واحد-إلى-كثير / كثير-إلى-واحد مثلًا، كمستخدم بمنشورات كثيرة:

@Entity() export class User { @OneToMany(() => Post, (post) => post.author) posts: Post[]; } @Entity() export class Post { @ManyToOne(() => User, (user) => user.posts) author: User; }

مُزخرِفات العلاقات الأخرى @OneToOne() و@ManyToMany() (الأخيرة تُنشئ جدول وصل تلقائيًا).

تحميل العلاقات

لا تُحمَّل العلاقات افتراضيًا — تطلبها بخيار relations:

await this.users.findOne({ where: { id: 1 }, relations: { posts: true }, // حمّل منشورات هذا المستخدم });
احذر مشكلة استعلام N+1. تحميل قائمة مستخدمين ثم الوصول إلى منشورات كلٍّ في حلقة قد يُطلِق استعلامًا لكل مستخدم. حمّل العلاقة مسبقًا (بـ relations أو JOIN في باني الاستعلام) بدل التحميل الكسول داخل حلقة.

باني الاستعلام للاستعلامات المعقّدة

لأي شيء يتجاوز عمليات find البسيطة — الوصلات والتجميعات والمرشّحات الشرطية — استخدم باني الاستعلام:

const result = await this.users .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .where('user.isActive = :active', { active: true }) .orderBy('user.createdAt', 'DESC') .take(10) .getMany();
استخدم دائمًا ربط المعاملات (:active)، لا دمج النصوص. يُمعلِم باني الاستعلام القيم، ما يمنع حقن SQL. بناء جملة WHERE بدمج مدخلات المستخدم ثغرة أمنية خطيرة.

الخلاصة

احقن Repository<Entity> بـ @InjectRepository() واستخدم create/save/find/update/delete لـ CRUD (تذكّر أنّ save() يُثبّت). عرّف العلاقات بـ @OneToMany/@ManyToOne/@ManyToMany، وحمّلها بخيار relations، وتجنّب استعلامات N+1. استخدم باني الاستعلام بمعاملات مربوطة للاستعلامات المعقّدة والآمنة. تاليًا: إدارة تغييرات المخطّط بالترحيلات.