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

Потренируйтесь самостоятельно: Песочница Express