DEV Community

patrick0806
patrick0806

Posted on • Edited on

Understanding Providers and Dependency Injection in NestJS

NestJS is a Node.js framework that allows us to build scalable and well-structured web applications using service-oriented architecture approach. One of the core features of NestJS is dependency injection, which is a common design pattern in object-oriented applications that allows you to create and manage instances of objects within an application environment.

To use dependency injection in NestJS, it is necessary to create and configure service providers, which are responsible for managing and providing object instances to other application components. In this article, we'll explore the concepts of providers and dependency injection in NestJS, as well as show you how to create and use custom providers and share instances of providers between modules.

What are providers and why are they important in NestJS?

A provider is an object that provides an instance of a service to other application components. In other words, a provider is responsible for creating, managing, and providing instances of objects to other application components. Providers are important in NestJS because they allow the developer to create and manage services in a modular and reusable way, making it easier to maintain code and create automated tests.

Providers are created through classes that have the @Injectable() decorator and are registered in modules through the providers method. For example, suppose we want to create a service that performs user validation in our application. We can create an AuthService provider as follows:

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

@Injectable()
export class AuthService {
  validateUser(username: string, password: string): boolean {
    // Lógica de validação de usuário aqui
    return true;
  }
}
Enter fullscreen mode Exit fullscreen mode

The AuthService class has the @Injectable() decorator, indicating that it can be injected into other application components. In addition, the class has a validateUser() method that implements user validation logic.

To use the AuthService provider in other application components, we need to register it in a module. This can be done as follows:

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';

@Module({
  providers: [AuthService],
})
export class AuthModule {}
Enter fullscreen mode Exit fullscreen mode

The AuthModule module registers the AuthService provider in the providers array, indicating that it can be injected into other application components that depend on that service.

Dependency injection in NestJS

Dependency injection is a design pattern that allows objects to be created and managed within an application environment. In NestJS, dependency injection is used to provide instances of services to other application components.

To inject a service into an application component, you must use the @Inject() annotation. For example, suppose we want to inject the AuthService provider into a controller that handles user authentication. We can do this as follows:

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}

@Post('login')
async login(@Body() body) {
const { username, password } = body;
const isValid = await this.authService.validateUser(username, password);
        if (!isValid) {
        throw new BadRequestException('Invalid credentials');
        }
        // JWT token generation logic here
    }
}
Enter fullscreen mode Exit fullscreen mode

In the AuthController class, we use the constructor to inject the EmailService provider, indicating that this service is necessary so that the class can send registration confirmation emails.

Sharing provider instances between modules

To share the same instance of a provider across multiple application modules, you need to import the module containing the provider into all modules that want to use that provider. This can be done as follows:

import { Module } from '@nestjs/common';
import { EmailModule } from './email.module';
import { AuthModule } from './auth.module';

@Module({
   imports: [EmailModule, AuthModule],
})
export class AppModule {}

Enter fullscreen mode Exit fullscreen mode

In the example above, the module AppModule imports the modules EmailModule and AuthModule, which contain the providers EmailService and *AuthService *, respectively. That way, all instances of these providers share the same dependency injection scope.

It is important to note that when sharing the same instance of a provider across multiple modules, care must be taken to avoid concurrency issues. For example, if multiple controllers modify the state of a provider simultaneously, problems with data inconsistency can occur. Therefore, it is recommended that providers be designed to be thread-safe, that is, to support concurrent access by multiple threads without generating data consistency problems.

Conclusion

In summary, providers and dependency injection are important features of NestJS that allow you to create modular, flexible and scalable applications. Using providers, we can encapsulate the application's business logic in independent, reusable components, which can be transparently injected into other components. Additionally, dependency injection allows us to automatically manage dependencies between application components, reducing code complexity and making maintenance and testing easier.

Top comments (3)

Collapse
 
bigkrp profile image
bigkrp

UserController class, we use the constructor to inject the EmailService

But where we can UserController or EmailService??
And why we should use @Inject if in the code example we haven't used it?

Collapse
 
patrick0806 profile image
patrick0806

Maybe I expressed myself badly, in the example I made, Nestjs is smart enough to automatically inject the service, but if it's something more complex or you're using some abstractions you'll need to use @Inject.

And regarding UserController, it was a typo, which went unnoticed, thanks for pointing out the correct one would be AuthController

Collapse
 
bigkrp profile image
bigkrp

I see. But why do we want to use @Inject instead of passing necessary dependencies in providers property of @Module decorator?