NestJS with MongoDB & Mongoose - FULL BEGINNER TUTORIAL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right welcome back everyone to a brand new nestjs tutorial in this tutorial I'm going to show you all how we can build a nestjs API using mongodb to store our data this is a very highly requested video many people who have seen my previous nestjs tutorials I've asked for mongodb and Mongoose so here it is so what we'll do is we'll set up a couple of API endpoints where we can create some data and read some data and I'll show you how we can use mang to search for our data that's in the mongodb database I'll also show you how we can update data as well as delete data as well I'm also going to be using mongod DB Compass as our GUI to look at our data as well so that way we don't have to worry about using the shell to write query statements to get our data you can use whatever graphical user interface you want for mongodb I'm just going to keep it simple and use mongodb Compass because this is what it came with when I installed mongodb yeah so let's go ahead and get started with setting up our project I'm sure most of you already know what mongodb is so I'm not going to go into what mongodb is or what it isn't so let's set up our Nest project so I'll type in my windows Powershell Nest new mongodb or nestjs DB I'll use npm as my package manager okay and uh this will take a little bit of time for it to scaffold but I will also mention that we only have two packages that we need to install we're going to install the base Mongoose package um and then we're also going to install the nestjs wrapper as well so I'm going to move this to my other screen and we'll wait for this to finish installing shouldn't take too long and now we'll CD into the nestjs mongodb project that was just generated so CD nestjs mongodb and let's go ahead and open up visual studio code and we have our project so that's good before we do anything else I'll just simply install my dependencies so let's type npmi at nestjs SL mongus and then space mongus I don't think we'll need any other dependencies so that will be good we'll keep this project pretty lightweight all right perfect so we have it all set up let's go ahead and run everything to make sure it works so we'll type npm Run start colon Dev and we should have our project up and running great it's working before we write any code let me just quickly delete a couple files so I'm not going to use any of this base stuff that is generated so I'll delete app controller spec. TS that's the test file I'll delete the actual controller file as well and I'll delete the service file you don't need to delete it you can keep it if you want to I'm going to remove this as well and then this okay nothing we need to do for main.ts so now let's actually make the connection to the database that's the very first thing we need to do or that we should do so we'll go inside app. module. TS and we want to import module and this will come from at njs mongus that's the package that we just installed and this is the module itself so what we'll do is in the Imports array we will reference module and we'll call for root and then we're going to go ahead and pass in the URI so that's the URI string that we can use to connect to the mongod DB database so most of you should know uh what this looks like already so I'll show you an example of the documentation so right over here you can see that they they have mongodb uh colon sl/ so that's the mongodb protocol and then the Local Host so they don't include the port because typically the default port for mongodb is 27017 but if you of course have mongod DB running on a different port then you need to make sure you specify that so what I'll do is I'll type mongodb col SL slash one thing that I will mention is that if you actually use Local Host for some reason this is happening to me I don't know if it'll happen to some of you all but if you do just Local Host then I'll just type in some random name for the database when I run the application it actually does not connect to the database um there actually should be more logs happening over here but if I change this to 127.0.0.1 it then works I'm not too sure why that's the case maybe someone down below can comment why if they know but yeah I mean I'm not using mongodb on dock or anything like that it's just it's installed on my computer so the Local Host should work but it doesn't so it's strange okay so this is good we have our URI of course if you needed to specify a port you would just do it after the host name so 27017 we don't need to do that because by default it is 27017 and if you had authentication let's say you were using a username and password you would want to have that uh before the host name so it would look something like this let's say my username is test and then my password is test 123 so you would have test colon test1 123 at the host name and then for Slash and then this is the database name that you want to use okay so username colon password and then at host name so that's the format but since uh my local mongodb server does not have any authentication enabled by default it doesn't so I'm not going to worry about that but of course if you're using mongodb in production you need to make sure you do have authentication okay but also recommend using uh encryption as well like some kind of certificate to make sure that everything is encrypted now the next thing that we need to do is we need to create some schemas in because schemas is what is going to Define our application data so for example what I'm going to do is I'm going to have a user schema cuz I want to represent users in my application let's go ahead and create a schema for that let's create a new folder I'll call this [Music] models actually I'm going to read to schemas cuz schemas and models are actually different in the terminology for so that's why I wanted to uh respect that so let's create a new file I'll call this user. schema. TS and then what we're going to do is we're going to create a class and we'll call this user and then to tell Mongoose that this is going to be a schema we just simply use the at schema decorator and it comes from so now when we actually register our schema it will take this user and it'll actually create a collection in the database to represent these user these users in our application okay um let's see what's going on over here oh whoops uh it's supposed to be imported from at sjs mongus not the base package that's why it was giving us this error okay perfect all right so now we can go ahead and Define some fields for our user class so a user will of course will have a name or username string and we'll use the at prop decorator uh I guess I can't import that here so we'll just have to manually import it it comes from the nestjs mongus package and then we'll invoke that and then let's go ahead and do a couple more Fields so let's do prop let's do display name String as well uh what else let's do prop and for this prop or this field uh we'll do Avatar URL so what I want to do for the username field is I want to mark this as a unique field because uh let's say if you're building a website and you have authentication you want to have users you don't want users to have the same username obviously so we can mark this username field as unique so that way if we try to save a user with a username that already exists in the database then it will throw an error so it won't allow you to save duplicates based on whatever the value of username is okay uh for display name uh I can also do something like this where I can set the required field to true or false if I want it to so if you want display name to be required then you can set this true or if you want it to not be required then you can set it to false I'll do the same thing for username I'll set required to True okay and then for display name since I want this to be uh not required to have it set to false so I'll also add the optional operator next to it so that way our typescript code nodes that display name can be undefined same thing for Avatar URL the user might not have an avatar set so we'll just add the question mark and then we'll set required to false okay so that's pretty much how we can create a schema and then Define its schema Fields so now what we need to do is we need to go ahead and use this schema Factory class so I'll import this up here from nestjs mongus schema Factory so this is a class right over here and then this class schema Factory has a static method called create for class okay so right over here we actually just created a class we actually didn't create a schema however this schema decorator tells mongus that this is going to be the represented uh schema based on this class in order to actually create the actual schema we need to use this create for class method okay so we're going to pass in this user class right over here as an argument and then we're going to go ahead and store this return value because this returns a schema we're going to store inside a variable and we're also going to export that variable so I'll use export cons and I'll call this user schema and there we go so now we can see that the user schema variable the data type is schema okay and of course it's a generic type and it's type annotated with the user class itself as well as the uh model okay so we're done with setting up our user schema now the next thing that we need to do is register our schema with module now what I would recommend is creating a user module for your nestjs app so that way you can keep everything in its respective module so what I'll do is I'll create a new folder called users and then inside users I'll create a new file called user users. module. TS and we're going to just pretty much create a user a users module class like this and we're going to use the module decorator that comes from um should come from from nestjs common I'm going to just copy that import so import module from nestjs common we're going to call this module decorator or this function which is a decorator and I'll use the Imports array right over here and what I want to do is I want to go ahead and reference Mongoose module so I'm going to import this as well from the nestjs mongus package and I'm going to go ahead and call the for feature method okay and I'm going to pass in this array which is an array of model definitions you can see the type of here okay and then what I want to do is I want to pass in the model definitions object so that's what's going to actually uh register our mongu schemas okay so that way it will actually create those collections or recognizer collections in the actual DB database so there's two properties we need first one is name this will be uh the name of your class itself so in this case I have a user class you can name it whatever you want really but I'm just going to name it based off of the name of the class right over here user so I'll import the user class and then I'll reference theame property okay this name property is going to be on every single class itself and then we're going to pass in this schema property which will have the value of the user schema right over here so we need to import that all right so there we go so now what we're going to do is we're going to go ahead and go into our app. module. TS and we'll just register the users module by just simply importing it so users module so I just imported that right from this user users. module. TS file and then let's double check our logs okay there's no errors that's good so what we'll do now we will go ahead and create a users service class which is going to be responsible before interacting with our data layer our mongodb database to do that we need to inject our user model into that service class so inside the users folder in the same file or in the same folder where the user users module file is I'll create a new file and I'll call this users. service.ts okay and what I'm going to do now is I'll create a class and I'll call this users service and then I'll go ahead and use the injectable decorator which is going to come from at JS SL common so let's import that right here then now that we have created our class and then we use the injectable decorator to mark this class as an injectable what we can do now is we can begin injecting the model that we want to use so I'm going to create a Constructor and in the Constructor args we're going to use use this inject model decorator which is import from at nj/ mongus so it's comes from right here and then the argument that goes into this inject model decorator is going to be the name of our model so it's going to match whatever name you specified for uh right over here your model definitions so since my name for our user model is user.name which is just going to be the name of the class I'm going to go ahead and import the user class not the scheme of the class so that'll be this class right over here and then I'm going to go ahead and reference theame property which is a property that is on every single class so it pretty much just resolves to the class name itself and then I'm going to go ahead and use the private keyword and then I'll name the argument user model and then we can type annotate this using the model generic type from so you can see model is an interface that is imported from Mongoose not njs Mongoose just the regular plain Mongoose package and then this is a generic type so we can whoops we can use less than greater than brackets to specify which type so we'll pass an user like that okay so now that we've injected our user model we can actually start using this to interact with the database before we can do that though uh we need to go ahead and take this users's service and add it as a provider so we'll go into the users. module. TS file and then right after Imports I'm going to add this providers key and this is going to be an array as well and and all we'll do is we'll just pass in the users service class as an argument inside the array or it's going to be as an element so users service so that is going to be import right over here and you can see now we have our providers and of course remember how earlier we registered the users module so whatever providers that we have registered here will also be registered inside um this app modu as well and let's just double check our logs make sure everything's okay and there are no errors so that's good so what we'll do next is we'll simply go back to our users service file and we'll create a couple of functions that will perform some kind of business logic so let's go ahead and do this we need basic operations that will um you know that will be responsible for creating and reading data from our mongodb database so what I'll do now is inside our users service class I'll go ahead and Define and Implement a couple of methods that will interact with our database so we'll go ahead and start off with a simple crate user method and what I'll do is for the arguments we're going to pass in an object into this create user method I'm going to name the argument create user dto now dto stands for data transfer object this is a very popular pattern among next njs and the developers encourage everyone to use dto data transfer objects because it's a very common pattern in many different Frameworks and it's actually a very simple thing to do so all really this is just going to be is an object but we're going to type annotate it with a class definition so we're going to create a dtl file so I'm going to go inside the users folder and I'll create a new folder called dtl and I'll go ahead and create a new file and I'll call this user. D.S and what I'll do is I'll create a simple class and I'll call it create user D and I should probably rename this from user. d. TS to create user because you want it to represent the actual object itself okay and the object is relevant to the uh fields that you're going to pass in when you create a user so we're going to go ahead and Define the fields that we expect the client to send over to the server whenever they're trying to create a user so of course we want this to also be very similar to how we have our user schema set up so for our user schema we'll have username display name Avatar URL of course the username is primarily the only thing that is required so display name and Avatar you are not required but we can also include that as well as part of the dto so what I'll do is I'll Define the username field as well as the display name field I'll mark display name as optional but I won't Define an avatar URL field for our create user dtl that's because typically um it really depends on how you implement your application if you create a user and then you have them upload an image then yes it would make sense to have an avatar your or some kind of um uh binary data right over here as a field that's sent to the server but in this case I'll just say you know what we don't expect the user to set a profile picture until after they've created their account so we won't provide we won't ask for an avatar URL for this create user dto another thing that you might be wondering is why are we not just simply reusing our user class that we created earlier keep in mind that this user class is is supposed to rep represent our mongodb collection and there might be fields in here that don't necessarily pertain to a request body for creating a user or performing some kind of put request or patch request so for example you might have some fields that are part of the user definition for our database that is not needed when you're trying to create the user so that is why we're not reusing that user class so I just wanted to mention that so now since we are using dto though and and this is just a very important thing when it comes to building crud applications is you want to make sure you are validating your data because remember this is going to be a class that we're using to represent our data being transferred from the client to the server or from server to server whatever we also want to make sure we're validating our data as we're receiving it so to do that we can actually install two packages so what I'll do is I'll go into my terminal and I'll I'll install class validator and class Transformer and this will allow us to actually use the built-in validation pipe that's part of nestjs to actually validate our data as we are receiving it and like I said you always have to make sure you're perform performing validation on the client side as well as the server side okay so what we'll do is let's restart our server so npn Run start col Dev and we're going to go back to the dto so make sure everything's okay it is so what I can do now in my create user dto for each field I can use uh these decorators that come from class validator so you can see right over here if I just type is capital I and the lowercase s you can see all of these different decorators come from class validator so for username the one that I want is I want is not empty so that will ensure that this username field is not empty I can also make sure that it is a string as well I need to make sure this is imported from class validator I'll just manually import it and for display name I can also um I don't need to do anything for display name because this itself is already optional um so what I can do is I can simply use is string decorator to ensure that this is going to be a string and nothing that is not a string so it won't be a bleen uh it won't be a numeric value okay and of course if the validation fails it'll just throw an error so you don't have to worry about that so now what I'm going to do simply is I'm going to take this create user dto class I'm going to type annotate with create user dto right over here and remember we're in the users service plat class right now so by the time that this create user method is called the validation has already occurred and that's simply because the validation won't happen and then once the validation happens the data will actually be usable on the controller layer and then from there on you can pass it to wherever you want which typically you would pass the data from the control layer down to the service layer so we can assume that the validation at this point is all good so what we'll do is we'll actually create our user document so this is going to be pretty simple I'll first create a variable called user or new user and I'll use a new keyword and I'll reference our user model by using the this keyword so this. user model and then what I can simply do is I can pass in this partial which is going to be an object that will have the fields that you want to use to create the user so I can actually just simply pass in this create user dto because remember our dto is defined in such a way that the fields do overlap so what I can do now is I can simply just call new user so this will give you this document type and you can just simply call save to save the user to the database okay so once we have done that we need to actually go ahead and go into our users folder and we'll create a controller now so users. controller .ts so I'll create the users controller so this will allow us to serve endpoints to our clients to interact with so I'll create a class called users controller and I need to use the controller decorator which is going to be imported from at njs common so let me import that from atness common okay and then we need to of course map a route to our controller so the route for user controller we map to users so before I do anything else let me register our users controller so I'm going to take our user controller and go into users. module. TS and then there's going to be uh inside this object for our module decorator we can specify this control property which is an array as well and then we can go ahead and simply import users controller and now if I show you guys logs you can see that now it says users controller is uh mapped and you can see that this/ users endpoint is now mapped to the user controller so that's good so we know that it is working but we need uh actual methods or route handlers in order for our endpoint to work so we need a post request for create user I'm going to create a method called create user and I'm going to use the post decorator which is also going to be imported from at njsc commmon and now this will map the slash users endpoint via a post request to this create user method so if I were to Simply go into my HTTP client so I'll be using Postman as an example to test everything I can just simply make a post request to SL users and it will call this create user method okay so now what I'll do is I need to go ahead and actually inject our users service class into user controller because remember the controller is going to interact with the service layer and the service layer will be responsible for interacting with the data layer or other external apis okay so what I'll do is I will implement the Constructor and I will inject the user service class using uh the Constructor so I'll simply use the private keyword and I'll name the argument users service and I'll type annotate this with the user service class and this is how you use Constructor injection to inject this user's service provider so what I can do is inside my create user method is I can actually reference the users's service and then I can invoke this create user method but of course we need our arguments we need the request body that is going to be sent to this endpoint okay so in order to retrieve the request body all we need to do is just use this at body decorator and I'm going to import that as well from at njsc common okay and then all we need to do is just give this argument and name so I'll call this uh create user dto and I'll type annotate with create user dto and what I'll do is I'm not going to do anything just yet except for just console logging this dto and then I'm going to make a post request right now inside Postman so I'm on Postman right now you can use whatever HTTP client you want vs code does have an extension that you can download I think it's called a thunder client that you can use so you can use that to test your end points but I just like using Postman so let me change this to a post request and I'll type in the URL which is going to be HTTP colon localhost I think I'm running on Port 3000 so 3000 for SL users and notice how now it's going to say 2011 created but you're probably wondering well I didn't send any data to the backend you can see right now the data is actually um empty it's an empty object but the problem here is that we have these decorators right over here why was the validation not happening why did the validation not work the reason why the validation did not work is because we actually did not uh enable the validation we need to do that before we can actually have it working and it's actually pretty easy to do that there's two ways that you can do this one you can enable validation globally so going back to our main.ts file and inside here we can enable the validation so I just reference app so that's this app variable right over here and then do use global global pipes and then you just need to create an instance of validation pipe and this is going to be imported from doesn't want to Auto Import this is going to be imported from let me just fix that yeah it's going to be imported from at nsjs commmon so now this willable enable validation globally at the entire application Level so all of your endpoints will uh have validation so if I go back to postman if I click Send you're going to see now it's going to actually perform the validation if you prefer not to enable it globally but would rather enable it uh on each endpoint you can also do that as well so you can use this Ed pipes decorator on the method so this create user method over here so this will ensure that validation will URS so use pipes I'm going to import that manually since my imports are messed up and then all you do is just use new validation pipe I'm going to import that up here as well validation pipe just like that okay and now I go back into Postman you can see that validation will occur and only for this specific end point so it's it's not enabled globally but just for this specific endpoint okay so we'll leave it like this for now uh so what I'll do now is I'm just go back and close this and let's go back into Postman and let's send some data so we'll send a Json so I'm going to go to body and then click raw and then select Json and then I'll specify username and I'll say hello and display name should actually uh be not required I think I need to use the is optional decorator to mark this as optional so let me try that now okay there we go and if I look in the request body you can see I have username right over here so what we'll do next is we will just simply pass this dto down to users service so uh I'm going to just do return this do users service do create user pass in that dto it's the same type annotation so we can just pass pass it in like this and now we should be able to create users because we had set everything up already we have the user service and then we have our create user method inside the user service class Implement already so this should create the user and save it to the database so what I'll do is I'll go back to postman let me change the username to Anson and then I'll click Send and you can see now because what I did here let me go back to the controller I'm just simply returning this um create the the return value of what a crate user is so it does end up returning a document and njs will handle um this for us so we don't have to so like for example in Express you have to do something like response. send you don't have to do it in sjs I'm sure many of you already know already you can just simply just return and then turn this value over here so you can see now it's just going to return this document so you can say username Anon the ID okay and it has thisor v property which is just a version key so I'm going to go ahead and try to create uh the same document with the username being Anson again just to show you what happens if if we try to create a duplicate record so if I click Send now you see it says uh status code 500 internal server error of course we need to make sure we handle our errors accordingly but what is really happening underneath the hood is it's giving us this duplicate key error collection okay and again see says nestjs um tutorial out users index username one duplicate key username Inon so that shows you that because we had that unique constraint set on that username field that if we tried to create another document in the user collection with the same username is going to throw an error okay so hopefully that makes sense but I'll go ahe and try to create another user this time setting the display name and I'll just do let's try a different name let's try Jack the developer and I'll set the username to Jack click Send and you can see now that we have username Jack display name Jack developer and if I go into my mongodb compass uh Let me refresh let me zoom in a little bit you can see now if I click on Nest J tutorial and if I click on users you can see I have all of my data right over here I have username Anon uh I have Jack and then display name so pretty cool we are saving data to our mongod dbab so that's wonderful all right so let's go ahead and do a couple other operations with our database so what I'll do is I'll go into uh let's see let's I'll go into users service.ts and I want to be able to get all the users so I'll create a method called get users and we don't need any arguments for this so at least not right now so what we'll do is we will simply just uh I guess what we can do is we can return this. user model and then there should be a method called find and you can see that this will return an array of documents which will be of type user and that's all we to do for that so now let's go ahead and create an endpoint for getting all users so I'll use this get decorator so I'm going to import that up top from at njs common so get we'll call this decorator and then I'll just call this method get users okay so now we have a get request uh for SL users map to this method right over here and then all we need to do is just reference this. users service and then we just call get users and it'll return whatever get users returns which is going to be an array of all the users in the database let's go back into hxman I'm going to create a new tab and I'll just do uh Local Host port 3,000 SL users click Send and see how now we have an array of all of our users all right so that's pretty cool so let's go ahead and create a couple of more operations for our application so we have create user we have get users let's go ahead and try uh get user by ID so back in my user service class I'll create a method called get user by ID and this will expect an ID for the user so the ID will be a string and what we can do to get this user is this so I'll return this. user model and then we're going to go ahead and call uh find by ID so this will search for a single user in the collection so we just pass the ID like that okay and keep in mind the ID in this case is going to be that autogenerated ID that that uh that mongodb will autogenerate for each document so that's the idea that I'm referring to see notice how the documentation over here says it's underscore ID field which is autogenerated okay so let's go ahead and go back to the controller and what we'll do is we will set up another get request but this time I need to uh add an extra path at the end of the SL US Route so that's going to be a route parameter so I'm going to do colon ID so our endpoint will look like/ users SL colon ID so we need the users ID passed in as a route parameter to get the single user from the database so I'm going to call this get user by ID the method that'll be mapped to this route and then now in order to get the actual route parameter we can simply use this at pram decorator I'm going to import that as well from at njsc common so at pram and then you want to provide the route parameter name so it's going to be just ID and I'm going to name it ID and then type man here to be a string okay so now what I can do is I can simply reference user service and then call our new get user by ID method but I'm going to also do do some checks as well because what I want to do is I want to uh call this method and store the value in a variable so let's do that so let's do const find user equals and since this get user by ID method is going to return a promise we need to use async A8 uh inside our controller for get user by ID function because we are calling that asynchronous method so we need to use as Gateway for that so I'm going to use async get user by ID and then AIT this. users service do get user by ID we'll pass in that ID right there so now what I can do is I can simply do if there's no user I can throw a new HTTP and there should be this built-in exception that is part of nestjs yeah HTTP exception and I need to import that I think that's coming from common as well so HTTP uh exception and then I can pass in uh let's see the response to a message I can say user not found and as a second argument for the HTTP exception Constructor I can pass in a status code so I'll pass in 404 which means not found all right so now uh what I can do is I can return find user which I know by this point the user has been found now here's one thing that we should also do before we actually do anything else is we should actually check to see if this ID is a valid um object ID because note that the object ID in mongodb collection for for your documents it might look like a string but it's actually an object ID type so we should actually double check to make sure it's a valid um object ID otherwise it's going to throw an error so let's say for example if I leave it like this right now and what I'll do is I'll go into Postman and I'll copy the ID of this document and I'll go ahead and test out the uh endpoint so I go to SL users and I'll pass in this ID and you can see it works just fine but when I go ahead and pass in a random string instead of getting 404 we actually get a status code of 500 internal server error and the reason why that happens is because it's trying to cast this into an actual object idea which of course is going to fail so what we can do to solve this there's there's many different ways that you can do this but what I would recommend is we check to see if the object ID is valid so this is actually pretty easy and although I would recommend doing this inside a middleware uh because that would make the most sense just to keep things simple um I'm going to do it inside um I'm going to do it right over here right before we actually call get user by ID because this is actually what's going to throw the error when when I call get user by ID and then it's going to call this. user model. find by ID and then we pass in this uh ID and it tries to do the casting it doesn't work throws the error so I'm going to do it right before I call this. user service. get user by ID so it's actually really easy what we can do is we can use this mongus so I'm going to import that up top over here from mongus and I'm going to go and reference uh let's see types. object ID is valid and I'm going to pass in that string that ID right over here okay and this will actually return a Boolean so const is valid equals whatever this value returns so if it is not valid then we know that the ID is not valid so what I'll do is I'll just simply throw an HTTP exception of user not found 44 okay I don't want to tell the user that the ID is invalid I don't want to tell the user anything else but just say that the user is not found so now I'll go back to postman and I'll click Send and now you see it says 44 us not found so this is uh how I would recommend you to handle errors in your application okay don't let it error out to a status code of 500 because 500 is typically for anything that is wrong with the server itself not with issues like this where the user is not found okay so hopefully that makes sense all right so now I'm going to show you all how we can update data in our mongod DB database so first what I'll do is I'll set up an endpoint that will allow us to modify parts of our user document so the best HTTP method for this would be a patch request so we're going to use a patch request to update parts of our user document so it's going to we're going to import this patch decorator up top over here from at njc commmon and I want to be able to update parts of our document not the entire document itself so typically the difference between put and Pat is they are both used to update data but put is really just to update the entire resource where patch is for modifying parts of the resource itself so we're not trying to update the entire user record we just we just want to update parts of it so for example if I look at my mongodb compass graphical user interface let's say for example I want to update this user right over here where it has no display name so I only want to modify just a part of this document by simply adding a display name to it I don't want to update the entire document itself hopefully that makes sense so what I'll do is for the ID we can actually pass in the ID the same way that we passed it in as a get request so we can grab the ID from the route parameter so I'm going to go ahead and call this method update user and then what we want to do is we want to specify in our request body What fields we want to allow the client to be able to modify so we're going to need a request body so what I'll do is inside our DL folder I'll create a new file I'll call this update user. D.S and I'll create a class called update user dtl and now here I will Define Fields where uh we can actually use those fields to update the record in a database so if I look at my user schema I can see that I have three Fields total I have a username I have a display name and I also have an avatar URL so of course we don't want to allow the user to update their username well in some cases you might allow that to happen but let's just keep it simple and prevent the user from updating their database once they've created their account they can no longer update their username but we can allow them to update their display name their Avatar URL they have an email address we can allow them to update that as well we'll keep it simple and just use just display name and Avatar URL because currently I have these values as optional so if we can create the user without these properties that would of course work but then we want to allow the user to later on attach these fields to their record in the database so that's where the update user and Point comes in handy so what I'll do is I will simply uh set display name set this a string and this is still going to be optional because you're not expecting the user or expecting the client to update every single property or you're um you know forcing them to pass in specific Fields you just want to allow them to pass in whatever Fields they want and then we'll update the database accordingly so we still want these to be optional so display name will be optional console Avatar URL will also be optional as well and we want to make sure since we're using we're going to be using um class validator decorators to perform validation we do need to make sure that we use the is optional decorator to Mark these fields as optional otherwise when we register or when we enable validation with our update user endpoint it's going to throw an error because we didn't pass in display name or Avatar URL Okay so so hopefully that makes sense I will also make sure that this is a string as well so I'm going to import that from class validator as well and same thing for Avatar URL we'll do that okay so what I'll do is let's see I'm going to go ahead and copy this used pipes part and I'll just paste this right underneath um the patch decorator call so that way my update user and point or this uh the route that's map to this method will have validation so we're going to use the body decorator and then we're going to name our argument update user DT and we're going to type annotate this with update user dtl class that we just created and that will be imported up top over here you can see import update user dto from the relative path okay so now I'm going to go ahead and I need to define a method in the service layer to actually handle uh the update for the user so we're going to go into users. service.ts and I'll create a method called update user and this is of course going to be a two-step process we need to make sure uh that the ID is valid then we can update the user if the ID is not valid then of course we can't update the user at all so not only do we need to grab the request body we need to grab the route parameter as well which is going to be the ID so what I'll do is I will use the at pram decorator and let me do this so at pram I'm going to specify the name of the route parameter which is just ID without the call in okay and I'm going to name ID string just like this okay so pram and then pass in the string ID like this and then give it the name idid and type annotated to be string comma and then the request body right over here so we're going to do the same thing that we did above for get user ID we're going to uh validate uh the mongod DB ID like I said I would recommend you to write a middleware cuz that it would make sense to write a middleware because you're going to be needing to perform this validation on pretty much any end point that you're working with mongod DB IDs or document IDs so I highly encourage you all to write a middleware maybe if we have some time at the end I'll do that and show you all how that's done so I'm going to just copy this and paste this right over here and what I'll do is I'll say if it's not valid I'll just simply throw a new HTTP exception and I guess what I'll do here is I'll just simply say um invalid ID and I'll set the status code to be 400 then what I'll do next is since we know that this ID will is in fact valid we'll go ahead and go back into the users service file and then inside this update user method we want to take in the ID as well as the dto so the ID will be string and then the update user dto will be just of the update user DL type that's going to be imported up top over here so now what I want to do is is I want to go ahead and reference this. user model and then there's this method called um let's let me show you update one or update many of course we are only dealing with one document so we're going to use the update one method but there's also this find one and update as well as find by ID and update and update one will pretty much just assume that you already know that the values in the database is not going to actually validate that that the document is there but if you use find one or whoops find by ID and update this will search for the record and then update it if it exists okay so I'd recommend using find by ID and update that's a much better appropriate method to use for our situation so what I can do is I can pass in the ID CU I know that this is a valid ID a a document ID or an object ID I should really call it that so the casting from the object ID or from a string to an object ID is going to work just fine it's not going to throw any errors like we saw earlier when we didn't validate it so we're going to pass in the ID and now the second argument is going to be this update query which is really just going to be an object and you're going to specify um what field you want to update so notice how if I type in display or di display name appears okay if I wanted to update username which we don't want them to update the username just yet so we're going to leave that alone I could uh and then what's the other one Avatar URL so you can see that I can update literally whatever I want so that's why I created this dto because this has our Fields defined over here that are all optional and you'll also notice that all of the fields in this object are optional as well because you don't need to necessarily update those fields whatever you provide in the fields is what it's going to use that value to update the database with so for example if I don't provide display name then it will not touch the display name property but it will update other parts of the document so hopefully that makes sense so I'll just simply pass in this update user dto object and I'll just simply return this method call okay so let's go ahead and go back into the controller and what I'll do is this I'll return this dot user service update user so we'll pass in the ID as well as the update user dto object so let's go back into Postman well first let's verify that we have no errors so my logs look all good we have all of our routes mapped see I have this patch request mapped right now let's go back into Postman and uh let's see let's get all the users so what I'll do is I'll update uh I'll update this document so the username of anon I'm going to add a display name as well as an avatar URL so I'm going to create a new request uh we'll use the patch request and the endpoint is going to be HTTP Local Host Port 3000 SL users and then the ID of the user we want to update so now notice how right now if I were just to click Send okay you'll see that the original document or at least it looks like the original document because you know we didn't actually pass in any any request body at all so it still went through the whole update part but of course it's not going to actually work because we didn't pass in anything for it to update so it's going to look like the same document now if I tried to pass in let's say if I try to update this ID to something that's not really in the database you're going to see that it actually still gives back a 200 it's still a valid object ID I guess um but it still return to 200 even though the user was not found so we can actually modify that so if it was not found and we don't do anything but I think this return value actually gives back an undefined value I think so we can adjust that accordingly but let's say if I were to just pass in some bizarre ID and you can see now it actually hits um this part right over here and then since is valid so since this is going to be false so we say if it's not false then this is going to be true then we throw a new http exception and then we say invalid ID and it gives us back a 400 status code so we know that this logic works okay cool so let's actually update our document so let me paste that ID back in going to go back to the request body and then change from text to Json and what I'll do is I'll pass in display name I'll set that to an the dev and then let's just leave it like this and let's see what happens so you can see that gives us back a 200 okay so it gives us back I guess the original document but if I try to query all the users again you can see now I get theplay name so this actually this query doesn't actually return the updated document from what I remember uh it actually Returns the old document but there is actually I think a third parameter that you can pass in yep that's right and uh what you can do so this is going to be an object and this is this query options let me show you uh query options type and if you actually specify the new property which is a boole you said it's true it will actually return the new document that was updated or the new version of the update document so you see how when I updated this with display name it returned the original document when I tried to use the users query or the users endpoint with a get request it actually gives me the new document so we can confirm that the data is being updated and just to show you all again if I go into my uh mongod DB Compass if I refresh you can see the display name is there so we know it's updating 100% correctly but let's go back into Postman and let's just say uh let's update this let's update display name to an the developer click Send and now you'll see that it gives me the updated record not the original one if I wanted to set the uh let's see Avatar URL I'm just going to pass in some random string you don't have any validation checks you'll see the Avatar Ural is there so I just wanted to point that out in case if some of you might have a scenario where you need the actual updated record after you perform an update so hopefully that makes sense okay let's just kind of tweak this a little bit so what I'll do is I'll store the value so I do need to await this method C because it does returnal promise so I'll add the as keyword in front of update user and then I'll do A8 and then I just want to console log this just to verify what the actual return value is if there's uh if the user ID is valid but the user is not found so I know that if the user is found though it will return the actual updated user what if I modify the ID to something that is not in the database so if I look at the logs you'll see it says null okay so that's fine so what I can do is this I can simply say uh let's do this if there's no updated user so if it's not null then we'll throw a new HTTP exception user not found and then pass in a 404 otherwise we will just return updated user okay so this will do some error checking for us instead of just sending a 200 even though the user ID is invalid so if I click Send again it gives me back a 404 and if I pass in the valid ID then it'll give me back at 200 so that's great and of course if you don't like to use the route parameter to pass the ID you don't necessarily need to you can definitely pass that in uh as a request body Some people prefer to use a request body to uh specify which user or which resource they're trying to update Some people prefer to use route parameters ultimately it doesn't really matter people have their own differences so it's really up to you so hopefully that all makes a sense so now you know how to perform updates to your mongod DB database Okay cool so let's go ahead and take a look at deleting records from database this one uh might seem a little bit tricky but it actually isn't because there are methods that support deleting records from database so I'll go back into the users's controller and I'll use the delete decorator to specify that our endpoint is going to be a delete request so I'm going to import that decorator from nestjs common and then I'm going to use route parameters as well for this to specify the ID of the record we're trying to delete okay so I'm I also don't necessarily need um a request body because we really are just trying to delete a record we're not trying to um you know we're not really trying to expect the request body so we don't need to do any validation for that so we're not going to create any detail for that okay so what I'll do is I will create a method called delete user and then what I'll do is right over here in the arguments I'll use the app pram decorator grab the ID the same way that we did it for update user and get user by ID so ID is going to be a string and then we will perform validation of the ID so I'm just going to pretty much just copy and paste these lines cuz it is pretty redundant at this point and we don't have any middleware setup so what I'll do is after we do the validation check and if it gets to this part over here we know that the ID is valid so let's go back to users. service.ts Let's create a delete user method it's going to take in one argument which is going to be the ID of the user we're going to delete so for the meth that we're going to invoke on the user model it's going to be very similar to to this find by ID and update but instead there's actually a method called find by ID and delete and we're going to use that so we'll just pass in the ID so it'll search for the user by its ID if it's if it's there it's going to go and delete it if it's not there then it'll return null okay so let's go ahead and move on let's go back to the users controller file and we're going to do the very same thing that we did over here on line 52 and line 53 so we'll declare a variable called cons deleted user and we need to use async A8 so let me add the Asing keyword in front of this delete user method so I'll await this users service. delete user call and we're going to just pass in the ID just like that okay so what I'll do is I'll just console log this for now just to verify that it is going to be and all value if we pass in some kind of uh ID that does not exist in the database so I'm going to go back into let's see let me remove this patch request I'm going to go back and copy this ID so I'm going to delete Anson from the database so let me create a new request so this will be delete request and just going to go to SL users and then the ID now if I click Send so this should actually delete it from the database and you'll see that the record is being logged and this is coming from line 62 where we are logging delete a user if I refresh you can see that uh that record is no longer in the database now if I try to let's say if I try to use the same ID again okay the the ID is valid but it doesn't exist in the database and you can see over here that is returning null so it is in fact returning null so let's just go ahead and finish this last part where we check to see if there's no deleted user or if it's not null okay then we'll throw new HTTP exception user not found return a 44 status code you don't necessarily really need to return anything so you can just do return and this will just give back a 200 status code so that IND the kits that everything is good so that means the user has been deleted okay and we return a status code of 200 so that's deleting users so so far you all know how to create the user get users get user get us get the user by ID update the user by ID delete the user by ID so hopefully all of this was pretty straightforward I know this was a pretty lengthy video but I wanted to fit in as much as I can and what I would encourage you encourage you all next to do is to take this source code that we just went through uh it's going to be on GitHub use it as a resource and try to build on top of it because now you have an actual full working crud API that can perform a bunch of different types of operations on a record now one more thing that I do want to show you all how to do with and with mongodb and sjs is I want to show you how you can have a model reference another model or a schema reference another schema so what I mean by that is sometimes you might come into a situation where you will need relational data so for example you might have a user record that needs a user settings record Being referenced and the user settings record will have Fields such as receive notifications receive emails uh receive text receive SMS same thing as receive text things like that as an example of course you don't want to put all those fields inside one single um document you want them in their own uh document in its own collection and this is known as encapsulation because it makes sense to encapsulate um you know a group of fields that are relevant to its model okay so for example we will create a user settings schema and then we'll do the same thing that we did with our user schema as well and I'm going to have a reference inside my user class over here it'll reference a user settings model so the first thing that we need to do is we need to go ahead and create a new file and I'll call this user settings. schema. TS and we're just going to create another class so export class user settings okay and then we will use the schema decorator to mark this as a schema and then what I'll do is I'll I'll keep this very simple so I'll use the prop decorator and I'll have a receive notifications uh field that will be optional and this will be set to a Boolean value uh let me also make sure I set required set to false as well I'll do the same thing uh for let's see receive notifications receive emails is the other field that I want I'll do one more uh let's do receive SMS for text messages okay and these are all going to be uh optional of course cuz typically with settings you want them to be optional okay some settings of course are required but in general uh you want the user to be able to enable and disable their settings okay but by default the settings field will be undefined so what I'll do is I'll create that user settings schema variable and the same thing that we have for our user schema on the left hand side I'm going to do the same for the rightand side so I need to reference schema Factory and call create for class and then pass in the user settings class so remember this create for class actually creates the schema itself and it assigns it to this user settings schema variable which we export so now we need to register our schema so we're going to go into the users . module. TS file so where we have module. for feature this is where this is where we registered one uh model definition for user we're going to register another one because this is an array and I'll just pass in the name which will be user settings so I'm going to import that class that we just created and I'm going to reference that name property that is on that class and then we're going to set the schema property to be the user settings schema value so that's just the schema that was was just created and exported this thing right over here okay so the same thing that we did for user we're doing for user settings all right so we're not quite done yet we do need to go back to the user class itself and what we need to do is we need to define a reference to that user settings uh document itself or that user settings uh type if you want to call it okay and it's it's actually very very simple so all we do is we simply just use this at prop decorator and then we pass in this prop options object and we set the type and the type is going to be mongus so we need to import mongus from mongus right up top over here so mongus do schema. types doob ID D and then you need to set this ref property and you're going to reference user settings so the name of the class itself okay and then uh you're just going to define the the field name so I'll just call it settings this is also going to be uh optional as well or possibly UND Define and then the type will just be the class user settings and there you have it so the way that this is going to work is the actual um data type that it's going to appear in the mongod DB database it's going to appear as a string and when you actually retrieve the user record if the settings field is set it's not going to actually resolve into an object right away it's going to be an ID uh well in fact the the object ID of the relevant user settings document okay so whatever that user settings document ID is that is related to the user that is going to be the ID set to this settings field so what you need to do in order to get that ID turned into an actual object or resolved into an actual object for the user settings is you need to actually populate it and I'll show you how to do that as well okay but if if that didn't really make any sense don't worry um you'll you'll see what I mean in just a second so this is all we need to do to provide a reference to user settings okay so now let's go ahead and close all of this out we just uh set up our schema we registered it and then we also referenced inside our user class we referenced the user settings just like this we provided a reference for user settings for our mongod DB database so I'm going to keep everything simple so I'm going to go into users controller and what I'll do is since I already have a uh create user method and I also have this create user DT I'm just going to make it so that when we we create a user I'm also going to allow them to um set the settings at the same time as well just to keep everything simple ideally though what you really should do is you should create separate endpoints uh for dealing with user settings so it's going to be its own entire domain and then those separate endpoints will have stuff like create settings update settings Etc things like that and then that's how you should actually create the user settings and then link it with the user it belongs to but I want to keep things simple so I'm just going to do it this way so what I'll do is I'm going to go inside create user dto and I'm going to go ahead and set this settings property right here and I'm going to type annotate this with a class that I have not created yet but that we will create right now and I'll just create inside here and I'll just call it uh create user settings D so let me just take this and type annotate settings with that okay okay so what I want to do is for create user settings dto I want to specify all of the possible settings that we can allow um the user to uh set when they are creating the user so we only have three Fields so we just allow all three of these fields to um be set when they are creating the user so receive notifications which is optional so that'll be bleen and I'll do the same thing for receive emails and receive SMS so receive emails and then receive SMS and these are all going to be booin same thing as our user settings model for our schema okay so now we need to uh Mark each one of these fields as optional because they're not required of course so is optional and then we'll use this is boene validator so that's going to be imported from class validator to ensure that if they do set this field in the settings object then it must be a bleen so I'll do the same thing for the rest so we did the same for receive emails and receive SMS so now down over here for this settings property okay we need to mark this as is optional so that way it allows the user or the client that is making a post request to our endpoint uh the ability to either set this settings object or not okay and of course even if they do specify this settings um property and let's say if they just pass in an empty object which is going to be totally fine because all three of these fields are optional okay now here's the thing though since we are validating or trying to validate a nested object because technically these two are uh primitive values scalar values if you might say but this rate over here is not a scalar value it's an object so we need to make sure that we are doing doing nested validation and to do that we can use this validate nested decorator and that is going to be imported right right up top over here so this will ensure that it will actually uh validate all of these fields as well okay so now that we have all this working and remember we don't really need to change anything with our controller because we only we modified the dto uh type definition to include setting okay so now all we have to do is just go back to postman and test this out so I'm going back to postman right now and I'm making a post request to uh the/ users endpoint right over here going to send a request body so let's do username let's do ansen one okay it's not going to pass any settings at all it just works just fine okay now if I do pass settings right now though it's actually going to throw an error because we do need to modify our um our service layer and I'll explain why so right now if you look at the logs you're going to see it says user validation failed cast object ID fail for Value so what's going on is it's throwing an error because first of all uh let me go into the user service okay so right over here so we're actually trying to set the settings at the same time while we're trying to create the user and you can't do it that way because you actually need that user settings record in the database already in order to link it with this user so what's really happening is we're passing this whole entire user DT create user DT object and so it's passing in the username display name and then settings and then it's trying to set the settings like this okay but we need the user settings record in the database first before before we can even link it with this user so what we need to do is we need to modify this whole create user logic to check to see first of all we need to check to see if the user is trying to create the user with settings if they are then we need to First create the user settings and then we can create the user and then link the user settings ID with the user if they're not trying to create the user with user settings then we can just perform these two lines and we're done so here's what I'll do I'm going to go ahead and uh take this create user dto object and I'm going to destructure it at the argument level so by doing it like this so pair of curly braces and then I'm simply just going to zoom in a little bit so you all can see better so I'm going to go ahead and take the settings object and then I'm going to use the spreader operator to pack all of the remaining Fields so everything except for settings cuz we destructured settings first so think of it like we're taking settings out of the box and then everything that's left in the box is going to be stored inside this variable that I'm just going to name create user dto okay so username and display name will be left inside that box analogy that I referenced but it'll be stored inside this create user DT object so this is a good way to pretty much kind of like take properties out of an object if that makes sense okay so what I'll do is I'll check to see if settings is defined and you can see settings has all these possible properties so if settings is defined what we will do first is we'll create a record of settings in the database so to do that we need the user settings uh model so the same way that I injected it injected the user model inside the Constructor or inside the Constructor arguments I'll do I'll do the same for uh user settings so at inject model and then I'm going to import that user settings class right up top over here here and then reference that name property okay and remember this whatever you pass into this inject model decorator so in this case I'm passing in user settings. name it needs to match what you set for name over here that's how it recognizes which correct model to inject okay so we're going to uh whoops private and I'm going to call this argument user settings model and then type annotate to be model user settings like this so cool we have our user settings model injected so let's just make sure there's no errors okay good let's go back into our code so inside the the if block for if settings what we want to do is we want to create the settings so const new settings equals new this. user settings model so this is how I can create my new user settings record and save it to the database well not save it we're going to do that part next so I'm going to pass in the settings object into user settings model okay and what I'll do now is I need to save new settings or the new user settings by simply calling the dove method like this but because this returns a promise and we still need to use the return value of the saved record to database to link it with the new user that we're creating we need to use async A8 to get that resolved value so I'm going to add the ASN keyword in front of create user and then what I'll do is I'll declare a variable called saved uh new settings equals await new settings. saave okay so now the saved user settings is now in this variable right over here and I can refence reference properties on this but we specifically need to get the ID okay so now that we've created our user settings and then we um saved it to the database we can actually create the user itself so we're just going to do the same thing down over here so cons new user equals new this. user model so for the argument for user model we're going to pass a new object we're going to unpack all of the fields from create user dto into this new object so like this create user DT and then what I need to do is I need to set the settings so this is where you can see how it like in typescript it's expecting this to be an object but it's actually supposed to be the the object ID of our save set save new settings so that's actually the reason why we had an error in the first place so this needs to be an object ID so saved new settings. ID like this okay so this will pretty much create the new new user and also set the settings property to the ID of our newly created user settings and then we can return new user. saave okay so hopefully that makes sense and if you don't really understand what's going on with this the structuring part or I'm sorry not the structuring this spreader operator part you need to look up how spreader operators work because it's a very important thing to kind of like understand really well in in JavaScript types script but just once again ideally we're just pretty much taking the fields username and display name and then it's pretty much taking them and then setting it as if we're doing it like this username and then display name like that okay but it's taking those fields and values from the create user dto so I just wanted to mention that just in case some of you might be confused all right so now we can test everything out so if we've done everything correctly which it seems like we did we should not any errors so I'm going to leave settings as an empty object right now click Send and now you'll see that because we specified settings but we marked it as an empty object it's still going to create that user settings uh document in the user settings collection in our database uh but it's just going to be an empty document it's going to have no properties set okay we still but notice how we still have this underscore ID or not sorry not here this one right over here this settings property and this ID right over here this ID is the ID of the settings record and if you don't believe me I'll show you right now let's go into our mongodb uh GUI and I'm going to go to user settings and right now you can see I just refreshed you'll see that uh the corresponding ID let's see 656 D5 bc1 is what it ends with so uh there's only three right over here bc1 okay this is the correct one and notice how none of those fields are set and that's okay because remember we marked it as optional so we are ensuring that our uh validation is working right over here okay is optional but since we did set settings it did create a settings record but then it didn't care because it didn't care that it was empty because all of these fields are optional now notice how if I were to remove is optional from receive SMS and if I try to create this now let me change this to an 3 uh it should actually throw an error I'm not sure why it didn't um that's weird I think I forgot to do one more thing so uh I need to use this type decorator from class Transformer and then I need to pass in this function and this is going to just be the create user settings dto just like that I think that's the reason why let me try again because that should have thrown an error okay there we go now now it works okay yeah sorry about that I completely missed that so we need at validate validate nested and then we also need this at type and then pass in this uh callback function and then just the return type is just going to be whatever the detto that you're trying to validate so it's just going to be the same thing as this Rec here create user settings dtl okay so hopefully that makes sense and now we can see that it is throwing an error so receive SMS must be booing value okay so that's good let me just go back and mark this as optional Okay cool so let me go back and create a new user with settings so let's set receive notifications to True click Send and so now I have my settings ID that relates to this user record right over here and I can go into the database and I can look for that and it's going to be this one right over here and in F7 yep F7 okay so now here's the question though I want to be able to retrieve the user but also have this settings not be an ID but be the actual object itself because if I go and try to query this user so let me get the user ID so I'm going to copy this right over here and let's just say for example whoops let me go over to here say if I try to grab the user by its ID see how right now I get everything um but the settings property is mapping to the ID and I don't want that I want the actual string how can we do that well what we can do is we can go back to our users. service.ts file and then let's let's go into get user by ID okay so this is where we are searching for the user by ID at the end we can go ahead and call this populate method and then all you need to do is just specify the path of the property of the field that you want to populate okay so in our case we only have one field called settings that we want to populate so we just pass in settings like that but you might have complicated uh you might have like a complicated reference structure where you might have a bunch of nested objects so let's say for example if settings had a reference to another document um or another model uh you would just do settings Dot and then the name of that model like this or the name of that property okay name of that property that references that model so that's how you can populate these nested objects so if I go back to postman if I get this user see how now it gets me the actual entire document not just the ID I have an actual object now and I can do the same thing for uh getting all the users so if I try to get all the users right now you can see that users that do have settings set they're just strings and that's okay we can simply go back over here call populate populate the settings field so at the end of this. user model. find inside the get users method I'm calling popul the same thing that I did down below for get user ID so now if I query all the users all of these settings should be populated okay so hopefully that makes sense so now one thing that I do want to show you all before I end this video that I think is very important are on to many relationships in mongodb so far we did a onetoone relationship with user and user settings and it it should make sense because the user can only be associated with user settings settings or at most one user settings document and vice versa user settings can only be associated with at most one user record or user document so that is a onetoone relationship when you have a relationship that is one to many or many to one I'll give you an example so a user might have many posts but a post will most only have one user okay so if you're thinking of like a Blog platform you're the user you're going to create a blog post now you have have let's say one block post and then you create another block post now you have two so that's a one to many relationship example okay a user can have many posts but a post can only belong to one user a post can there are cases where a post can belong to multiple users okay that would be an example of a many to many relationship so like let's say if you allow a post to be collaborated between multiple users and that would be an example of a many to many but we're going to keep it simple we're going to keep it as a one to many many to one relationship so what I'll do is I'll set up a new schema for um our current application I'm going to try to keep it as simple as possible so what I'll do is I will go ahead and set up a post schema which will represent uh posts in an application like blog posts or social media posts as an example so post. schema. s and then what I'll do is I'll simply go ahead whoops I didn't mean to open that I simply go ahead and create a new class called post and then use the schema decorator to annotate this as a schema I am going to require two fields to be uh valid or present so we need Title and description and we'll just leave like that for now or I should actually change it to contents not description okay and then I'll use the prop decorator for this and we'll set we'll pass in some options we'll set required to true and same thing for contents as well prop required true so that way these are not null and then I'll go ahead and create this post schema variable and then we need to reference schema Factory so I'm going to import that up here and then call create for class so this will actually create the actual post schema so we just pass in that post class and then it'll assign the post schema to this variable right over here okay so now that we've done that uh what we want to do is we want to reference this post right over here inside user okay now like I said typically you can uh create like a separate model or separate schema for author because typically it's an author that has a post not a user but I'm going to keep this very simple so I just want to I'm just going to do it inside this user schema because we we already have it already Okay so since this is going to be a one to many relationship this indicates that the user will have uh a bunch of posts which typically the data type for that is going to be an array so first thing I'll do is I'll set this posts field to be an array of post so I'm going to import that post class from this file right over here post. schema. TS okay I just imported that and I'm going to set this to be post array like that so that way typescript knows that this is going to be an array then we need to to tell mongod DB that this is also going to be a one too many relationship so we use this app Prop decorator and then it's going to be very similar to how we did it for user settings okay so I'll show you so we're going to set type to be okay and it's going to be square brackets like this and then passing this object and then set type so it be Mongoose do schema. types. object ID and then ref and then the name of the post class like this so it's very similar to how we did up top over here but this is for multiple posts so this is going to be an array of posts okay so really what's what's actually it's going to look like in the document is this is going to be an array of object IDs not array of post objects but an array of object IDs okay and remember how earlier when we were uh trying to retrieve the record for user user settings it was giving us back the uh settings ID when we tried to retrieve the user record but what we did was we populated that user settings uh field that settings field and then it gave us back the entire um object itself so this is going to be an array of all of the IDS but if we use the populate method it will give us it will resolve all the IDS into the actual corresponding post objects okay so I just wanted to mention that so now that we have this relation ship set up now we need to actually just uh first we need to register our post schema okay um so what I want to do is I want to create a completely new controller service and uh module just because you know we really shouldn't be keeping everything in one single module and post is in its own domain so I'm going to create an entirely new module for post okay so let's create a new folder we'll call this posts I'll create a new file called posts. module. TS and then it's going to be very similar to our users module uh so what I'll do let me close all this I'm just going to use the module decorator export class posts module and then we need to import module from at nsjs common and then we're going to need to specify the Imports controllers and providers okay so let's start off with the post schema because we need to register that inside the module that we're using it which is going to be post module so we're going to need to import mongus module from at njs mongus and then call for feature and then pass in this array of model definitions and it's exactly the same way that we did it in users module like this so nothing new so I'm just set the name going to import that post class and set the name to post. name and then for the schema property I'm going to set it to post post schema like this okay next let's go ahead and create the controller so inside the post folder uh posts. controller. TS so I'm going to import controller from nsjs common then export class posts controller and then I'll use the controller decorator to indicate that this class is a controller and I'll set the route to be posts okay so this will map the SL posts endpoint to this entire controller right over here uh let me go ahead and just simply create the service just so that we have everything so posts. service.ts and then we need the injectable decorator which is also imported from at njsc common and then we just create the class Post Service okay okay before I Implement any methods let me just import the controller and the service and pass it into the corresponding uh arrays so for controllers I'm going to import post controller so that's going to be imported up over here and we're going to pass that as a value inside this array same thing for the providers so Post Service like this right over here import post service and then pass as a provider inside this provider's array okay so that should be pretty much it for this post module so we'll start with our post service class or injectable and then we'll go into the controller and set up the route so first I'm going to inject the post model so I'm going to use this Constructor and inside the args I'm going to use inject model and it's going to be imported from at njs mongus I'm going to import the post class and then reference a name and then I'm going to go ahead and do private post model and then I'm going to type annotate it with the generic model type that comes from the mongus the base mongus package so import model from and this is a generic type so I can set this to post like this so This Is How We inject our model and we should not have any errors in our logs so that's good so we should be able to begin using um these uh this model to interact with the database okay or the uh the post collection in database so I can do create post I can do find posts find posts things like that find post by ID stuff like that okay I'll just do create post for now so create post is going to need need the object um that is going to be used to uh create the actual post itself so we need of course an object that has title and contents okay so what I'll do now is I'll go ahead and go back to the posts controller and I'll set up a post endpoint so let's do controller let's import the post decorator okay and I will simply do create post so now we need a dto to represent the request body the data transfer object that's coming from the client to the server so I'll create a new folder inside the posts folder called dto similar to how we did it for users and I'll create a new file called create post. D.S and this will be a class called create post dto and then it'll just simply have the title and contents field and then we'll use decorators from class validator so is string is not empty and I can also set a max length as well so max length set the max length to be 100 characters okay and then I'll do the same thing for Contents I want this to also be a string I'm going to use the is string decorator from class validator is is not empty and then max length let's just say 500 okay that's pretty much it for that so let's go back into our controller and then we'll grab the request Body by using the body decorator so I'm going to import that from at njsc common so post or create post dto type annotate that with our dto class that we just created and then we need to inject the service the post service into post controller in order to actually call this create post method and pass in create post etto so let's inject the service which is very easy private uh Post Service Post Service like that okay nothing new Here and Now what I'll do uh since I don't have the logic implemented yet I'm just simply going to go ahead and call this. poost service. create poost and I do need to update this method this method signature so I'm going to just take create post detto and pass it in like that and the error should go away once I update this so back in our post service class for create post I'm going to update the signature to take in this create post DT type so I'm going to type annotate with the create post DT class so now let's actually create the model and save it to the database so we're going to declare a variable called cons new post equals new this. poost model and then passing create post detail and then we just do return new post. saave and that is pretty much it so let's go ahead and just test out this uh endpoint so we're going to make a post request to slost posts okay make sure our API no errors good um let's see oh I forgot one more thing I apologize I think I may have forgot to register the post module into the app. module. TS our root module so let me do that that no wonder why it the logs didn't say uh the post endpoint was uh mapped so let me go into the Imports right underneath users module I'm going to do Post posts module save and let's just make sure there are no errors good and now we see post controller is right over here it's resolved so the route should exist so let's go into Postman and let's go and create a new tab we'll do a post request to uh Local Host Port 3000 slash posts okay if I click Send uh let's see what's going on okay yeah so it does throw an error uh of course that's because we passed in an empty a object but the database is expecting title and contents to be there I did also forget one more thing I forgot to also enable validation for create post okay because we don't have validation enabled globally we have to enable it manually for each endpoint so we're just going to do that for create post I'll do at use pipes so this is the use pipes decorator which is going to be imported from at njsc common and then we just pass in this instance of new validation whoops validation pipe it should look similar to uh I can just import validation pipe yep validation pipe right over here so what I did was I imported validation pipe and then I just created this instance and passed it into use pipes like that so new validation pipe okay so now it should actually just throw a 400 instead and give us these validation errors which is fine cuz that's what's supposed to happen let's actually pass in a request body so let's pass in uh title uh let's say hello world and then contents hello world click Send and now we get our very first post created in the database but here's the thing though we are creating posts but we're not linking it to the user cuz this post exists in the database let me show you very very quickly inside my database uh guy I can see this post collection over here and it exists okay but here's the problem though we don't know who this post belongs to okay and currently it doesn't belong to anyone because it's not linked to any user so we need to make sure we are also linking the new post that was created to a particular user so now how do we do that okay it's actually pretty simple but here's the thing though because we don't have any authentication we don't have any State uh with our HTTP requests there's no way we know know um you know who the current user is in the session um so we do need to just for demonstration purposes we do need to take in a user ID so that way we know who the user is that this post belongs to if you have authentication in your application already then you can very easily figure out who the user is by just simply getting that data from the session data okay but I am going to modify my create poost dto and I'm going to take in this uh I'm going to just call this user ID which is going to be a string so is string and is not empty okay so we'll pass in the user ID that we want to link this post to so this will require us to modify our logic just a little bit so we do need to modify our create post logic uh just a little bit because we are creating the model but we're not linking that model to uh uh the existing user okay since now that we have the user ID that's going to be sent in the request body when we try to create a post we can easily take that ID and search for that user the user ID search for it in the database and if it exists then we can link the post but in order to of course search for the user we do need the user uh model so I can just simply inject that inside post service so uh I'm going to go ahead and just copy this whole thing right over here just to save some time and paste it right down over here but I'm going to just change everything up so I'm going to replace this with user I'm going to import the user class okay from the schemas folder so I can reference user.name and I'm going to change this from post model to user model and I'm going to change this generic type from model post to model user so now we have post model and then we have user model okay we are going to get an error of course because we need to register the user module mod or I'm sorry the user model inside uh this post module okay because currently we are in a different scope of our uh application right now we're in the posts module okay and we only have the post uh schema the post model registered within this posts module okay we did register user and user settings but that's inside the users module okay now what you could do is you could actually take this whole thing copy it and then add it as an exports you don't have to do it and I wouldn't really recommend especially if you have a lot of um schemas registered inside this module because you might be importing a bunch of registered schemas that you don't necesserily need so I think it's just better to just register whatever you need inside uh your module so I'm going to go inside posts. module. TS and I'll register the user model so I'll just import user inside post. module. TS right up top over here reference. name and for the schema we're going to import user schema from that same pth here okay Source schemas user. schema so now that error should go away and it does so now we can actually interact with our uh users collection inside the scope of our posts module okay so what we'll do inside our create post method is we'll search for the user first okay so um I'll grab the user ID from create postl uh well actually let me do this let me destructure this like this let get the user ID and then the rest of the properties will be stored in create post DL okay and then what I'll do is I'll search for the user so con uh find user equals and then we need to use async A8 so I'll add the E keyword in front of create post method so away this the user model find by ID and then pass in the user ID like this okay if there's no user we can actually just return um or throw an error here um I'll just do this if there's no user I'll return null and then I'll take care of the error handling inside the controller layer I could actually just throw the error here and then the uh the exception layer will take care of handling the error okay so you know what why don't want just do this throw new HTTP exception uh [Music] user oops user not found four okay so then what I'll do is is if the user is found we will go ahead and create the post okay we'll save the post um instead of returning this just yet uh here's what I'll do right before we return the new post oh wellit actually I'm sorry I need to save this so const saved post equals A8 and then what I can do is since I already have this fine user document already I can just do fine user and then I can go ahead and just call update one and then what I want to do is uh let's see I don't need the ID because we already know which user we're going to update already what I do need is I need this push operator because we want to basically push to the posts array for find user okay and the right way to do this is to use these uh these queries push to actually allow it to modify the array for you instead of just doing you know find user. posts. push okay so for this you need to Target the specific property you want to update so in this case it's just posts like this and then uh you just want to pass in the post ID okay because remember this is going to be an array of IDs object IDs for the Post not an array of post objects okay so posts and then we'll do new post or I'm sorry save post uh and then do ID like this so there's that and this should be asynchronous as well I believe yes so uh const updated user equals await and I think that should be it yeah I don't think we need to save anything I don't think we need to save anything because this will pretty much update and save it as well okay so we will just return save post and let's just see what happens with everything so let's go into our endpoint or Postman let's go and send the user ID now so let me go into let's see mongod DB let's go into our users and Let's do let's do jack okay so user ID okay so we do uh not get any errors so that's good let's go into our database okay so you see how now when I re after I refreshed you can see that this document for the username of Jack let me see if I can zoom in a little bit you can see now it has this post array and it has this object ID in there okay and then if I refresh post is just going to look like a regular document okay you know nothing about the author from here but if you go over here you can get all of the you can get the array of post IDs okay so now if I go back into uh let's see let's go [Music] into here let's get all users you can see now uh when I query all users I get this posts um array which is an array of IDs and similar similar to how we populate the settings field we can populate the post field as well so it's very simple I'll show you how to do that so what I'll do is I'll go to let's see I'll go to users users controller or I'm sorry users. service.ts so all I'll do is for populate I think populate only takes in uh let's see I think popul only takes in it's hard to read this sometimes I think it does take it might takeen an array I think let me just double check yeah it takes an array as well a string or array so I can pass in settings and I can also pass in posts as well like this so let's go ahead and get all the users again and this time you'll see that I have all the posts populated um let's try this let's let's do one with settings and post so I'll take this user right over here Anon 3 I'll create a post right now for Anon 3 let's grab all the users and now you can see for Anon 3 all of the settings are pop the settings object is populated and the posts array is populated with all of the post records okay so hopefully that makes sense and hopefully that shows you all how you can deal with one to many and many to one relationships okay so I hope that makes sense and I hope you all learned a lot from this tutorial I really enjoyed making this video it's probably one of the most requested tutorials on this channel for nestjs with mongod DB so um I was really happy uh that I got a chance to make this video for you all so I hope you all enjoyed this video the code will be in description so you all can use this as a reference um if you like the video of course please leave a like down below leave a comment if you have any questions I'll be more than happy to answer them and uh feel free to subscribe to my channel if you wish to see more similar videos like this I am posting more Nest Chase tutorials now and I will also be posting some other videos uh with Express as well so that's going to be pretty much it for this whole tutorial I appreciate all of you for watching and I'll see you in my next video peace out
Info
Channel: Anson the Developer
Views: 9,918
Rating: undefined out of 5
Keywords: nest.js, nestjs, mongodb, mongoose, nest.js mongodb
Id: dJz94r5C3QA
Channel Id: undefined
Length: 118min 52sec (7132 seconds)
Published: Fri Dec 08 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.