Learn Nest.js from Scratch by building an API

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi and welcome to this video in this video we'll have a look at nest Jas a pretty fun note reyes framework which has been gaining some traction over the past months spin around for much longer than that though and it's fun to work with in my opinion in this video we'll have a look at it I'll introduce you to its core features will build an entire mini API with it and therefore by the end of the video you hopefully have a feeling for what it is what you can do with it and whether you might want to dive deeper into it so let's start so what is nest Jay s nest Reyes is a node.js framework so we use it on the backend on a server side it builds up on expressjs though it's flexible and theoretically you can also use other foundations so to say but in its base version and in the ocean you'll most commonly use it builds up on Express j/s so under the hood of nest JS were expressed J's which of course although is a node.js framework and therefore the question is what's the idea of that framework of a framework nest Reyes adds some cool ideas to the server side to know chess which we have had before in note reyes frameworks it embraces typescript so whilst you can actually also work with it without typescript it's definitely meant to be used with typescript and I know some people don't like typescript for some people this might be a reason to not use it and that's of course fine but if you're open to typescript or if you're a fan of typescript then you will definitely get a lot of benefits out of nest shares because it uses typescript because they're extra type safety and so on can save you some errors it also introduces the penalty injection and modularity and it's therefore a bit like angular for the back end indeed it's heavily inspired by angular the team or the founder of Nesta as the team behind SAS says that themselves and they borrowed many ideas from angular though it's really important to understand that of course nest raius solves a totally different problem it runs on the server side it's not an alternative to angular it's not yet an our front-end JavaScript framework which you would use to build user interfaces it's indeed a framework for the backend so it just borrowed some concepts from angular and where applicable transferred them to the back and so to say our concept of course are not taken over because they wouldn't make sense on the back end so typescript depends each action modularity some cool features uses a lot of decorators as you will see really nice to work with and you can use it to build MVC apps so where you also render your use on the server with templating engines like parkour handlebars you can use it to build a REST API and I would say that building REST API is maybe the the main thing of nest JS though that sounds like your things are hard to do they are not but building REST API is particularly easy let's put it that way but you can absolutely build MVC apps as a chess set and you can always use nest j/s to easily build graph QL ap is because nest is basically also provides a built-in wrapper around Apollo which is a popular graph QL server framework or library and that's easily integrate able into nest yes so you can build any server-side application with nest chess that's the key takeaway now because it uses typescript and forces modularity and so on it generally forces you to write clean code and forces a clear project structure and gives you some clear ideas on how you should structure your app where to put which code and therefore separating your concerns is particularly easy and hence nest J's can be used for applications of any size but especially more complex or larger applications might be a bit easier to build with nest chess because you are forced into certain patterns which make your code easier to manage easier to understand easier to reason about so that's nest chess in theory let's have a look at it in practice now nest Transcom is obviously a great starting point there you can dive into the documentation to learn all about nest chess and all about the many features it offers we're not going to cover them all in this video or on this channel but we'll cover the most important ones of course and for that let's start with creating an Astraeus project nest j/s gives you a CLI which you can install with this command and for this command and because it's a No Trace framework you of course need nodejs installed so make sure you have that installed from no chest or ik simply the latest version there so thereafter you can run this command and if you work with angular which by the way is absolutely not a prerequisite to work with nest chess then this might look familiar because an angular also have the add angular CLI which you installed similarly and there you would run ng new to create a new project here its nest new but again it borrowed some ideas it's not a copy it's not an alternative and it works with any front-end your either n during your views on the server anyways or if you're building an API you can connect your nest API with react apps with angular apps with view apps with when the let JavaScript apps whatever you have so it just borrowed ideas from angular so I already got no chance install so let me copy that command here and run it in my terminal here I can simply copy it in or paste it in here now if you're getting a permission error when running this you might want to add a sudo in front of this and thereafter enter your password if you prompted on Windows this should not be required and thereafter this will simply install this nest jsut lie once it is installed we can use it to create new nest chairs projects with it so here it finished installing now you can navigate into a folder where you want to create your new nest chess project and then simply run nest new and then any project name of your choice now limit nest chess in true but again the name is up to you and now this will create a new nest chess project in that folder in that nest chess in true folder which it will also create now if you've got multiple package managers installed as I have you're asked which one you want to use I'll stick to NPM um yarn would be an alternative if you have that installed you can of course use that as well and now it's setting up this folder installing all the dependencies this project has so all the NPM dependencies we have in there and once this is done it also shows us the commands we can run to start the nest as server and actually not the development server but the web server as we could serve it on the server we deploy to on the host we deploy it to will not use that instead I'll open it in an IDE and for that I will use Visual Studio code but of course here as well you can use any IDE you prefer and Dad is the project opened up in my IDE now I'm using the dart plus theme here and I also got the material I an extension installed which is totally optional but which leads to the I consider seeing here and then this is the project as the nest Eli creates it for you now on first look this can look overwhelming because we have so many files and and also some folders here but let me quickly walk you through them because code is simply a configuration folder for my ide has nothing to do with nest chess node modules holds all our dependencies all the installed dependencies so of course we're not going to change anything in here these dependencies are managed with the package.json file but that is something you should know because for this video I actually do expect that you have some basic understanding of how node and Express works and they offer you probably know what a package.json file does the source folder is more interesting in there we have the source files we are going to work on and they end with dot ts4 typescript and i'll walk through these files and what what's inside of these files in a second got the test folder where you could write your own and to end tests we're not going to do this here but of course Nastia supports this get ignore config file forget for a prettier which is for code formatting here nest Eli configures the nest is Eli and by default you can leave this as it is you could change the folder where your source code lives and if you want a to but I'll stick to SRC node Mon dad is actually a tool which is used under the hood by nest chest to launch the no it's server which we in the end have of course because it's an old framework and then automatically reload or relaunch that server whenever we change something in our code so node Mon makes developing easier because we don't have to manually restart our server and that's what this contact father also controls the package so Jason fall of course with all our dependencies and then a couple of scripts we can use to start various processes here and we'll have a look at this in a second and then some typescript configuration for linting and for the compilation of typescript unless you really know what you're doing you shouldn't change this because default here should actually be fine now in the source folder we have five files this file here the spec file that's just a testing file for unit tests I'll delete it here because we're not going to run unit tests here and then we're left with four files now the main TS file is actually the entry point of your nest reyes application so when you later execute a command to start your development server or to build your application so to say what will happen is that the code gets compiled to JavaScript because no J's can't run typescript so it gets compiled to JavaScript and of course then it also gets bundled it gets merged into a single or in a couple of JavaScript files as an output now the code in here in the main J's file will end up in the bundle as the code which actually runs first when you later start this entire app with your node server with the node command and in here we have the bootstrap function which gets called here and what we do here is we use the nest Factory which is a tool provided by nest Jas to create a new nest application you could say we're passing in the app module which is coming from another file and we'll have a look at this file in a second again if you work with angular this might look familiar to you and then here we start listening on port 3000 and if you work with Express trance which you all just show tour which is at least recommended then this might also look familiar to you in Express trace apps you also create a app by basically calling Express as a function of course here that wouldn't work because I'm not using Express here but in an Express app you would basically say Constable's Express like this and then on the Express app you could also call listen and because nest is under the hood wraps Express chess we indeed do you is expressed as here just with the extra nest chess layer in between but let's have a look at the app module then what's in there the app module in the app module here's foul that's an important thing in nest chest because as I said it embraces modularity and that simply means that nest Reyes doesn't by default take all the files you have in the air you have in your source folder and look into them to see what they do and compile them together instead you need to tell nest chess which features make up your application an important parts in an estrellas app are rollers and providers controllers are the meat of the app controllers control how you handle incoming requests they are responsible for accepting incoming requests then doing something and then to return a response that's the job of the controllers they handle requests and they send back responses providers on the other hand are extra services extra classes so to say which you can inject into controllers or also into other providers to provide certain functionalities for example you could have a service or a provider and the name is kind of exchangeable here so you could have a service which reaches out to a database to fetched item and then a controller can use that service to let the service get the data from the database so that the code in the controller is relatively lean and you don't have your database access code in the controller but instead you have that in the service and that's that modularity where you have a couple of different tools where each tool or each part of the app has its job and therefore each pot on its own stays relatively lean and focused and the module here the app module and you can indeed have multiple modules and bigger apps simply bundles up a couple of controllers a couple of providers which these controllers can use or which other providers can use as well and you can also if you had multiple modules import a module into a novel and that's how you link your entire app together youtell nests chess ok this module has these three controllers these five services and also depends on or wants to use some our module and that our module might also have 10 controllers and 5 providers or anything like that and this is how this all is connected and modules are typically split by features in your app so if you're building an online shop you might have a module for products maybe one for the shopping card one for a user authentication something like that one important thing to note of course is that the module here is just an empty class all the magic happens because this at module thing is added to it that's a decorator and that's a feature provided by typescript module is coming from is the at thing here is important to attach it as a decorator and decorators can be attached to classes to methods and classes to properties to arguments and methods they can be attached to a broad variety of things and of course it depends on the decorator you're using where you can attach it module is a decorator that should be attached to a class and then you pass in an object to that decorator which you call like a function to configure that decorator basically and behind the scenes this will basically add some metadata or transform this class this this object that's created based on the class transformed it into something else into a more complex object that's some magic happening behind the scenes done by typescript and by nest Jas and this is simply a very convenient way of setting this app in the app controller if we have a look at this we also have a class they are we actually do have some content in the class but we're also using decorators here we have the add controller decorator which is coming from nest chess and the add get decorator coming from SJS and here we also see a decorator that's not added to a class but to a method instead now as I said controllers are important for handling incoming requests and sending back responses now if you have a controller like this with just add controller where you have no argument here in add controller then this controller will simply handle incoming requests to your domain comm slash nothing so to your route route so this controller by default will have a look at requests that reach your domain slash nothing if you had a request that reaches /pha or slash users it would also reach that controller because it still of course well starts here at this segment but then you would need a method in here which explicitly targets slash users because that's the our thing this controller has no filter regarding the path of the incoming request it wants to handle we could pass a filter here we could for example pass products in here and now only requests that start with your domain slash products would reach this controller and if you at products one it would all to reach this controller if you had users however it would not reach this controller because this clearly does not start with products so that's how this works but let's get rid of that to start with slash nothing and now this method here that's the second thing this method now can be named whatever you want but with this decorator this method will get executed automatically whenever a get request reaches just slash nothing with slash users this would not get executed because this here this controller thing doesn't filter the path at all as I just explained this year however does mean only nothing only an empty path triggers this method so really only requests to slash nothing if you would want to handle requests to slash users you would have to pass users here and if you had here products and here users then only products users would trigger this method and just products would not trigger it just users would also not trigger it because we are only handling requests that start with your domain.com slash user app products and then here we only have one method that looks for another path segment so to say what should be users so after this path segment the user segment therefore only products users would be handled so I hope that's clear at least a bit well of course see this in practice once we add our own routes or an additional path which you want to handle the idea here is that you route requests with decorators if you work with expressed as you are used to setting up the Express router or using middleware with path filters to handle incoming requests here you filter requests with at controller and then with at gaps and of course by the way you also have at post at patch at port at delete to also handle such requests with such words so a lot of talking let's have a look at the last building block we see here and that's the service this constructor thing here that's actually dependency injection in action and if you work with angular again this looks familiar what we're doing here is we're adding a constructor which every JavaScript in typescript class can have and we're specifying one argument which we expect to get which should be of type app service this is how you assign a type and typescript by the way : after the argument name and then the type name over here for example for this method a colon after the method and then this is the return type but here we're assigning a type of app service to this argument app service and now of course whoever is creating a new instance of this class needs to pass in this service now who is creating a new instance of this class well we are not nest chest Estes Nest Reyes will actually create an instance of this controller and therefore its nest Jas duty to pass us such a service object and that is possible because we added app service here as a provider with dead we're telling nest Reyes hey this class here app service is a class which I will probably use in some of my other classes I want to inject it there so please be aware of it here is the class name here is where you can find it be prepared to create a new instance of this class so that you can pass it to me when I request it in a constructor and that's exactly what's happening here we need a new app service object here when the controller gets created and s Ches creates such a App Store's object and gives it to us because we told it how to do that by adding app service to the providers here now side note if you are familiar with angular there whatever service you provide by default is available application wide in SaaS services are really scoped to the module you provide them in so if you had multiple modules and you provide a service in this module then in an error module you could not inject this service automatically you would have to provide it there as well but that's just a little side note so that's dependency injection in action this type definition year or declaration is very important because with just the argument name nest Reyes would have no idea which type of object it should give us right because the argument name well to us humans it's clear that we want the app service but you couldn't named as something and that would also be allowed and then there would be absolutely no chance of inferring what you mean so by adding the type here however Ness jeaious is able to find out which type of object it should give us then it looks in the providers list and if it finds to type there it can create that now and the app service itself is just a class with the ad injectable decorator which makes sure that this can be injected it's also imported from Ness Chasse and then here we have one method in the service and as I mentioned here in services you could be reaching out to a database you could be doing all kinds of stuff here you basically want to do your heavy lifting you want to have the logic so that your controllers can stay relatively lean and can focus on handling the incoming requests and then just putting together a response so these are the basics now I talked for minutes now and we still haven't written any code I hope the basics are clear though so for now let's start the development server by running this command here start death so I'll open up my terminal navigate it here into the project folder and then I run npm run start colon death and this is now starts a development server which watches our files and automatically recompiles and restarts the server whenever we change something now let's have a look at this remember that it was port 3000 where you can see this and if you visit this you should see hello world here because that is what the app controller in the end returns here it returns the result of GATT hello in the service and the service returns hello world that's another important thing by the way so what we're turning here is a string not a response object again if you work with expressjs you are used to getting a request and a response object here in the methods were in the functions that handle the incoming requests and then you would do something like response whoops response send or response jason-2 sent back responses right now here Ness Chasse does that work for you you just return the body of the response though there still are ways of setting extra headers if you need that but here by default you just were turn the body and then SJS has a look at the type of data you're returning and based on the data it sets some default headers for the response so here we're returning some text in the end and when you're returning text let's see which header nest is infers by opening up the developer tools going to the network tab there and now let's reload this page and inspect this request which is the request responsible for this page there at the response headers we see that the content type is text HTML so if you're returning some plain text nest Reyes automatically infer that this is text and that therefore probably should be HTML and hence it appends or it sets this content type header on the other hand if you would return a JavaScript object here like name max something like this and now I'm getting an error here because I'm actually stating that I want to return a string so the solution is to either set this to any which is quite forgiving or you can define an object type here by using curly braces here and then define that you'll have whoops without quotes here that you will have a name property in that object which will then be of type string then you are telling typescript that you'll be returning an object with a name property which is of type string and now we're alright and if you do that so if you return such an object here and you reload then you see it's getting printed here but if we inspect the response the content type is now automatically set to application Jason Sohn SJS automatically transforms this to Jason and sets the appropriate header so it's really smart regarding that and that's of course a huge help because typically that's exactly what you want if you still would want to set your own headers you can do that though you can import header here from nest a is common like this and then add an our decorator to this method he had a decorator and there you can set the name of the header like content type and then the value like text HTML and now this header would be a pendant or would be added to the response and it overrides to default headers nest chairs would otherwise set or it allows you to add brand new headers to the response and now we're saving this and we reload this let's have a look we see now this is the content type set here so this is how you can work with nest chairs how you can return data and with decorators you really control a lot so that's something you can get used to in SJS you'll use tons of decorators so that you don't have to write everything explicitly in code but that you instead simply you can set some configurations in different places and nest chairs will then automatically do the right thing now still it's a boring example let's build a little dummy API here let's say an API where you can add new products can then delete or update these products and of course also get a list of all products or a single product so a very simple API we could be using as part of a shop let's build this now for that to also work with multiple modules let's actually create a new subfolder here which is not required but just to keep our code organized and I'll name this product and in there let's add a product dot controller TS file that's the naming convention for these files you would describe the feature then the type of file or what you want to have in there and then of course dot TS as the extension now in there we export a class products controller that's the naming convention for the class you also have to the type of feature in there so it's for the products feature but then it's also a controller so you describe both in a class name and by the way we of course use a different import and export syntax then you normally do a note or Express Apps we're using DES modules imports index instead because typescript supports that so that's why we have that so if that we have that products controller now you learned that the thing that actually turns this into a controller is the add controller decorator so let's add at controller here and add controller needs to be added or imported from nest J's common and my IDE added is automatically in case yours doesn't you have to add this import manually so now of course this should be a controller that kicks in for requests that reach our page comm / products so let's add products here as argument - controller so that we filter for requests that start with / products and in here let's start with a method that allows us to add a new product now for the moment I'll by the way just manage my products here in memory but I'll just show you how to write them to the database no worries so here let's start by adding a new method maybe add product name is up to you and this should handle incoming post requests now because since we'll build a basic API here I want to follow the common rules where when you add a new item a new resource on the server you send a post request so here I want to handle a post request by the way you might want to check out my complete series where we build a REST API with node expressed from scratch there I dive way more into these API and rest basics and of course we all have a look at node and Express there in case that's something you're interested in - so here we need to import post and with that we can add at post here we could add a pop filter here if you wanted you but I'm actually fine with managing incoming requests to just products and add product now needs a function body of course but also a return type and here I want to return an object and for now I'll set this to any we can fine-tune this later once we have a clearer idea of what we want to return so here we now need to store the product as I said for the moment not in a database but simply in an array which we manage here in our nest chase application and which therefore of course is lost whenever we restart business chase application but for the moment this will do now we could manage everything in that controller class and it would work but that's exactly not the idea of nest Chasse instead you should split your code you should distribute your code and should have leaned focused parts in your app so let's add a products service a product service TS file and in their exporter class products service this is now a way better class for managing this now to make this injectable here we need to add at injectable and that's a decorator imported from nest a as common as well and now here we can have a list of products so we would probably have products here and yeah the type here should be an array but I wanna be more specific an array of what so let's define how a product should look like and here a good practice is to define a model for this so let's define a product model maybe in a product on Model T as file to be clear what's in that file and this is now a file which will be holding a plain vanilla typescript class so without any special decorators and I'll name this product like this you could name it product model but I want use this to create new products basically calling new product and I think it looks nicer than if you have just product and not product model because here we will be the ones creating that class or the instances based on the class now what do I want to have in that a constructor so that we can indeed call new product and now here which properties do we need well let's add a couple and then let's accept them in a constructor product will probably have an ID which could be a string it will have a title let's say maybe also a description which can be a string a price which could be a number and yeah doesn't look too bad you could of course add more like an image URL or anything like that but let's go with this basic set now let's accept all of that as arguments you can name these arguments like the properties here or name them differently that's up to you so we expect a string here a title an ID a title description and also a price and then in the constructor body here we can simply call this ID equals ID this title equals title this description equals desc this price equals price so that we store all the data we're getting in the constructor when people create new products in the properties here now this is such a common pattern that types could has a shortcut for this so that we don't have to write all this boilerplate code you can get rid of your properties up there and of your constructor body and instead you can simply add an accessor in front of your arguments like public or private defining whether this should be available only inside of this class or also accessible from outside I want to have it accessible from outside so I'll add public and by adding the successor in front of all these arguments and now I'll name this here description by adding this these are automatically stored in equally named properties of this class so types could automatically adds properties to the class with it which have the same name and stores the incoming values in these properties so that's a convenient shortcut typescript offers suppose that we define how a product should look like back in a product service I now want to manage a list of products and dad should now use our own product type so let's add an import here where we import product from dot slash product model and then here the type after the colon is product but then an array of products which you defined in typescript by adding square brackets there after so now we're saying that we'll have an array of products in here and initially that's an empty array and if you would now try to add a string to that array you would get an error by typescript because it only accepts products now here in the product service we can add an insert product method or whatever you want to call it name is totally up to you and that ends our product method should do just that it should simply insert a new product here into this array now you can either accept the finished product here as an input or the individual inputs so here you could expect let's say a title description and a price not the ID because the ID can be generated we pass here and then here you could create a new product by calling new product so using our product class now and there for the ID we now will need a ID and we have no real way of creating a unique ID so what I'll simply uses I'll use new date here Q string it's not a perfect ID because it's not not unique you could create two products at the exact same time stamp but here in this app this will not happen therefore as a dummy ID it will do if you needed a real ID there are packages which you can install note Express packages because you can by the way use any Express package in SaaS apps you could install packages that generate real unique IDs so for us this will do though let's now also forward the title the description and the price to the product constructor and price should be a number here of course so that the types match and with that we're generating a new product with that new product generated we can reach out to products and simply push our new product into that array like this so now with that we have a method to insert a product back to the controller now here I now want to inject that service so that we can use it to inject that we add a constructor here to the product controller now we won't create a products controller nest chess will do that for us and then the constructor we tell nest chess what we need here and here I want you have an instance of my products service the name is totally up to you but important it has to be of type products service and you need to import product service here so that you can use this class as a type otherwise typescript doesn't know where this is coming from and I'll again use the shortcut where I add private in front of this to make sure that this argument is automatically stored in an equally named property you can also add read-only which is another types good feature this makes clear that you will never replace product service with a new value which you of course don't plan to do we will stick to this product/service instance we are provided with so now an ad product we can use this product service and then call ins our product here now the question is where do I get my data from so the title the description and the price and the answer is probably from the incoming request that incoming requests should indeed have some data attached to it it will have a request body now again if you were using Express chase you would be getting a request and a response object here and then you could call request body and extract data from there this is not how it works in SJS whilst there actually is a way to get these requests and response objects that's not the way you should use instead you again use decorators there is a body decorator which you can import from nest as common and you used it here in your argument list there you can add add body and thereafter an argument like let's say prot title which will be a string and embody you now can specify the field in the incoming request body that should hold the value which nests chest will then extract for you from that body and give you in this argument and there let's say we're expecting a title field in the incoming request well then we would set up this decorate like this and now nest J's will look at the incoming request parse the incoming request body so converted from chase into a JavaScript object and then look in that object for a title property and take the value of that property and give it to us in this product I'll argument we can of course repeat this to also get our description let's say we expect this on a description here and then we have the prot desk here which is also a string and we do the same for the price and therefore here we would have prot price which is let's say a number now one important note you could also say at body and get the complete body here and then define how this object looks like so that this has title key which is a string a description key which is a string and so on you could do this it's totally up to you which approach you prefer I think this approach of having three different arguments is really clear and straightforward but you could use another approach if you wanted to so now we got all the data and hence we can therefore forward prod title now broad description and broad price and therefore we're now calling inter product in a correct way leaves us with one question what do we want to return here you can of course return any response body you want I want to return the idea of the product which was generated and of course that's a dummy ID but nonetheless so here I'll have my prod ID in the service which is the state and which I then use here and I'm just storing this in a constant because I want to reuse this and also return this year so here I return fraud ID now by the way we could now also add a colon after the argument list to be clear about what we're returning here which type of data and that would be a string but you actually don't need to do that because typescript has a feature called type inference which means it's actually able to infer that you're returning a string because you have a return step and here and there well you return a string so inner product returns a string returns the ID in the controller we can therefore return the result of insert product and return this ID back to the user however that would be a string and the effort would be treated as HTML typically in an API we want to return JSON data so of course we could set our own header but then a simple string would still not be valid jason so instead here I will return a new object where I have an ID key and that's then my generated ID which has store in this constant which I then set as a value here and now we're returning this and now we can always be more specific about what we return here or not the find return type at all because again typescript is able to infer this now since we're returning an object here and the same would be true for lists by the way nest Ches is able to infer that we want to set the jason the application chase and content type header now we have a post route one crucial thing is missing though do you know which one a module because as I said you need to tie your features together with modules so let's create a product start module dot ES file and in there export a class products module what's module written like this which needs to get the add module decorator which you import from nest as common you pass an object to this decorator and then you can setup imports so our modules you might be depending on which we don't have here but you certainly will set controllers there we have our products controller which you need to add so we'll need to import products controller as well and then pass the type so don't create a new instance of this class just pass the class name here two controllers and then for providers we also need to set this up otherwise dependency injection won't work and there we point at the product service and you also need to import that now we got this module set up and still it would not work because we need to let nest chess know about this module as I said earlier nest chess does not automatically scan all your files and then make some guesses instead you have to be really clear about how your app is structured now we got our app module and of course we could remove the app controller and service because they're not doing anything we would use here I'll leave them here though but one thing we need to do in the app module is in imports we now need to add the products controller because we kind of depend on this of course the app controller doesn't use anything from the mob from the products controller but the app module needs the products module because this makes up our entire app and you need to link your app module to the other modules that also make up parts of your app so here an app module we point at the products module now and with that and you also need to add an import for that so with steady imported with that added to imports here we're ready to test this now one side note about imports by the way this import statement here is a language feature which typescript needs to know where this products module I was coming from so this is required by the typescript to tie all your files together and bundle them together this year is an SKS feature it's named imports because the nest chess team chose to name it imports it has nothing to do with file imports it's just a nest race feature to link modules together so now let's test this let's save this this restarts our API and now we can send a post request to products now how can we send a post request there are different ways pretty convenient and easy one is to use postman which is a tool that allows you to send requests HTTP requests to api's you can download postman from this website I already got it installed so I'll just start it so here I started it by the way you don't need to sign in at the very bottom there's this small almost invisible text which allows you to skip sign in and then in here in this interface you can create your requests and here we can send a post request to a URL which is of course localhost 3000 slash products right because our server is running on localhost 3000 we're filtering for slash products and we don't need to set anything else here no special headers but of course a body now our body will actually be raw and then here in the drop-down you can choose Jason now Jason is written by adding curly braces and then your keys are between double quotes and your values are as well at least if they are text numbers are not between double quotes and then we'll need a title key because we're expecting a title here we're also expecting a description and a price so a title like test then the description key here which is this is first product and then a price key and this is a number of values so we don't put the value between quotes here with that let's hit Send here and that's looking good I'm getting back my ID and that simply my date here right because we're using that as an ID and therefore the requests definitely reached this end point and we had no error otherwise we wouldn't have gotten back this ID so that is working we're storing our product in memory at least the next step that would make sense is that we can also get all products and a single product so let's add a new method here in the product controller get all products and the names of the methods are completely up to you don't matter at all this will handle get requests so let's import get from Ness J's common that's this decorator which you now add here again we could filter for the path but we don't need to we want to handle a request to just slash products and then here we need a method in the service that gives us a handle to all these products in the service by the way we can also add private here in front of products so that the products array is really only usable from inside the service and our service methods are the only way of interacting with it this ensures that we can never added or directly tap into products without going through a method which allows us on the other hand to make sure that we always follow the same steps for adding data or for removing data for example that we always generate the ID in the same way so now here we could add fetch products or get products whatever name you would prefer and here I want to return this products but now we have to be careful if you return like this it will work but since arrays and objects are reference types in JavaScript and in case this doesn't tell you anything below the video you'll find another article in video by me where I explain this since they are reference types what we actually return here is not a copy of that list but a pointer to that same list memory so now we indirectly open up a door where the controller that returns our products or gets our products could edit the products now of course you might say I'm writing the code why would I mess with my own code well that's correct but consider that you're working in a team where other people might not know how you plant that this should be used or that you're coming back to your code after six months and you forgot how you wanted to use this you can introduce strange bugs if you start at products from outside this service and therefore here we should return a copy and we can create a copy is simply by calling slice here or now alternative by returning a new array by wrapping this into square brackets but now we would have an array with an array in there but by using the spread operator which is a modern JavaScript feature which is also supported by typescript we're pulling out all the elements off products and add them as new elements to this new array now the products themselves are objects and we don't copy those so these are technically be editable from outside if you would want to make sure that this also isn't the case you would have to call map here and transform every product to a new copy of the product I'll not do that here since it's way less likely that we start editing single products in the return list but if you want it to ensure that this can't happen that's a pattern you also might consider so now I'm returning my products list here in the products controller we can therefore simply well in the end returned this product service get products this will return a list of products and typescript is smart enough to infer this and this will automatically alter be converted to Jason just like if you return an object if you return a list that's treated as Jason because it makes up where it can be transformed to valid Jason and that should be all we need here you could of course wrap this into an object where you have like your products in a product key and then have some other key in case you need that but here I'm fine with just returning the list of products so I'll just return that and that's that really lean controller thing I was talking about right so let's save this and let's go back to postman and now send a get request to the same URL I'll open a new tab up there so that I can easily resend a post request because this is not all safe there send that to the same URL we don't need to set up a body because you can't have a body on get requests don't need to set up anything else let's just hit Send and I'm getting back an empty array now this makes sense because the server restarted when we changed the code and we're only managing our data in memory when the server restarts that memory is cleared it works however if I now add a new product with post and I'd then get my products again now the server wasn't restarted in between so our memory is still there and here therefore here we have our products list if I add another product second test this is a second product and I then get my products we see both products here now of course saving products in memory is not a production ready solution this is just so that we can focus on controllers and so on first I will show you how to also manage the data in a database so we can add products we can get all products how about getting a single product that would be nice too wouldn't it in a controller we could have a get product method and now here this of course all the handles get requests now one problem we have is if you have two methods in the same controller with the same decorator then the first one will win so this will never execute a get request to slash products will always execute this method this method has no chance but actually here I don't want to execute this method for just slash products because I want to get a single product so the incoming requests should actually contain some information about which product I want to return now you can't add a body to get requests as I mentioned so we can't put that information into the request body but we can put it into D URL so that we send a request a get request to something like products one if we had IDs that are that simple if we used that then we could look for that URL and return a product the problem is of course I can't look for one here or for queue because then I would only handle exactly that product we need to be more flexible if you know Express chest you know that you could set up dynamic segments by adding a colon and then any identifier of your choice like ID or prod ID but that's up to you and you can indeed do the exact same thing in SAS with this syntax you're saying well there should be an extra path after this one I don't know what the exact name of the path is because it's dynamic but I want to get the value that's in the path as an ID parameter and you can get that parameter now with the param decorator which you need to import here in your method argument list by adding at param and then again you can add it like this to get an object with all parents in there or just as for the body you add your param and here I used ID up there so we have to use ID down there and that gives you that parameter value and that will be a string and that's how we can extract data from the incoming URL from the parameter list and therefore now we can add a method to the service to get a single product here I expect to get the product ID which is a string as an argument and then I want to return that single product from my list of products so let's identify the product and store it in a constant by reaching out to this products and to get a product by ID there we can use find and fine takes a function where we get the product because this function we pass to find runs for every item in this array and then this function has to return true if it's the object or the item we're looking for and false otherwise and we want to return true if prod ID is equal to product ID now we got the product and now we can return product like this or also to copy it wrap it into square brackets into curly braces excuse me and use the spread operator there as well because just as you can use it for a list you can also use it for an object to create a new object then take all the key value pairs of the old object and add them as new key value pairs to the new object now we have a problem though and that should be free equal signs we have a problem we might not find a product for a given ID so add a check and check if product is null or to be precise if not product basically then I don't want to return my product now we could return a different response or we just throw a new error an S Ches can then send a error response however not if we throw an error like this instead we can now import not found exception for example and there are different base exceptions nest Ches offers for a common problems like you didn't find the data you got invalid client data for validation something like that so here we have a not found exception and we simply throw a new not found exception here an S Ches will then automatically send back a 404 response which makes sense to also use that status code you can pass your own message here like could not find product and with that we're making sure that we're handling this case as well now to have a bit easier IDs to work with I'll use something else then new date I'll use math.random to string still not unique we can of course generate the same random number twice but at least a bit shorter than the date so now we have get single product here we're generating easier IDs we're hopefully finding products or throwing errors in the controller we now can return this product service get single product for the prod ID we extracted from the incoming requests so now let's save all of that let's go back and let's first of all add a new product because we restarted the server all data is lost therefore so now we should have that in there that's the ID so let's grab that and let's now look for for for all products first we can see it here and now for this specific product by adding that ID after slash products send a get request there and that's looking good now let's send a get request for some other ID which doesn't exist and we're getting a 404 error with our custom EMA error message so that's perfect that is working exactly as it should with dead we have a method for returning a single product as well now how about updating a product we can add it we can get it but we might also want to change it now for updating there are different strategies you could accept incoming requests which give you some an object with some updated property and then you merged that with the existing product or you expect to get a complete replacement so a complete new object I'll go for the merging strategy and therefore here we could have an update product method and our controller and parameter that now makes sense or a HTTP word excuse me HTV work that makes sense is patch you could use put but port would be better if you plan on replacing the current product with that product you're getting instead I will use patch because again I want to merge the changes with the existing product so I don't replace it I just replaced the fields that changed so add add patch here and just as with get product here we of course need to know which product to update so I expect to get that ID in the path here as well now a patch request also does have a body so we could also expect this in the request body but logically I'd say it makes more sense if the identification criteria is in the URL and the update data then is in the body so here we get the ID in the URL and we can therefore extract that just as we're doing it up there but as I said we'll also have some data in the body and there will have a title our broad title which is a string we'll have a description our products which is a string and we have our price and of course some of these fields might not be there but that shouldn't be a problem so with all that extracted some fields from the body and the parameter it's again time to go to the service and now add a method for updating a product here as well so here we can add a new method name it update product as well or name it differently whatever you want an update product expects to get the Product ID of course but we also will expect the title here the description and the price all of that is expected here as well and then we first of all need to find the product so we can repeat this and we actually also want to throw an exception if we don't find the product so since we'll run that exact same code we can of course also outsource this cut it from here and add a new private method in this service because we'll only call it from inside the service which we'll name find product there I want to have my ID as an input and now here we can have that logic just rename this to ID because I renamed the argument and then simply return the product here and then here we can just well in the end get our product by calling this find product and forwarding the product ID and that's the same thing we can do an update product now and this make sure that we get our product or fro the error if we don't find it and then here I want to update my product so actually what I'll also need is the idea of that product so I'll tweak does find product thing a little bit here and I'll actually use find index here which will give me the index of the product in the array so the position then here I can still get my product by using this products whoops products and then use the product index so this still works as before but here we can now return the product anti index maybe an array first element product second element product index and now here in get single product this year of course returns an array now not a single product anymore and in fine product we now can also add a return type here to make it clear that we'll have an array as a return value where the first element will always be a product and the second element will always be a number this is a so-called tuple type and type script so it's not an array where we can have an infinite amount of our elements or different types of data in different positions no it will have exactly two elements first element is a product second element is a number and now in get single product we can get our product by using the first element we're getting back here and an update product well there I get my first element which is my product and then also my index with this find product product idea then the first element or we use array destructuring in the end here to only have one line of code and then we make that call without extracting the value but here on the left side of the equal sign we put our variables or our constants here between square brackets and the first element is the product second is the index and that's simply a syntax supported by type script and also by modern JavaScript which if you have an array on the right side of the equal sign puts the array elements into constants with these names or variables can be variables as well with these names by simply wrapping them in square brackets here on the left side so if that I'm extracting my data from fine product we have the product we have to index now now here we just have to set this products for the given index equal queue and now I want to take the old product and merge it with my new product data so I'll create a new object here and get all my data from the existing product which is this product we're getting here but then I want to set title to my title I'm getting description to the description and price to the price I'm getting the only problem we have here is that we might not get all that data maybe the user just sent us a new title and no description word user send all fields so we need to make sure that we don't overwrite existing data with gnal or with undefined values now there are different ways of doing this I'll choose away which hopefully is relatively easy to understand I'll add a couple of if checks and there are shorter ways of achieving this but I'll use AF check and if we don't have a title and that will also by the way trigger if the title is empty which is good if we don't have a title then I don't want to set it or put in other words if we have a title I want to use it so add a new updated product will be a copy of the existing product here and then here I'll set my updated product title if we have a title I'll repeat the same for my other values so here we can check description and this will also basically not make it in here or desk is name if it's empty or undefined and we have no other validation in place by the way although for the price we're not checking whether that's negative or anything like that it's something you can add an SKS has some nice features for that but let's not worry about that for the moment so here for a description I'll set the description to desk if desk is set and also last but not least here if price so if that has a valid value then we'll set up that product up price equal to the incoming price so now we have our updated product and now dad's in the end what I'll use down there to overwrite the existing product again that's just one possible approach but hopefully one that's easy to understand so now we have some logic here to replace our existing product back in the products controller we can now call this product service dot update product and now forward our prod ID our prod title our prescription in our price and thereafter simply return null maybe because I don't really need to return anything else there it is there is really data I want to return of course you could return data but I don't see which data we would pass back let's give it a try let's save that and as before let's create a new product first of all because we restarted the server let's get that ID let's send a get request to that ID to verify that it's there and now let's create a new request here a patch request to localhost 3000 slash products slash that ID in there in the body now set this to raw and then Jason and there add curly braces and then let's say we want to set an updated title but know our values if I send this I get back no response body which makes sense but it is a success response to a hundred status code so now let's check this item again here before we had a second test if I sent us again we now have an updated title and all the old data is still there so updating seems to work let's now also update let's say the price - oops should be between double quotes 100 dot $0.99 this request here and now get this back and now we see that as well so this all seems to work we can update products as well so to come to an end now let's also add a delete method so here we import delete from nest is common and we've delete imported in the controller we can add a new method remove product and that method receives the delete decorator and just as patch and get single product we of course need the ID of the product here so let's accept it as a dynamic segment because delete requests don't accept a request body hence we need to get this from the parameters let's extract it as we did before and then in here I want to reach out to the product service to remove a product so in the product service we can add a delete product method that method expects to get the prod ID as a string and then in here well let's delete this product let me actually move that up above the private method doesn't make a difference it's just better style and now here first of all let's get our product with that array destructuring some text by calling this find product for the prod ID and let's also get the index because actually that is the thing I care about don't really care about the product we can use an underscore as a variable name to signal that we don't really care about that or of course as alternative simply retrieve that second value which the index is by accessing index one here like that so if that we get the index of the product we want to clear now we can call this products now there are different ways of removing a product we could all use the filter method to get a new list of products with which we could overwrite the existing one I will use splice which takes the index as a starting point and then splices which means removes one element so now we have two lead product back in remove product we can now call this product service delete product for word the prod ID here and we should be good to go and just as for patching I'll return now but of course you could return a different response so let's save this let's give it another try first of all let's add a product and let's it maybe add more than one so we have a second test we don't have have a first one so let's add a first test that first product and also a third one and I want to have multiple items here so that we can see that we actually delete the right one so now let's get all products that's looking good let's now get this one a first test which is in the middle actually get it here for a single product seems to work just for a quick check let's also update it maybe with the update data we still have in there get all products again this middle one should have changed now and it did and now let's send a delete request so a new request here with the little delete word to localhost 3000 slash products slash this ID and nothing else that should be all if we had send getting back status code 200 and if I now get all products the middle one should be gone and we only have a second and a third tests or the one which we had in the middle indeed is gone by the way if I try to delete this again we now will get the 404 error because we couldn't find that product and that of course makes sense so now we have the basic crud functionality implemented here with in-memory data of course but still and with that we covered a lot of the core basics NESTA's offers now nest Reyes has loads of features of course if you visit their Docs you can learn so much more about it I will actually release another video which maybe is already available when you're watching this where I will dive into how to connect this to a database to MongoDB now to save our data not just in memory but in a real database so that's something I'll have a look at and then we'll see where we take it depending on whoever you like that what you think about this video I hope it was helpful and I hope I see you in future videos bye
Info
Channel: Academind
Views: 231,750
Rating: 4.953651 out of 5
Keywords: nest, nestjs, nest.js, node, node.js, node nest, node nestjs, express vs nestjs, nestjs vs express, nest vs express, node expressjs, expressjs, angular, full course, tutorial, guide, crash course
Id: F_oOtaxb0L8
Channel Id: undefined
Length: 69min 39sec (4179 seconds)
Published: Wed Jun 26 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.