#7 Dynamic 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 welcome back to this serious where we will build an entire graph QL API and a react front-end connected to it now we started working on the graph QL API will build an event booking API so we got events and we got users thus far we got our API schema which well allows us to interact with that but there are still some features missing and there are some things we can polish about this API as we currently have it I'll work on this in this video and in the next video of this series I'll then work on the actual bookings which we can make or which we can make as a user of this event booking service so let's dive into the polishing and the completion of the API as we currently have it now obviously there are more missing features but about the API as we currently have it there's one big thing that misses and that is our relation between users and events in our mongoose models which have no direct effect on the api I really want to emphasize that in these models we see that we have for a user a list of created events and in that list we store well a list of event IDs and for an event object or an event entity in the database we have that creator field which is a single user ID now currently I can't fetch that data through the API and that is the first thing I want to change so here for my event type I want to add that creator field and dad will return me a user object that is the idea and a user is defined as it is down there and for a user on the other hand I will have the created events field and you could pick different name than you have here in your mangu schemas but I will use the same names and the created events field should return me an array of events this may actually be now if a user hasn't created any events yet but the but if it has data in there then it has to be an event object and not a null object so now we updated this and we can now try to use that so here in graphical I'll now send a query to the events endpoint and there let me fetch the creator and I need to reload that page to get auto completion now let me get that creator and there let me get the ID now this works here in graphical because it fits my graph QL schema but if I hit enter I get this ID cannot represent value error okay so let's try with the email of the Creator here I actually get an average that I cannot return null for a non-null above field user email and what's the issue here the issue is that whilst I do say that an event has a creator which is of type user once I say that here in my schema definition this is not what I actually record in my resolver because for events here I do return my event my event objects as I do fetch them from the database but we always have to keep in mind that in the database there is no complete user object stored in a single event instead creator is simply holding the ID of the user so all I'm returning here is an event object where the creator field has the idea of the user the creator field is not an object with the user email or anything like that and therefore when I try to query for that it is certain to fail because I can't get the email if all I get back from the database is in the end a single string so this will actually not work here as a consequence we have to adjust the day that we return here and we should make sure that the events we return actually do have all the user information for that I obviously need to fetch information for the user who created the event I can use a feature provided by Mongoose to get this additional data I can find and before I call then I can add populate populate this a method provided by Mongoose to populate any relations at nose and Mongoose knows a relation by this ref key so here creator for an event has that ref key so when we now call populate Mongoose goes and visits that collection or that model were pointing at here on the ref key and tries to pull in all that extra data from our database to for the given user ID which is actually stored in this creator field so therefore here I can populate I just have to tell Mongoose which field i want to populate and that would be the creator field so it should look into that field and see if it can find a reference there and now with that if we save that it will restart and now let's try it again let's try to fetch the email for the creator and there it is this now works now if I try to fetch the ID here I get the error we already know from the past videos this is related to how the ID is stored and that it is this object ID object which is not understood by graph QL and we could fix that by going into our returned object and overwriting the Creator and there I actually want to get all my event dark creator data or of the Creator document but I want to override the ID and set this to event dark creator ID without the underscore to use that built-in ID virtual getter provided by Mongoose now if I hit enter this looks much better and the other fields also should still work so now we are populating our event data now obviously we could now say okay we get a creator here a Creator has created events and created events are events with the title and so on but if I hit enter we face the same problem because now we did populate our event with the rich user data for the creator field but then that extra user data that was pulled and merged into the creator field by Mongoose that still has the created events field then but that again is just an array of our IDs of events someone goose does not recursively kind of populate this and this is good because we would enter an infinite loop because if Mongoose would now populate all the created events there well then we would again have events with a creator field and if Mongoose would populate this again then we would have to created events again and Mongoose would have to populate this again and you see where this would be going and therefore this is not necessarily the best approach I can think of because we can only add a certain amount of nesting and we don't have the full flexibility of querying as deep as we want and therefore to get this flexibility I will actually implement this merging manually with a more flexible approach and for that I will add a function here in my app J's file and I will name this function user now this will be an arrow function which receives an user ID as an argument and then here I will return user find by ID to find a user by that ID in my database so by that user ID and here I will then have then and catch and if I have a problem here I will throw an error in the then block however I will have my user data and there I simply want to return user doc where I overwrite the ID so just use your ID here with that I have all the logic to fetch a user by its ID now I can use that user down there where I want to get data for my creator we can now get rid of the populate method here and then stared here on the creator I can point at my user function so keep in mind user here is really just pointing at this constant not at this at this constant here which holds my arrow function in the end so I can point at this function down there and I just need to make sure that the argument is passed in so that this user ID is passed in and I do this with the bind method and the second argument I passed there will be the argument received in the user function we defined above and there I will pass in my event doc Creator field and that will be on that object ID which mangu is retrieved from the database now let me save that and let's give this a try we still will not be able to get access to the created events yet but if I hit enter like this this still works and for my events I can get the email address of my Creator so this proves that my manual population approach here works now the cool thing is I can now also add never a constant which I'll name events and there I expect to get multiple event IDs and here I will then return event find and there I will look for all events were the ID and now here comes some special MongoDB query syntax where the ID is in dollar sign in that is a special operator understood by MongoDB in in a list of ID's so I basically write a query where I want to get all events where the idea of the event is one of the IDS to find in an array I'm passing here and that of course is the event IDs array which I expect as an argument so here I can then find a list of events basically all events that have their ID in that pool of ID's I'm getting and there I then also have then and catch and I might have an error which I just want to throw in the then block I know I have a couple of events and there I just want to return my events in a mapped version to transform each event and basically return a new object for every event in that array get my doc data but then also override the ID like this and importantly overwrite the creator field to make sure that there I point at user so add my function down there which I can thanks to hoisting so I point at user bind this and then there I want to pass this creator field so event dot Creator okay so basically here I'm just setting up that my creator property it does not hold us value but instead we'll call a function when I try to access it and for my user down there I will also make sure that they created events field which I have will also not return its value but instead will point at my events function here and why am i doing this I'll explain it in a second so here I'll point at events and I'll just make sure that I bind so that the event IDs are passed as an argument and here the event IDs can be found on user dock and then there it's the created events field so why am I doing this why am i replacing created events and creator with functions because the way graph QL works whenever through an incoming query I try to access a certain property this will actually see if the property is a value like a string or a number and it will just give me that value or if it is a function and if it is a function it will call that function for me and return the result of that function as a value for the property I tried to query so that is a flexibility we have in graph QL and this now allows me to model relations in a highly flexible way because now down there where I get all my events I make sure that D creator points at that user function so when I access this creator in my graph QL query we call that function or graph GL calls that function and gives me the data return pilot function the write the data returned by the user function happens to be there's a rich object with all the user fields and with the created events field and if I drill deeper into that and I want to get access to D created events and fetch some event data then this function would get called and therefore it would execute this function and give me that rich event data where again I could drill deeper into the Creator now this is not an infinite loop because these functions are not executed as long as I don't request that specific value on this specific level and therefore now if I save that and it reloads we can reload here and I can still get the email for the creator but I can also get to created events and there get the title and you see this works and I can now for all these created events again get the creator email and this now all the works and now I can drill into this without entering an infinite loop and this gives us all the flexibility we want or we could need so if I now add an hour event and not a user and event with event input and this will be added for the single use I have because I hard-coded that user ID in my back-end an hour and I give this a description this is just another test and I give the surprise of 999 999.9 9 and I also gift is a date off let's use to good old developer tools again real quick to get that date so here I'll just call new date UI so string real quick now I have that date string and let's answer this here and here I get back my created event and I will actually also have access to the user who created this if I hit enter I get an error for user email okay let's try without that and investigate this in a second so this generally worked now let's again get all our events here and for the events let's get the title and let's get the creator and for the creator let's get the email and there's no works for multiple events and I have another event twice because of that error I created it twice but this now works this gives us this gives us all the flexibility now regarding that error I mentioned well there the problem was simply that for create event we all return an event in the end that created event and the problem here is that there I'm not using that creator functionality so when I assign a value to created event which I'm doing here then I also of course need to make sure that I replace creator here with user and then bind this to result because we're salt here it's the variable holding that event data in this place result dark creator and now we will actually also enrich this here and now if I do create an event again so if I go back to graphical and I do create an ever um event here with create event event input and there let's insert a title this should now work let's add a description it really should let's give it a price of 29.99 and date of that same date string I used before now I should be able to drill into my Creator here yeah and this now works at this now proofs that we got this rich merging going on now thanks to the capability of graph queue of not just returning values like strings and numbers but also using values that are functions because the graft UL parser the package we're using here will then execute the function and used ad return value and this is an extremely important part of graph QL which gives us a lot of flexibility and power of course now to end this video here I actually want to clean up my files because we have that large app.js file now I want to start moving to a different structure and it will add a new folder graph QL you could name it differently and in there I want to have my schema folder and I want to have my resolvers a folder and in the schema folder I'll add index.js file and in that index.js file I basically want to export my schema we can further split this up later but for now I just want to basically export this call to build schema here so I'll cut it out of app jeaious remember that we also will need this import therefore an indexed Reyes I will import build schema and not from but by requiring graph QL like this and this is using the destructuring syntax the object restructuring syntax and then here I will module exports well basically what I had an app shares before build schema with our schema definition string and in the resolvers there I will add an index J as file queue and later we will add more files but for now I'll go with one file at least and it will now grab my root value so this object here I will grab that and we'll move that into dad resolvers file there I will module exports this but of course here we also have a couple of dependencies which we need first of all I need to grab my events and my user function here from app KS which I use in my resolvers so we should insert this year as well and of course we need a couple of imports for example we need to imports for be tripped we need our event and user import so I can cut dad out of app trace and move it into the index.js file in the resolvers like this and the nap trace I can also remove the build schema import here of course so now we just need to make sure that we import the things we now outs worst into our files back into this file here for that well I simply add a couple of imports in app j s i'll name this graph QL schema the name is totally up to you and I will require well basically what I get from the graph QL folder and there from the schema folder and there from that index file and it will also import the graph QL resolvers by requiring that from graph QL resolvers in XJS and now I will but use both here so here I have to graph QL schema and for the root value I have to graph QL resolvers and now this makes this file a bit easier to read now it crashes let's see what's wrong yeah the import pops of course are wrong here in the resolver the models are now actually two levels above where they previously were now this looks better and now if I go back and I reload graphical let's see if I go for the events again doesn't look too bad so this works not done yet though this is a great first step now one problem I have is for example if I fetch the date here this is just a string which looks like that it's not actually a readable date now that is due to the fact that the date is stored as a date object in MongoDB but when we then parse it back into a string this does not yield a readable date and that is of course our responsibility to change this therefore in the places where I do fetch my events there I now want to well also replace the date field by accessing my date but I'll wrap this into a new date call and then I'll call to I so string to create a date string that is more readable than what we had before and therefore I will also do that down there of course here I also need to replace that with event doc date wrapped a new date with two eyes of string and although when we create an event therefore the created event I should also do that and yes you could refactor that for sure I'll keep it like this for now but with that if I now fetch this again we should have a more readable date yes this is looking great now last but not least there's one other thing we could do and that is we can use async a weight of course instead of this promise syntax with then and catch and this is 100% optional you don't need to use async await but you can if you wanna use it it's relatively easy here in this resolver for example here for this events function you add a sink in front of the function so in front of your argument list and then inside of the function you don't need to return this anymore it will be returned implicitly because in an async function you always return your topmost promise so to say so instead here we just need to await this now of course and this will make sure that this also gets returned this of course all the means that we no longer use then like this instead we await our events like this year so we get rid of this line now and if async await is the syntax you don't know obviously you should learn this first but the core idea is that we can transform this code into a way that looks like it runs synchronously even though it still doesn't it still uses a promise behind the scenes just a different syntax but now I wait my events here that means that in the next line where I return events map I don't do it like this I will just call map here I will just map it like this and how do we handle errors well now we do this with try-catch so I will now simply wrap this block here with a try block and then I can catch any errors we might get and then execute the error handling code like this and let's remove that closing parenthesis and now I successfully converted this first function to use async await now one thing I need to do here at the end OS here I now need to return events so to make sure that we return something graph QL can consume otherwise it waits for it to complete but we return no value and now if I run this query just looks good and if I now dive into the created events and I get the titles here this also looks quite good perfect so this works and now of course we can convert this to use async/await to this user function just as before I now get my user here by a waiting for this can remove that and remove dis pair wrap it in try-catch catch that error we might be getting and get rid of this line and of this closing parenthesis and now here i also use try catch and here i already am returning this and this is exactly what i need to do so it just looks good and now of course we can also do it down there so here i can also add a sync in front of this function which i map to my events key here and then here I get my events I can await for this that means I can get rid of this line here I still map it down there like this I get rid of that here we should wrap this with try-catch still so let's add this here catch any errors remove that line removed at closing single parenthesis that looks good and now all the way we create an event then I can also turn this into a sinc function here I will await event save and this will give me the result which I want to store in a constant so now we remove that then block here get my created event where I use that result let's get rid of this and in the next step I want you not return user find by but instead a wait user find by here get rid of this then block of course have our check here push and then also here get an average alt and here I'll name this user save result because I already use the constant result above and now the scoping is different than before so here I will now a wait user save get rid of this get rid of this here actually we don't even need to store this in a constant we're not using the result anyways so let's instead write all that code like that it's all they used try-catch and wrap all that code up to the catch statement in a try block and then open the catch handler removed that closing parentheses and I now converted this to async await too by the way if this is too fast you of course find that finished code attached to this video were in a link in the video description last but not least I'll also set async dudas create user function to use async await so here I'll get my user by waiting for the result of find one don't need to then block here therefore remove that as well here I get my hashed password by a waiting for be tripped hash get rid of this then block therefore get rid of this here I want to get my result by a waiting for the result of users save whoops and there I accidentally removed the closing parentheses of my user constructor don't want to do that let's get rid of this down here let's also just as before wrap this all in a try-catch block all the way to the catch statement here so catch the error get rid of this line and of the closing signal normal bracket and here I might have a typo yeah password should have two S's this looks now better but there seems to be an error somewhere user has already been declared yeah here I'm fetching my user and then I recreate that user object so I will just rename that first user to existing user in these two places now if I save that that looks better it recompiles and now let's quickly check that all let's add a mutation where I create a user with my user input which has to email test - or let's first of all create one with an email address that alright exists let's get the ID now I get userexists already which I should a new email address does work now let me create an event and I will create this with event input there I will add a title this is a test description should be user to end I will actually copy the email app the idea of that user we just created because I want a hard code add into the code now so that we have different users for the different events so first of all let's add a price here $39.99 and date I'll fill that in in a second first of all I'll go back to my code and in all the places where I hard-coded the user ID and will change its later but where I did do that I will paste in that user ID of the newly created user so that would be here in the create event function and I'll restart this and now here on the front-end I'll again create my date whoops I'll create my date string real quick copy that and move it in here and get back the title user is not defined now looking into the MongoDB Atlas database I see that this event was created however so this user is not defined method here has to be stemming from the code which I just changed here and actually there the problem is that I use user as a name again that obviously clashes with user function i define up here so to solve this problem down there I will simply name this creator here and in all the places where I used user I'll use creator only in that function if I now save that let's rerun that query looks better and now we should have that event with this is a test and user two as a description we should have that in our collection twice because I now well basically ran that query twice yeah there it is and this now all seems to work let's improve this by running one final query a query here where I get all events and there I want to get the titles of all events and from the off the creators I want to get the email address let's say let's execute that and here we actually see the different users here we have different different email addresses and this now all seems to work this was a pretty complex video I guess especially with this dynamic graph QL can use this as a function and get the value thing but this is a core feature of graph QL and really powerful as you can tell and combined with the code which is now at least a bit more organized I'm really liking the progress we made thus far this was a huge step forward definitely dive into my source code which you find in the video description to have a look at it in detail in case I lost you somewhere otherwise of course I hope I see you in the next part too
Info
Channel: Academind
Views: 50,397
Rating: undefined out of 5
Keywords: graphql, graphql api, tutorial, full project, example, example project, mongodb, mongoose, relations, mongodb relations, async await, react, react.js, node, nodejs, node.js
Id: bgq7FRSPDpI
Channel Id: undefined
Length: 33min 13sec (1993 seconds)
Published: Thu Dec 20 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.