Video Explanation
Please find video explanation below:
Introduction
Lets first divide the entire term in two parts. "Dependency" and "Injection".
This suggests that there could be one dependency class which needs to be "injected" to another class (simplest of the example).
In nest.js, we normally inject providers (services) in controllers to perform some sort of business logic.
Please note that the task of service should not be authentication, authorization, validation, transformation and/or extending or overriding the function. Because, for all this, we have separate components in nest.js like middleware, guards, pipes and interceptors.
Make sure that the dependency you make should have one concern and the concern should be specific to the business logic you plan to implement.
Non Dependency Example
Lets take a look at the example below:
Car has an Engine that means, Car depends upon Engine.
As such, we have two classes, i.e., Car and Engine.
If you notice here:
We are directly instantiating Engine in the Car. You must have done this in other programming languages as well. This means that Car and Engine are tightly coupled. If you were to make any changes in Engine, then you might have to make changes in your class code as well to make sure that the Car class instantiates the Engine class correctly.
In short, in this example, your code is managing the dependency.
Goal of Dependency Injection
The main goal that dependency injection achieves is to reverse the order of things explained earlier.
Instead of the code managing the dependency, it ensures that the dependencies and their relationships are managed by library or framework. In our case, the dependencies would be managed by Nest.js runtime system.
Creating Injectable Dependency
Nest.js allows @Injectable()
decorator to perform this operation. It can be imported from @nestjs/common
.
This decorator needs to be placed right above the class and it ensures to add metadata to the class which makes the class as injectable to other classes via. dependency injection.
Make sure that the class you tend to make as a dependency is exported as shown in above image.
Some basic code would be like this:
import { Injectable } from '@nestjs/common';
@Injectable()
export class ShameelService {}
Now lets take a look at the following code:
What I have done above is created a private variable cats
which is an array of strings and created two methods create()
which pushes incoming argument to cats
variable and findaAll()
which simply returns the cats
array. It's pretty simple, no rocket science intended.
Injecting Service in Controller
This is the simplest code to inject service in a controller:
This is an example of dependency injection via. constructor (you guessed it right, there are other method but we will only be covering constructor based dependency injection here).
Lets understand that line a bit which does all the magic..
constructor(private readonly catService:CatService)
So, we have a variable named catService
which is private
and readonly
. This means, the scope of this variable is within this file only and it cannot be modified by any means from this file during run time.
The magical part is CatService
which in TypeScript defines the type of catsService
variable.
Lets add the following code here:
The code implements POST endpoint which adds incoming arguments to the cats
privatevariable in CatsService
.
But... How does CatsController
just got access to everything within CatsService
? Because, as far as the programming concept goes, CatsService
needs to be fully resolved and its instance must be created so that it can be utilized somewhere else. Lets discuss this below.
Registering the Dependency
The key part after creating a dependency is to register it. Once you register the dependency then whenever you run your nest.js application, it will first resolve the dependency and create its instance.
Nest.js keeps instances of the providers within an IoC (Inversion of Control) container. Whenever some control tends to perform dependency injection for some service, nest.js runtime picks that service from its container and provides it to that particular controller, provided that the controller is in the scope of the service it tends to inject.
If you do not register your service and tends to inject it then you will get the following error:
Nest.js suggests you the following suggestions:
In order to register your service within a module, all you have to do is to give provider class name in providers
array within the module
.
@Module({
controllers: [CatsController],
providers: [CatsService],
})
Conclusion
Provider and Controller both are classes. The difference comes from the decorator being used at the top of the class which adds relevant metadata to that particular class.
These are the steps that you have to follow:
- Create a dependency with
@Injectable()
decorator. - Register the dependency within the module.
- Apply dependency injection within the Controller to get access to the instance of the dependency.
In next article, we will understand what actually is Inversion of Control, that we slightly mentioned in our article here.
Happy coding! 🚀
Follow me for more such content:
YouTube: https://www.youtube.com/@ShameelUddin123
LinkedIn: https://www.linkedin.com/in/shameeluddin/
GitHub: https://github.com/Shameel123