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;
}
}
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 {}
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
}
}
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 {}
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)
But where we can UserController or EmailService??
And why we should use
@Inject
if in the code example we haven't used it?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
I see. But why do we want to use
@Inject
instead of passing necessary dependencies inproviders
property of@Module
decorator?