#6 Adding Relations | Build a Complete App with GraphQL, Node.js, MongoDB and React.js

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi and welcome back to this series we're making good progress we added MongoDB in the last video we added our event model and we're now able to actually hit the database when we get an incoming mutation or when we want to retrieve our events now let's also make sure we add users to a picture so that we get closer to our final API implementation so we need more models now we want to have users and users should be able to book events so in the models folder here I will add a user JS file because obviously we want to define how a user should look like in our application and there just as in the last video I will start by importing mongoose then i will access this schema object with this schema constructor provided by mongoose because i want to create a new user schema here now as you learn in the last video we do call new on that schema constructor and pass a javascript object to it and there we now define how this object should look like so how i user object should look like in our app and in our database now my users will be very simple you can't could add tons of data there the address mobile phone number i will give my users an email and dad should then be off type string and it should be required and my users will have a password of course and dad will also be of type string and this will also be required now that is not all however of course my users should be connected to the events and actually there are two ways how users are connected to events either they created an event so in our app we want to enable users to create events something like your birthday party or maybe something else if it costs money the other way we connect users and events is of course by bookings that a user booked an event now let's focus on the bookings later for now let's add another field here which i'll name created events so thatis that connection where we say which user created which event now created events will actually be an array so here we will store an array of data because obviously a user can create more than one event and in a mangu world we depict this relation by first of all adding square brackets and then in there we define how a single element in that array of data would look like so we have multiple created events potentially at least each single data field we store in that array will however be an object where the type is now not string not number it will instead be schema types object ID because we have this special object ID data type that Mongoose or that MongoDB uses for its IDs and here we will just store a list of IDs in the end here we store all the IDS of the events this user created now this is not strictly required we can't have an empty array when a user is brand-new the array will be empty but if we add data it should look like this so the elements in there should simply be IDs and by the way this year just to make this really here does not mean that we add objects to created events we add instead data fields we add our object IDs our IDs here so we will have multiple IDs in that array multiple string like objects one special field I add here in the in the definition of how my data looks like is the ref field however this is important internally for Mongoose because this allows me to set up a relation and let Mongoose know that two models are related which will later help us when we fetch data because then we can basically let Mongoose automatically merge data which is really cool ref takes a string and now it's important here we use the name of the model to which we want to connect this so here we want to connect this to the event model and in my event model file I chose event as a name like this so we should use that exact same name here with these same casing and so on and now we're telling Mongoose that there is a connection so that we store object IDs and created events and that this will actually be object IDs from my event model otherwise mongoose couldn't out because object IDs don't have metadata in them to which model they belong so now we have disconnection of course we also want to set it up in the other model in the event model every event should actually be created by a user so here I will add a creator field and now this will not be an array because every event will only have one creator so here instead this will be of type schema types object ID just as in the our um model and I will also set up a reference here and here the reference will of course point at my user model now I haven't given this model any name yet because I haven't called mongoose model yet but i will name this user eventually so I will use that name here and now of course in the user J's file when I create that Mon model with Mongoose model I should make sure that I name it user like this here too and there I will point at my user schema and just as before I want to export this so I will call or I will set module exports equal to that model now we created a user model and that is now actually connected to our events now I'll quickly go back by the way into my collection view or if you don't have that into MongoDB comm-pass and there you can clean up data I'll quickly refresh to see all the events I created since these don't have a creator I will actually delete them here to make sure that I don't have invalid data in my database so let's get rid of all of that and now go back to our code because adding this model is a nice first step we'll work on the booking later now let's of course make sure that we actually are able to create new users and that we actually also add the user ID to a newly created event and that we then add the event ID to the user so let's get there step by step go back to app J s and let's tweak our graph QL schema and we'll outsource this into a separate file eventually by the way there we now don't just have events we also have a new type to user type right I user will also have an ID underscore ID to be precise will have an email which will be required an on Nala bilang and will have a password and now the password will actually be nullable so I won't add an exclamation mark here because will not always guarantee that a password is set because when we fetch user data I never want to return that password you should never be able to access that password we'll need it when we create a user but we will not be able to ever retrieve it from the database so in my resolver I will make sure that I don't return the password therefore this is actually nullable now of course we also can create users so we'll add a new input data type and I'll name it user input and when I create a user there I will have an email which is non malleable and a password and here the password will also be non malleable because when I create a user or when I try to login a user I need to F password so if it's passed in from the outside from the front that I absolutely want it but it will never return it so therefore here I got this user input type now we obviously need the filling queries and mutations so let's start with the create user mutation you can name it however you want I will name it create user and there I believe my user input and it will return the user object to kind of give di a give an idea of which user was created this is it for now let's now go down to the resolvers and there after create event I will add to create user resolver now just as before this will get my arguments so my user input in this case and now I can add logic to create a user in the database for this let's first of all import that user model by requiring it from slash models user and down there when I create that user I will create a user object with new user there I need to set an email and a password obviously when storing it in the database we need a password so we'll access our X user input just as I did it with our X event input up there and then there should be an email input because that is how I defined it in my graph QL schema there in user input I defined that there would be an email and a password now we are accessing the email now let's go down let's do the same for the password so here I will also add password our X user input dot password and this would be a huge security flaw already now do you see what's wrong with this way of storing the user data in the database well if we store it like this then we would store the password as plain text in the database and no matter if we use SSL encryption for transmitting the data to the server at this point of time it would be plain text and it would end up as plain text in the database and if anyone ever hacks our database or is some employee who doesn't like us has access to the database then our user passwords are exposed as plain text and we absolutely don't want to have that so we should create a hashed password which can't be decrypted basically for data will quit my development server and I will install a new package which is called decrypt j/s and this gives us some cryptographic methods that help us creating a hash which you can later also compare to an incoming password to see if the incoming password is correct but which we can't build back so which we can't get the original password from so with that installed I can restart my development server and now here I will actually import be tripped by requiring bcrypt j/s here and now we can use that down there and create user and we'll do that before I create the user object I will call be tripped and then there is this hash method and hash takes as a first argument the string we want to hash so in my case the hazard rx user input password and the second argument is assault now this is basically this defines the security of the generated hash and twelve rounds of salting are considered safe now this gives us a promise because this actually is a asynchronous task it could fail for some reason in this case I want you throw an error and it could also succeed of course and in this case I will have my hashed password and I then want to use that here and now I will create that user inside of this then block once I have the hashed password and I will store that hashed password in the database now since this is a asynchronous task and we are in a resolver we want graph QL or Express graph QL to wait for us so I will return this so that Express graph you will notice that we got a asynchronous operation and it should basically go into that promise chain and wait for its to be resolved and now we are on a good road to save our users but we're not there yet we created user object but we don't save it to the database so we want to call save here and this returns this promise like object which we can also return in here so that in the next then block we actually now have the result of this operation or an error which will be handled by catch still and in there I now have the result of this operation this is now the created user so here I can now return just as before result underscore doc and also overwrite the ID to be result ID using that virtual gather provided by Mongoose now let's give this a try I have create user in place here should work I have my schema definition updated let's go back to graphical and there let's create a new mutation and you need to reload that page to have correct Auto completion again so now I have created user here as an option I can pass my user input and there I have my email for example test at test comm and I also want to have my password here tester and then I want to return something and let's actually try to get both the email and the password if I now it enter this generally looks good we were able to create our user otherwise you would have got an error you can also check this in the database if i refresh there you know i collection do I have to the users collection now and there I have that user we also see that the password is hashed but we also see that I'm able to retrieve the password and yes it's the hashed password and this is pretty secure but I actually don't even want to retrieve it because where would you ever need that it is useful for nothing and it still is even a tiny but it is still a security issue so I don't want to return my password here and one easy fix is to go down there where I create my user and I will override the password and I will always set it to null not password which I'll store in the database just a password which I retrieved thereafter so now if I run that same query again you see password is actually null I'm not able to get this it's now nullable or it was inaudible before but now I'm taking advantage of this when everything you might have noticed is that if i refresh my collection again I now I have two users with the same email address and I don't want to have that in there as well so what it will actually do first before I even create my user I will look for a user with this email address in my database and I can use that user address and add user object and there I can use find one to find one single user because if one user with that email address exists it's already too many and there shouldn't be more than one of course I will add my filter criterium here between curly braces and I'll look for the email address being equal to RX user input email and then here I have my then a my catch block and let's ignore a catch for now because I will solve this differently anyways but in the then block here I get my user and now the way Mongoose works is actually not such that if we make it into then we have a user and if we don't make it into there we have no user we will always end up in the van block unless we have a connection error or or some permission air or any other error but otherwise we'll always end up there and user IVA is undefined if there is no user that matches our filter or it will be an object and for us this means that if user if this is true ish which it would not be if it's undefined and we're looking for the undefined case of course because we were looking for the case where there is no user but if this is true ish which means there is a user then we have a problem and then I want to throw a new error whereas a user exists already otherwise if we make it past this if Jack I know this user does not exist yet so we can safely continue and now I want to return be tripped hash and when I return this in this then block and now I can chain my next then block on to this so now we would continue with the other chain if you throw this error we skip all our then blocks and we can write in there and return this error back to graph QL so now if I save dad let's try that again exact same mutation with the existing email address and now you see I get back null here and create user problem is of course that I don't return my promise here and therefore agrea graph QL finished too early or didn't wait for this promise chain to resolve so let's add return and save again and now I get an error user exists already which is what I want and if I go back to my collection view here in the database and I quickly clean up these duplicate entries so I clean up all my users for now here as well if I do all of that now we have no users in the database now I am able to run this again and I added this user to my collection because we had no user now if I reload we will see that single user we just added of course but if I now rerun this I would get this error again and thereafter if i refresh we still only have one user because we didn't hit the database instead we threw that error and we didn't continue so that's great we're now able to create users we didn't really add a login or anything like that we'll do this later the last thing I want to do is I want to be able to connect my created events with a user and for that and my models I've already prepared something I have to create or field on the event for example where I want to store the object ID off the user who created the event now later once we added authentication we'll be able to retrieve that object ID automatically because we'll basically add a header to our requests to pass that on and it will take care about this later for now I will use a simpler approach I will copy my object ID so this string here is enough off the user which I will now use as a dummy user for all created events and again we will change this later and an app j/s when we create an event I will attach this 2d created event so here on the event I'm creating I will now add the creator field because that is the name I assign in my schema and there I now want to store the ID of the user and again I will hard code this for now and the cool thing with mongooses I can't just pass a string there and Mongoose will automatically convert this to an object ID which we ultimately need you need to store an object ID so that MongoDB can work with it but mangu estas this conversion for us so we can just pass a string here and now we will save that user as the creator of this event if you remember however in our user model we also have to create an events key so once we created the event I also want to add an entry to the user to make clear hey this user has these events which were created by the user and they offered now I will go back to a purchase once I called safe and I have my events stored I will actually not return a response where a result for the resolver immediately instead here I now want you edit my user and for that I will call user find by ID and I'll find that user with the for now hard-coded ID and I will return this here so that I can simply add a new land block here and there I will have my user now of course we could somehow have a case where we don't find a user for that ID so if that is the case I will actually throw an error but that is very unlikely and it won't happen here because I hard-code the idea of a user who does exist but if we make it past as if check we know a user exists and now we can simply edit the created events so this field we can simply edit this on the user and call push there and this is a method provided by Mongoose and there we pass the event so our event object there or just the idea of the event that is enough to we could pass both Mongoose is able to handle both if we pass the whole object Mongoose will pull out the ID if we pass just the idea it's fine anyways so here I will just pass the entire event object which I'm creating up here and this has to be an object based on a mongoose model which is this so now i passed this whole event object and thereafter i will return user save here so that i access the database and and update the user this will now not create a new user it will update the existing user and therefore we add whenever then block here there i got the result of this operation and now i want to return what do i return well still an event right and now we have a problem if i copy my old return statement there I returned result doc and so on but the doc is now actually the user document of the updated user so if I want to return the created event there I need to do this in a different way and I will do it in a very simple way I will treat a new variable created event up there which is initially undefined but once I have my event created which is the case in this then block I will actually set created event equal to what I previously returned here like this oops created event should be the name of course so we'll set created event equal to that and then down there once I added my user I will just return that created event here now let's give this a try let's save that let's go back to graphical and let's create a new mutation here the create event mutation with the event input and there we want to have a title testing description this is a test a price of course 9.99 and we need to good old date again where I will again generate it with the help of the developer tools here this is my date string I want to use let's close that it's inserted here and then 40 created event I want to get my title and I want to get my description and I get user exists' already so that through an error well the message is wrong anyways and of course the check is wrong I should check for the opposite if we don't have a user and then I should return user not found my bad just copying that was of course and not enough I have to check the opposite because here the problem is if I don't have a user not if I have a user so let's try it again this looks better now now here I actually created that event and if I now go into my events a collection we will see it this worked and I actually have two events here simply because previously I messed up because of my error so I will remove one of these events and by the way if you want to handle such a case where you have like two operations on the database that kind of works together you could use a transaction maybe I'll add that later MongoDB supports transactions since version four I do cover them in my course for now don't want to dive too deep into MongoDB so let's leave it like this this error is fixed anyways now we have one event here and that event has the Creator this is our user ID and we can check the users all remember this event ID it ends with EB 4 if I go to the users collection you see there we have to create an events array and there we have an event ok this is not a different event because I am actually deleted two wrong one of my duplicate events let's fix this let's still eat this event here real quick let's go back to users and in this array we can add at this by clicking this pen icon let me delete this object ID in there update now I removed that event from the user and now if I simply create the event again now we have a clean set up and now you will see if you go back to events really quick here is our event with the user object ID and it ends with a CA 6 and if we go back to the users collection we see under created events there is this event ID so now this connection between events and users all the works we are now able to create users and create events that are connected to users that's a huge step forward now obviously bookings are still missing real authentication is still missing so there still is work left to do but we're making good progress and I hope I can welcome you back in the next part of this series
Info
Channel: Academind
Views: 59,674
Rating: undefined out of 5
Keywords: graphql, graphql api, node, nodejs, node.js, mongodb, mongodb relations, relations, models, tutorial, full project, full example, example, react, react.js
Id: MRrcUQhZwBc
Channel Id: undefined
Length: 26min 47sec (1607 seconds)
Published: Mon Dec 17 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.