Menu
CoddyTech

A Service

Part of the TypeScript and NestJS section of Coddy's Node.js journey. Lesson 9 of 24.

The controller holds the tasks itself, so no other part of the app can reach them. Data and logic belong in a provider: a class Nest creates and hands to whoever asks for it.

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

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

@Injectable() marks the class, and the module lists it in providers. A class asks for it by naming it in its constructor: this is dependency injection. private readonly in front of the parameter is TypeScript shorthand: it creates a field this.booksService and stores what Nest passes in. Nest reads the parameter's type, which the compiler keeps for decorated classes, creates one instance for the module that lists it, and passes that same instance to every class there that asks. The data is shared, and a test can pass in a fake instead.

This lesson opens with the controller already calling this.tasksService.

challenge icon

Challenge

Easy

This step opens the course’s prepared Nest task tracker project, rather than your project files from Status Codes and Errors (including src/tasks/tasks.controller.ts). Your earlier work stays in its own lesson. Read the supplied files before editing src/tasks/tasks.service.ts. This copy contains the Nest task routes, with the controller prepared to delegate to a service.

The tasks live inside the controller, so no other part of the app can reach them. Move them into a service: a class that owns the tasks, which Nest creates once and hands to every class that asks for it. The controller is already rewritten to call the service. Your job is to finish the service and connect the two.

  1. In src/tasks/tasks.service.ts, mark the class TasksService with @Injectable(), from @nestjs/common. This lets Nest create it and hand it out.
  2. In the same class, write a method create(title). It builds a task with the next free id (one more than the largest id in this.tasks), that title, and done: false. It adds the task to this.tasks and returns it. The controller's POST /tasks calls it with the title from the request body.
  3. Write a method remove(id), where id is a number. When no task has that id, it must respond with 404, like findOne: call this.findOne(id) first, which throws. Then remove the task from this.tasks. It returns nothing.
  4. In src/tasks/tasks.module.ts, import TasksService from ./tasks.service, then add a providers list to the module, next to controllers, and put TasksService in it. Nest only creates the classes a module lists there.
  5. In src/tasks/tasks.controller.ts, replace the comment inside TasksController with a constructor that takes one parameter, written private readonly tasksService: TasksService. Nest sees the type TasksService and passes in its one service. The words private readonly in front of a parameter also create a field, this.tasksService, holding what was passed in. That is the name the methods below already use.

Try it: POST { "title": "Buy milk" } to /tasks. You get 201 and:

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

Send GET /tasks: four tasks, with Buy milk last. Send DELETE /tasks/4: 204. Send DELETE /tasks/99: 404 with "message": "Task 99 not found".

Try it yourself

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

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

// Make this class a provider that Nest can inject.
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;
    }

    // Write create(title) and remove(id) here.
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in TypeScript and NestJS

Practice on your own: Express playground