Menu
CoddyTech

خدمة

جزء من قسم TypeScript وNestJS في رحلة Node.js على Coddy. الدرس 9 من 24.

يحتفظ المتحكم بالمهام نفسها، لذا لا يمكن لأي جزء آخر من التطبيق الوصول إليها. مكان البيانات والمنطق هو مزوّد: فئة ينشئها Nest ويسلّمها إلى من يطلبها.

@Injectable()
export class BooksService {
    private books = [{ title: 'Dune' }];
}

@Controller('books')
export class BooksController {
    constructor(private readonly booksService: BooksService) {}
}

تضع @Injectable() علامة على الفئة، وتدرجها الوحدة في providers. تطلبها فئة بذكر اسمها في بانيها: وهذا هو حقن التبعية. إن private readonly قبل المعامل اختصار في TypeScript: ينشئ حقلًا باسم this.booksService ويخزّن فيه ما يمرره Nest. يقرأ Nest نوع المعامل، الذي يحتفظ به المصرّف للفئات المزينة، وينشئ نسخة واحدة للوحدة التي تدرجه، ويمرر النسخة نفسها إلى كل فئة فيها تطلبها. تكون البيانات مشتركة، ويمكن للاختبار أن يمرر بديلًا وهميًا بدلًا منها.

يبدأ هذا الدرس والمتحكم يستدعي بالفعل this.tasksService.

challenge icon

التحدي

سهل

تفتح هذه الخطوة مشروع متعقّب المهام المُعدّ مسبقًا للدورة باستخدام Nest، وليس ملفات مشروعك من درس رموز الحالة والأخطاء (بما فيها src/tasks/tasks.controller.ts). يظل عملك السابق ضمن درسه الخاص. اقرأ الملفات المقدّمة قبل تعديل src/tasks/tasks.service.ts. تحتوي هذه النسخة على مسارات مهام Nest، ووحدة التحكم فيها مُعدّة لتفويض العمل إلى خدمة.

توجد المهام داخل وحدة التحكم، لذا لا يمكن لأي جزء آخر من التطبيق الوصول إليها. انقلها إلى خدمة: فئة تملك المهام، ينشئها Nest مرة واحدة ويمررها إلى كل فئة تطلبها. أُعيدت كتابة وحدة التحكم بالفعل لاستدعاء الخدمة. مهمتك هي إكمال الخدمة وربط الجزأين.

  1. في src/tasks/tasks.service.ts، علّم الفئة TasksService باستخدام @Injectable()، من @nestjs/common. يتيح ذلك لـ Nest إنشاءها وتمريرها.
  2. في الفئة نفسها، اكتب دالة create(title). تنشئ مهمةً بمعرّف متاح تالٍ (أكبر معرّف في this.tasks زائد واحد)، وذلك العنوان، وdone: false. تضيف المهمة إلى this.tasks وتعيدها. يستدعي مسار POST /tasks في وحدة التحكم هذه الدالة مع العنوان الوارد في نص الطلب.
  3. اكتب دالة remove(id)، حيث تكون id رقمًا. عندما لا توجد مهمة بهذا المعرّف، يجب أن تستجيب برمز 404، مثل findOne: استدعِ this.findOne(id) أولًا، فهي تطرح استثناءً. ثم أزل المهمة من this.tasks. ولا تعيد الدالة أي شيء.
  4. في src/tasks/tasks.module.ts، استورد TasksService من ./tasks.service، ثم أضف قائمة providers إلى الوحدة، بجانب controllers، وضع TasksService فيها. لا ينشئ Nest إلا الفئات التي تسردها الوحدة هناك.
  5. في src/tasks/tasks.controller.ts، استبدل التعليق داخل TasksController بمنشئ يأخذ وسيطًا واحدًا، مكتوبًا هكذا: private readonly tasksService: TasksService. يرى Nest النوع TasksService ويمرر مثيل الخدمة الوحيد. كما أن الكلمتين private readonly قبل الوسيط تنشئان حقلًا، this.tasksService، يحتفظ بما تم تمريره. وهذا هو الاسم الذي تستخدمه الدوال أدناه بالفعل.

جرّب ذلك: أرسل POST مع { "title": "Buy milk" } إلى /tasks. ستحصل على 201 والنتيجة التالية:

{ "id": 4, "title": "Buy milk", "done": false }

أرسل GET /tasks: ستحصل على أربع مهام، وتكون Buy milk الأخيرة. أرسل DELETE /tasks/4: ستحصل على 204. أرسل DELETE /tasks/99: ستحصل على 404 مع "message": "Task 99 not found".

جرّب بنفسك

import { NotFoundException } from '@nestjs/common';

export interface Task {
    id: number;
    title: string;
    done: boolean;
    createdAt?: string;
}

// اجعل هذه الفئة مزوّدًا يمكن لـ Nest حقنه.
export class TasksService {
    private tasks: Task[] = [
        { id: 1, title: 'Write the README', done: true, createdAt: '2026-01-05T09:00:00.000Z' },
        { id: 2, title: 'Fix the login bug', done: false, createdAt: '2026-01-06T14:30:00.000Z' },
        { id: 3, title: 'Plan the sprint', done: false, createdAt: '2026-01-07T08:15:00.000Z' },
    ];

    findAll(done?: string): Task[] {
        if (done === undefined) {
            return this.tasks;
        }
        return this.tasks.filter((task) => task.done === (done === 'true'));
    }

    findOne(id: number): Task {
        const task = this.tasks.find((task) => task.id === id);
        if (!task) {
            throw new NotFoundException(`Task ${id} not found`);
        }
        return task;
    }

    // اكتب create(title) وremove(id) هنا.
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس TypeScript وNestJS

تدرّب بنفسك: Playground لـ Express