Providers
Providers
Providers are a core concept in Nest. Many of the basic Nest classes, such as
services, repositories, factories, and helpers, can be treated as providers. The
key idea behind a provider is that it can be injected as a dependency, allowing
objects to form various relationships with each other. The responsibility of
"wiring up" these objects is largely handled by the Nest runtime system.
Hint
Since Nest enables you to design and organize dependencies in an object-
oriented manner, we strongly recommend following the SOLID principles.
Services#
Let's begin by creating a simple CatsService. This service will handle data storage
and retrieval, and it will be used by the CatsController. Because of its role in
managing the application's logic, it’s an ideal candidate to be defined as a
provider.
cats.service.ts
JS
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
Hint
To create a service using the CLI, simply execute the $ nest g service cats
command.
Our CatsService is a basic class with one property and two methods. The key
addition here is the @Injectable() decorator. This decorator attaches metadata to
the class, signaling that CatsService is a class that can be managed by the Nest
IoC container.
Additionally, this example makes use of a Cat interface, which likely looks
something like this:
interfaces/cat.interface.ts
JS
Now that we have a service class to retrieve cats, let's use it inside the
CatsController:
cats.controller.ts
JS
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
The CatsService is injected through the class constructor. Notice the use of the
private keyword. This shorthand allows us to both declare and initialize the
catsService member in the same line, streamlining the process.
Dependency injection#
Nest is built around the powerful design pattern known as Dependency Injection. We
highly recommend reading a great article about this concept in the official Angular
documentation.
Scopes#
Providers typically have a lifetime ("scope") that aligns with the application
lifecycle. When the application is bootstrapped, each dependency must be resolved,
meaning every provider gets instantiated. Similarly, when the application shuts
down, all providers are destroyed. However, it’s also possible to make a provider
request-scoped, meaning its lifetime is tied to a specific request rather than the
application's lifecycle. You can learn more about these techniques in the Injection
Scopes chapter.
Custom providers#
Nest comes with a built-in inversion of control ("IoC") container that manages the
relationships between providers. This feature is the foundation of dependency
injection, but it’s actually much more powerful than we've covered so far. There
are several ways to define a provider: you can use plain values, classes, and both
asynchronous or synchronous factories. For more examples of defining providers,
check out the Dependency Injection chapter.
Optional providers#
Occasionally, you may have dependencies that don't always need to be resolved. For
example, your class might depend on a configuration object, but if none is
provided, default values should be used. In such cases, the dependency is
considered optional, and the absence of the configuration provider should not
result in an error.
@Injectable()
export class HttpService<T> {
constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}
In the example above, we're using a custom provider, which is why we include the
HTTP_OPTIONS custom token. Previous examples demonstrated constructor-based
injection, where a dependency is indicated through a class in the constructor. For
more details on custom providers and how their associated tokens work, check out
the Custom Providers chapter.
Property-based injection#
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
Warning
If your class doesn't extend another class, it's generally better to use
constructor-based injection. The constructor clearly specifies which dependencies
are required, offering better visibility and making the code easier to understand
compared to class properties annotated with @Inject.
Provider registration#
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
Nest will now be able to resolve the dependencies of the CatsController class.
So far, we've covered how Nest automatically handles most of the details of
resolving dependencies. However, in some cases, you might need to step outside of
the built-in Dependency Injection system and manually retrieve or instantiate
providers. Two such techniques are briefly discussed below.