NestJs - Service Providers & Dependency Injection [03]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to episode 3 where we're going to talk about a very important topic in sjs which is providers we're going to talk about this service right here this injectable decorator and then we're going to explain how we can inject that service in a controller or in another service to be used and we're going to explain dependence injection and how it's handled by an sjs behind the scenes so sit back and focus because this video is very important now in the last episode we explained controllers we said that the controller is simply a class that has the controller decorator and that the role of this class is to handle incoming HTTP requests using the handlers which we talked about today we're going to look at this service layer right here okay so if you take a look at the signs first of all we can see that the controller depends on the service and then the service depends on the data X layer now to better understand this you should know that in backend applications you obviously don't have all of your logic in one place you don't have the controller handling the logic of your application and then the database queries or operations so you would need to divide that two different layers and every layer should have its own tasks so the controller's role is to handle the HTTP requests and then the service role is to handle the business logic so the controller must depend on the service now that's the best practice of course you could just put all of your logic in the controller but that's a very bad practice so typically you would need to create a service to handle your business logic and then on top of that you would also have a repository where you would have your database schema and models and this is the place where you access your database and where you have your operations so if we take a look again we can see that the controller would depend on the service and the service would depend on the data access layer now there's a pattern here as you can see every class that is dependent on by another class or by another entity has this add injectable decorator here so this injectable decorator whenever it's used with a class it marks them as a provider now in this case we can see that the service is a provider and the data access layer which is the repository is also a provider because they both have the injectable decorator now what is the main role of a provider well it's to be injected in different places in your application so you can have them as dependencies and let's take a look at the documentation as a reference they say that many of the basic Nest classes may be treated as a provider such as Services repositories the two examples we gave factories helpers and so on and they also state that the main idea of a provider is that can it can be injected as a dependency just like we also said meaning objects can create various relationships with each other just like we've seen in the diagram now let's just apply what we learned so far but first I'm going to delete the app controller and the app servers and then create a different module because I like to keep app module at the root module and not have any controllers or services in there so let's go ahead and remove app controller app service remove those so now we have an empty module and then let's just say Nest G meaning generate and then let's say controller let's just call this one customer for example as you can see we got a customer folder here with a customer class and then as you can see as well we got the controller decorator generated for us so marking this as a controller and we also have a path prefix of customer now let's go ahead and create our customer service so customer dot service.ts let's export a new class and let's call it customer service and then just like we said previously we're going to mark it as injectable so the injectable decorator that we're going to import from nest.js common now that we mark this class as injectable we know that this is a provider and a provider can be injected as a dependency so here we can do that so let's create a Constructor and then inside of the Constructor let's say private customer service and the type is the customer service that we just created so of course this can be named anything you want this is the name of the variable and here this private keyword here is just a shorthand for declaring this new customer service data member of the customer controller and then setting it in the same line instead of that you could have said of course private for example customer and then you could have said here this Dot customer equal customer service but I like to use the private notation it's a quicker way and it's a typescript feature now we can see that we have injected the customer service which is this provider right here it's a provider again because it's marked with ADD injectable we have injected it inside of our controller so now the controller class depends on the customer service because anytime we create this controller you know that in oop whenever you click create an instance of a class you're going to invoke The Constructor right away so here we can see that we have a dependency in our Constructor which is on the customer service so we depend on that service inside of our controller now inside of the customer service I added a new member called customers which is an array of customers and then I've created two different functions the first one that Returns the customer's array and then the second one which creates new customers which will take across customer now of course it would be best to have dtos to show the shape of the customer but for now I'm focusing on the provider and then here we're just pushing the new customer into that array now in our controller we're going to add some handlers as you can see we've created two different handlers in our controller one which is of type get and the second one post to get our customers and then to create a new one and then inside of get we can say return this Dot customer service so we can make use of our servicenow DOT customers which is going to return our customers uh my bad cat all customers actually and then here we need to create a new customer we can say this Dot customer service Dot create customer and then we need to pass in a customer so that's why I used add body here if you recall in episode 2 we talked about this decorator so I'm just going to pass the body now if you remember last time in episode 2 I said that whenever we have controllers we need to register them in a module for now I'm gonna register the customer control inside of the app module but it's not a best practice a better practice would of course be to create a customer dot module.ts file in which we're going to put the customer controller and then we would register that customer module inside the app module here and we would import it but just to be quicker I'm going to run the server and then try those apis so let's say npm run startup and now we're gonna see another that might occur a lot of time with you so I want you to be careful here as you can see Nest can't resolve dependencies of the customer controller so this is the customer controller it can't resolve its dependency which is the customer service please make sure that the argument customer service at index 0 is available in the app module context and they also give you potential Solutions so it asks you is app module a valid in sjs module if customer service is a provider that's our case it is a provider because it's noted with ad injectable is it part of the current APP module if you take a look at app module well it's not a part of it it's not in the provider's array it's not registered yet and then they say if customer service is exported from a separate module that module imported with an app module we're going to see that in a bit maybe but now this is the solution the second one the app module does not have the customer service provider registered so we simply just copy that and then in app module we just add this to our provider's array and then we of course need to provide it now if I press save to restart the server as you can see it started now in Postman if I try to hit on localhost 3000 slash customers as we can see we don't have any customer yet let's go to the Post API let's send this with this body of course it's better practice to have a dto and validation we're going to see that in the future don't worry in future episodes but now if I hit get as you can see we got a new customer so this time in this episode instead of just returning everything from the controller we have a service which is a provider because of AD injectable and then we're using dependency injection inside of our controller inside of our Constructor specifically so this is a constructor-based dependency injection and this time the service is the one handling the business logic of of course we could also have customer dot dataaccesslayer.ts for example and then we would have the queries there and then we would inside of our customer service have a Constructor and then inject the Dal service in here and then use it as well but for now we're just gonna be using controller and service so the steps to create a provider is ADD injectable on top of a class then you can inject it using the private for example keyword inside of the Constructor and then you need to register it and the provider's array inside of your module now what I've done is I created a customer Dot module.ts and then I just copied the app module and pasted it here so now here we can remove the customer controller remove the customer service and instead just like I told you before just to keep it clean and have different modules separately what we can do is because right now the application does not know about this module if I save and run because the root module doesn't know that the customer module with the controller and service exists so now if I go ahead and hit on the routes it's not gonna know about them it's gonna get 404 so what we need to do is go to the appmodule.ts and then in the import say customer module but of course we should not forget to change the module name here to customer module and then we save it and in app module we need to import the right path and now if I save again and I go back to postman I create a new customer and then I hit on get as you can see it's working fine but this time we have separated the application into real modules so we have a customer module with its control and service and we have the root module here which is app dot module which just Imports the different modules in our application now it's important to note that since the customer service or this provider is inside of the customer module it cannot be injected okay it cannot be injected and for example controllers from different modules unless you export let's see what I mean now I'm gonna teach you a new command Nest generate or Nest G resource and cancel save so as we've seen we had Nest G controller to create a controller now we can use resource to create a module so we can choose something from here as API or graphql Microsoft sockets we need just API would you like to create code operations no so now as you can see we got a new folder cats I'm gonna remove the testing files so as you can see Cats module we have a controller's array where we have a cast controller okay marked with controller and then we can see that it has injected a dependency to cut servers which is a provider because it's marked with ad injectable and of course it has been added to the provider's array here in this module so it can be used after being registered now let's say we wanted to use this service this provider customer service inside of CAD controller what would happen would it work or not let's try this out so let's say we had customer service here and then let's create a Handler of course we need to import that and then let's create a Handler let's say add get and we need to do return this Dot customer well that's what customer service let's call it there's a customer service dot get all customers just like we did there if I save this all right and we can see here that Nest can't resolve dependencies of the cats controller please make sure that blah blah we've seen this error before and to solve it before we had to take a look at potential solution number two where we just needed to add this provider and its own module since we're using it in the same module now we're going to look at potential solution three if customer service is exported from a separate module that module imported within cast module so there are two steps to do here first of all it should be exported from this module so here on top of impulse control and providers we can add something other than those which is exports and then here we can add the customer service so we can export that to the outside to different modules outside of this current module and the next thing we should do is that module imported with within cats module if we take a look at cat's module nope we do not have this customer module imported so we need to say Imports and then we need to pass in customer module okay now if we restart as you can see it's working and now if I hit on slash cat or slash cats my bad as you can see we got the anti-array because we don't have any customer but if you add one now and hit on send as you can see we were able to use the customer service or this provider in a different module but to do that we have to do two steps first of all we had to export this provider from this module where it lives and then we had to import the whole module inside of this be careful here you should not do customer service as an import okay instead because now if we try to save this as you can see we got an error what we have said should be doing is import the whole customer module once we do that what nest.js does is it goes to this import it goes to this module and then it takes a look at the exports array every provider that exists inside of this export array will be imported Within this module wherever we have the import customer module now just like we had some metadata for the controller decorator we have the same for the providers so at injectable can take some options if we take a look we can see that it takes variable and scope I want to talk about scope take a look at scope it takes an enum of scope so we can say scope so if you take a look at scope we can see that it has different values default for example the provider can be shared across multiple classes to provide the lifetime is strictly tied to the application life cycle once the application has bootstrapped all providers have been instantiated so basically default is treated as a Singleton and that's the default Behavior we don't have to set it manually if you want to use the provider as a Singleton you have requests as well for example a new instance is instantiated for each request processing pipeline you're creating new instances for every request but most use cases we're going to be using the default one so we don't even need to set the scope which is just a Singleton so in our case when we're using or when we when we're injecting this provider and the controller here we're creating a new instance if it does not exist Nest is creating a new instance just like we've seen so whenever we also inject it in a different place just like here for example we're just injecting it again Nest is going to take a look at the scope of that provider being injected okay it's going to see that it's the default one meaning a Singleton so it's going to look at the cache it's going to look does it already have an instance of this provider existing does it have customer service already if so then it would give that same instance since it's a Singleton to this cast control to be used as well if not is going to create a new instance that can also be used and be shared across different modules since we said by default it's a Singleton and we can see what I just said in the documentation here it says Nest to resolve the cat service by creating and returning analysis of cat servers this is explaining that whenever we have cat service in the Constructor of cats controller that NASA is going to resolve it and in the normal case of a Singleton which is the default scope for providers it's going to be returning the existing instance if it has already been requested elsewhere if not so of course going to create it so we can see that Nest is managing the dependency injection for us we can see some more details about dependence injection here it's an inversion of control ioc technique where we delocate the instantiation of dependencies to the ioc container well in our case it's the Nationals runtime system so basically we leave the instantiation of our dependencies to NAS JS that will be happening behind the scenes once we run our application it's going to take a look at our root module it's going to take a look at our providers our Imports is going to go to our modules take a look at the providers to see if we have dependencies then it's of course going to check the controllers to see if it depends on anything else and then based on those relationships it's going to see what it should create before what it's also worth mentioning that in the registration step of the provider just like we said whenever we added to the providers array in our module that's when we are registering it what happens behind the scene that we are registering the provider with the nest ioc container so it's going to be handled by the nest.js runtime system and here we can see some more details if you're interested it says that whenever the nest ioc container instantiates a class controller just like I said previously it's going to look for any dependencies it finds the catch service dependency inside of the Constructor so it's going to perform a lookup undercatch service and then it will look based on the scope let's assume it's a Singleton just like I said previously it's going to create an instance of cat service if it does not exist and then cache it and return it but if one is already cached and the type on disk or the scope is Singleton is going to return to existing instance and use it so far we've been registering our providers in this way inside of our modules we would add the class name and the provider's array so that we register that provider and after that we are going to be able to inject it into our Constructors so that we can use it as a dependency now the documentation tells us that this syntax is a shorthand for the more complex syntax which is this here so instead of actually passing the class name inside of the provider's array we're actually passing an object now with two different properties provide and use class for both we used the catch service name as a value so here they are explaining how the registration process works they tell us that we are assigning the token cat service which is here so provide takes a token with the class CAD service so here we're saying use class and then we're providing our class cat service and this is why we can use the shorthand notation since both of those use the same name so here the token is used to request so the token cache service is used to request an instance of a class which is this cat servers as well that goes by the same name this is why this shorthand term exists and this covers most of our use cases when using providers however in Nest you can create your own custom providers what if you don't want to use the same token as the class well let's take a look here here we are providing the cat service name as a token and then we are instead of doing use class and giving it cat service we're doing use value and giving it a mock cat service if you take a look at Mock cat service as an object so now this token whenever we inject it to catch service inside of a Constructor is not going to resolve into the CAD service class anymore instead it's going to resolve into this object mock cache service of course this way whenever we have a token that is different than the provider that it should resolves into we can no longer use the quick way since it's not the same name anymore so we can no longer use this way that we've been using before and we can read the documentation the catch service token this is going to resolve into the mock cache service mock object use value requires a value which is an object that has the same interface as our cache service which is this object here so whenever we inject it in a Constructor is going to resolve into this mock cat service instead of the actual CAD service and here they say that this works because of typescript structure typing so you can use any object that has a compatible interface including a literal object like we've seen here or a class instance instantiated with new now in both scenarios this one here where we use the mock catch service is a value and then here where we use the CAD service the use class we used a class name cache service and catch Service as a token name however in sjs you can also use a string now you should be careful whenever we set the token as a string like that here we can no longer inject it this way so before we used to say Constructor private cache servers cat service and then since we have already registered this catch service inside of the module Nest would take a look if it's this way then it knows that the token is catch service and the provider is also got service it resolves into the same class or it would take a look and then it would see that okay catch servers is going to be resolved into this object and then it is going to inject that object in that Constructor however now in our case we have something that doesn't exist like whenever we give it this string this is not an existing class well thankfully we can use this way we can say add inject in our Constructor and then we provide the token name so Nas is going to look at the token name and then it's going to look at our provider's array and it will see okay token connection should use value connection here comes from a different file it's an object so whenever we see add inject connection this token is going to be resolved to this object here so it will be injected and can be used in this class let's see one more important use case for custom providers so for example here we have okay we are passing the conflict service class name as a token because provide takes a token and then use class here as you can see we have a condition we are checking if our application is running in development environment or not if it is then this token should be resolved into development conflict service class if not it should resolve into production config service so we can see that use class is dynamic also here note that we created or reused the properties provide and use class inside of a new variable that we created instead of just writing them down right here right away but it's the same we could have just taken provide and use class and put them here in an object without using this extra variable this is just to organize your code here to explain what I just said that they have used the config service class name as a token we've seen that multiple times now so far and that for any class that depends on config servers and a Constructor if we have config Service as a dependency Nest is going to inject an instance of the provided class either development conflict service or production config service depending on this condition right here you can also check Factory providers and Alice providers I'm not going to cover them in this video you can check them out in the documentation of sjs and fundamentals custom providers we already covered 90 of the use cases for providers and you're good to go but if you want to check more information I invite you to the documentation to summarize it quickly in this video we've seen that whenever we have a service or maybe a data access layer or helpers or whatever if you want to inject them as a dependency somewhere else in our application we would need to use the add injectable decorator which takes some options which in which we can specify the Scopes by default it's the Singleton scope meaning it's gonna be one single instance created by the ioc container by an sjs and use across the application so it's going to be shared and a provider is just something that can be injected as a dependency just like we've seen for example the service can be ejected as a dependency in the controller for example and then we should not forget the third step which is to go to our module and then to add that provider and the provider's array we've also seen that if you want to use that provider in a different module in our application we would need to add it to the exports array of our module and then in the second module where we want to make use of that service we would need to import the whole module so here cats controller depends on the customer service we have injected the customer service and cache controller to be able to use it we would need to first of all export it from the customer module and then inside of cache module we would need to import the whole customer module be careful again not customer service but the whole customer module we also covered custom providers and how we can use different tokens in different ways and then we can use a class or a value to be injected I hope this video was clear if you're still watching you obviously like the video so please leave a like subscribe leave a comment and I'll see you in episode 4.
Info
Channel: Computerix
Views: 3,511
Rating: undefined out of 5
Keywords:
Id: 8T6lEvdbS7Q
Channel Id: undefined
Length: 27min 36sec (1656 seconds)
Published: Sun May 14 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.