Lesson 3
Providers in NestJS
Introduction to Providers in NestJS

Welcome to this lesson on Providers in NestJS. So far, we've covered setting up a basic NestJS application and understanding its project structure. We also explored how controllers in NestJS handle requests. Today, we’ll dive into providers — a core concept in NestJS that helps manage and execute business logic.

Providers are a fundamental building block in NestJS applications. Understanding providers will help you build more modular, maintainable, and testable applications. There are many different kinds of providers in NestJS but we're going to learn about Services. We'll be focusing on a BookService in this lesson which is responsible for providing Book data.

What is a Service in NestJS?

In NestJS, a Service is a class that is decorated with the @Injectable() decorator. Services are used to encapsulate and manage business logic, data processing, and any other operations that are not directly tied to handling requests (which is the role of controllers). By separating business logic into services, you promote a clean architecture where concerns are properly separated, making your application more modular and easier to maintain.

Understanding Dependency Injection

Dependency Injection (DI) is a pattern where an object receives its dependencies from an external source rather than creating them itself. This strategy makes it easier to manage and scale your application.

In NestJS, DI is built-in and allows you to inject services and other dependencies into your classes. This is done through the use of special decorators and the NestJS DI container.

Imagine you have a coffee shop, and each coffee machine needs a supply of coffee beans. With DI, instead of each machine fetching its beans, a central service provides beans to all machines. This simplifies management and ensures each machine gets what it needs efficiently.

Creating the BooksService as a Provider

Let's start by creating a service called BooksService. This service will provide methods to retrieve information about books.

Here is the step-by-step code to create BooksService:

TypeScript
1import { Injectable } from '@nestjs/common'; 2import { Book, books } from './books.data'; 3 4@Injectable() 5export class BooksService { 6 getAllBooks(): Book[] { 7 return books; 8 } 9 10 getOneBook(id: string): Book { 11 return books.find(book => book.id === id); 12 } 13}
Explanation
  • @Injectable(): This decorator indicates that the class can be managed by the NestJS DI system.
  • BooksService: This class contains methods to get all books and to get a single book by its ID.
  • getAllBooks(): This method returns an array of books.
  • getOneBook(id: string): This method returns a single book that matches the given ID.
Using Providers in Controllers

Next, we need to use our BooksService within a controller to handle incoming requests.

Here's the code to set up the BooksController to receive an instance of BooksService and use it in the GET handler:

TypeScript
1import { Controller, Get } from '@nestjs/common'; 2import { Book } from './books.data'; 3import { BooksService } from './books.service'; 4 5@Controller('books') 6export class BooksController { 7 constructor(private readonly booksService: BooksService) {} 8 9 @Get() 10 findAll(): Book[] { 11 return this.booksService.getAllBooks(); 12 } 13}
Explanation
  • @Controller('books'): This decorator defines a controller that handles requests to the /books route.
  • BooksService: The BooksService is injected into the controller via the constructor.
  • @Get() findAll(): This method handles GET requests to /books and returns all books by calling getAllBooks from BooksService.
Organizing Providers and Controllers in Modules

To fully integrate BooksService and BooksController into our application, we need to declare them in the BooksModule module. We'll cover Modules later but for now, notice how we can define the BooksService as a provider in addition to the BooksController which already existed from the previous lesson.

Here's the code for BooksModule:

TypeScript
1import { Module } from '@nestjs/common'; 2import { BooksController } from './books.controller'; 3import { BooksService } from './books.service'; 4 5@Module({ 6 controllers: [BooksController], 7 providers: [BooksService], 8}) 9export class BooksModule {}
Explanation
  • @Module({}): This decorator defines a module that groups related controllers and providers.
  • controllers: [BooksController]: This array lists all controllers in the module.
  • providers: [BooksService]: This array lists all providers in the module including our new service.

By declaring BooksService and BooksController in a module, NestJS can properly manage their lifecycle and dependencies.

Summary

In this lesson, we explored the concept of providers in NestJS and how to create and use them. We started by understanding what providers are and the role of dependency injection. We then created a BooksService to fetch book data, integrated it with a controller, and finally declared them in a module.

Providers are essential for managing and structuring business logic within your application. In the upcoming practice exercises, you will get hands-on experience to reinforce your understanding of providers in NestJS. Keep these concepts in mind as they are crucial for building robust and maintainable applications.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.