How to setup a GraphQL API with Apollo, Nexus and Prisma

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hello everyone this will be the first of a video series for learning how to create a graphql api for now we will just focus on the main concepts for a real world project understanding each of the libraries needed and i will walk you through each of the steps to create a first working version of the project there are many different ways to create a graphql apis the most popular guides include frameworks like next digest for creating full psych applications and there are also content management systems that generate a lot of the code for us to make a graphql api but this guide will be more for creating a standalone api which means that we'll still depend on some libraries but we'll have more control of the service thus facilitating the growth of our project later on these might be especially useful to those of you who already have a full stack application like in xjs and want to decouple from the front-end setup or to those who just want to create a service that might be used by different client technologies and that maybe even have the language that is different than the one that that you're using for the api so the project for this video will be a voting app which will allow users to create a board and this board may have multiple items and these items can be voted so let's jump right in so now let's talk about the organization for our project this is going to be a monorepo because we are going to be needing to have an api and then a client or other things so for facilitating the deployment it's better just to worry about a single repository so i have created i have created the volts app folder and now i'm just going to create a folder for the api and in the terminal navigate to that folder and initialize the package we can put this as votes api and leave the rest okay so now that we have our api initialized let's add typescript you might not be very familiar with typescript but it's going to give a huge value to our project later on making sure that the code that we define makes sense with the data model and also it will make sure that all the resolvers of our graphql api are compliant without a specification so now we only need to run mpx tsc in it and get the base configuration for typescript so now i'm just gonna go ahead and open the repository there you're gonna find a link on the description of this video to copy the same configuration that i'm using for this project i'm just gonna go ahead and select this and paste what i took from the repo and inside the api folder let's create another one for the source code and then index.ts and in here console.log hello okay so now let's open the terminal and after this got generated just run nose this and yep indeed it's working now let's talk about apollo server apollo is a commonly used library for serving graphql apis it can be used in different ways such as a standalone graphql server including in a server-less environment it can be also an add-on for our application that might already be using in ojs middleware such as express or fastify and it can also be used as a gateway for doing a federated data graph and more than likely you have seen or used one of the approaches that i have mentioned but today we will use the express add-on which is apollo server express which has a more extensive configuration but brings the benefit of supporting advanced features like websockets custom middleware and more in other words these will let us configure express freely in fact the apollo server library uses these under the hood and projects that start by using just apollo server sometimes need to swap it out for apollo server express when additional configuration is required so now in our project let's install the appropriate libraries for this so it's going to be john at apollo server express in version 3.3.0 and it's going to be apollo server core with the same version and also we might include express it's going to be 4.17.1 and graphql version 15.5.3 now the next thing that we should add it will be for running our project so that will be darn under development he is now deaf in version 1.1.8 and we will also add the dot amp in version 10. okay so now let's go to our package.json and in here we are going to add the scripts option and let's just go ahead and copy all the scripts that are on the repository you're going to find a link on the description of this video and don't worry not all of them are going to work but we will go through each eventually and explain what's going on the next thing that we are going to do is set up our server so this is not useful anymore and let's just import the libraries that we need so first going to be import from apollo server express and that's going to be apollo server itself and apollo server express conflict we are also going to be in the need of restarting our server every time there is a change so for doing that safely we have a plugin from apollo server core and that's going to be drain http server we are also going to import express and http okay so the next step in here is to create a function to set up everything and that's going to be start apollo server we don't need any parameters for now and the first thing to do in here is const app [Music] express to set up then we are also going to be needing to configure the http create server we are going to be passing whoops we're going to be passing the express to it and the graphql server that's going to be new apollo server and apollo server expects you to pass type devs and resolvers or instead you could pass the schema in our case we're going to be passing a schema but see since we don't have that right now let's just define the plugins so it's going to be the drain plug-in and for this we need to pass the http server so let's await to the server to start and after these we need to apply the middleware the word for now it's the app itself so now we need a promise to make sure that we are listening to the right port so to do that we need to set up a new promise of type void since it's not going to be returning anything that matters and in here we're gonna listen [Music] to the port 4000 and eventually resolve that promise okay so once our http is ready it's already listening to the port we may want to console.log to alert uh that it's already running so just go ahead and copy the message that we have on the repo that should be fine for now so this is going to tell us where our graphical api is running and now that we have a function it's just a matter of calling it so let's put it down here and that's it so open the terminal and what's going to happen now is that it's going to fail because we don't have any of the graph fuel schema declared yet so we cannot start a graph field server if we don't have at least the root types so the next thing that i want you to do is to well let's get rid of this for now to create a resolvers folder in here and this is where all our schema declaration will live so now let's create a couple of folders in here input queries and this is going to help us organize our code later on you can add a subdirectories in here for each type if you want or maybe for each type of operation that you might be doing it could be a section for updating information or for launching certain things so just try to organize by the the common characteristics that your resolvers have as you may have noticed we already have some of the benefits of typescript in our code it tells us the configurations that is expecting and but now it will truly become relevant to our project in graphql we know that the declaration of a schema is like a three so each leaf or knows of data that graphql3 um it's going to be validated by typescript in our project so for declaring this schema we have two approaches that's a schema first and code first let's talk about the first one the definition of schema in the schema first approach must be by ourselves and in there we need to specify the schema according to the rules of the graphql convention just like if we were defining a menu of available options in a restaurant but we we also need to create the actual code that resolves those requests so compared to the restaurant analogy it will be like teaching all the employees at the kitchen how to create these different dishes so you're doing the same task two times of having to specify what's part of a dish and the the problem is that we are not only in the code uh maintaining these a graphql file but also all our typescript source code um with the same structure so that's not ideal and it will make our project hard to maintain and on the opposite side the code first approach which is what we're going to be using on this project simplifies things by just requiring to focus on the code so that it generates the right schema for you so it will follow the graph field rules strictly and reduce the time spent on development and for these we have nexus so nexus is a schema first construction library and it's going to help us a lot in this process to set it up in our project just open the terminal and let's do yarn f nexus and the version i'll be using is 1.1.0 and the thing that you are going to notice is that we get an error after the installation and that's because we are having all the scripts for generating things that we haven't configured yet so for resolving that just start adding the configuration and in here we are going to need a folder for models the models are gonna be like a mirror or the data model that we have on the database but the nice thing about this is that it takes it to a higher level of abstraction it's not just the data that is coming from database but there could be some virtual fields coming from other data sources so this is basically what our graphql api it's going to be exposing we may have on the database a lot of fields of a user for example password and that is not something that we want to expose through the api so we're going to have a different field for this so in this let's just add index dot yes and we are going to import from nexus for models we are going to need object type so let's put a summary of the things we need we need a user we need board item and finally the vote okay so in here it's gonna be const user object type name this is going to be user and in definition we get access to this parameter which is going to allow us to define the different fields of that object so let's say that's going to need an iv strain first name last name and uh eventually it's gonna have relations to the other uh models but since we don't have those declared yet let's just forget it for now so in here we need to do the same for board and well we don't need these two you may want to give a name a description for an item we can have an id we can have content and last but not least we need the vote so let's just copy from above the difference though is that vote is going to map users to the item that they are voting so in here instead of having this id we are going to have item id user id eventually we can add relationships to the appropriate items but for now since we don't have any resolving logic we can forget about that so actually we also need to export all these so that the schema is built appropriately okay and now all we need to do is export this so that will be basically an index.ts file where we are exporting everything from the models and we have now to declare the mutations and the queries so for queries let's do the same the difference is that we are not going to define objects but we are going to define query so for that import from nexus and it's going to be query field export const we may want to see the boards that we have that's going to be for now no level since we don't have data and also this is going to be a list so for doing lists in nexus just import lists of course the items should not be null on a list but the list itself can be okay then to resolve this we have to pass an asynchronous function oops my butt and in here just return an empty list for now the next thing that we are going to need is getting a specific board so just copy this and this is actually just going to be the item so you can get rid of those [Music] and return no okay so let's repeat the process of exporting everything as you may have seen is just taking everything from there and the next thing to define are the mutations so in here we need to allow users to create the boards the items and both for those and in this case it's going to be as you may have guessed mutation field and when we create a board we may want to return such a thing then we can copy this for create word item that's going to return an item and it's going to return null for now just repeat the process eventually we are also going to need arguments for this i'll get deeper into that later but right now we need to make sure that we have the layout of the logic that we are going to be needing okay so now all these exports are going to be taken by nexus and nexus is going to generate the graphql declaration that we need now for declaring the schema we are going to be needing to install another package so yarn f graphql middleware and it's going to be 6.1.6 so make sure to specify that and while these installs let's create a file in source which is going to be schema.ts and context.ts so the context in case you're not familiar with graph fuel it would it will be passed down to each of the resolvers in these three so it uses this concept this design of dependency injection so in there we can pass the request from express we can pass the response details and of course any library that we may need down the line with the resolvers so for defining this context let's import from apollo server express list apollo server and here's going to be express context then let's import from express and that's going to be request and response so now in here we need to define the interface the data type of what our context will be so that nexus is aware of what the resolver is receiving as context that will be export context the request will be request response and then we need the function to create actually the context so export cons now sorry async function create context and in here we are going to be receiving the request of express so it's going to be express context and that's going to be returning a promise which partially is gonna fulfill the context then we just return whatever comes in the request and response will be request.rest [Music] and request request dot wreck [Music] okay so now that we have created the context we can go to the schema and configure it import from graphql middleware now we apply middleware we're converting our nexus into actually graphql definition import from nexus max schema from path then import all the types from the resolvers and now constant schema is going to be equal to applying middleware of making schema from types adding the plugins and then having as uh generated outputs a schema and the types so this is gonna be have the join with your name schema.graph field this is where all the specification will be saved and then typed in it's gonna be a path i join your name schema type gen then name doesn't really matter these will just contain all the typescript declarations that we can reuse later on nexus of course also uses those and down below let's define the context site so the module for these will be require the resolve context the alias is going to be context and it's going to be an export of context last but not least let's just add this configuration okay so this is a basic setup it's just exported and there are plenty more options that you can see on the nexus yes documentation this is just what i usually recommend for the setup so now that we have these let's go to index and pass it to the configuration of apollo okay so now the one more thing that i want you to have is a plugin know that we have the apollo plug-in drain uh for restarting our server but by default apollo is going to try to open um use a placeholder from them and in here you can instead place the playground so that you can actually see the documentation and also test your apis uh queries and mutations so should be this one nope it should be from apollo server core directly and that's going to be a function now to make this work right we are going to be needing one more dependency that's going to be ts node so while this installs let's go through the scripts so right now uh you're gonna see these degenerate.prisma and when we set up later on our orm these will become important by now just just just see that we have the generation for nexus and basically this is gonna take our configuration under schema it's gonna take all the different nodes in the tree and build the schema for graphql also it will get all the type generation so that we can reuse it on our development and in here at this script depos install it will just make sure to generate both nexus and prisma outputs as soon as we install something so more than likely it's going to keep failing but eventually when we have prisma ready set up everything will be running fine so in here yep it installed i showed an error we can ignore it for now and now let's actually run and you can see that it got generated this file it was already present in fact since we have the post install but yeah and here is the declaration of the types so to set prisma up let's open our terminal let's add prisma 3.0.2 as a development dependency and after prisma gets installed we are also going to be adding the prisma client as a dependency now we can see that the generation is gonna run and once we have prisma we need to do mpx prisma init and this will create a prisma folder with a schema file where we can declare all our models so prisma actually by default is going to put osgrist but is not the only type it supports all the relational databases so depending on your requirements or preference you can choose any of the other ones you can even use that as of today is a preview feature but it i just want you to know that is not locked into just one option i'm going to use a hospice as preference you can find here also the emv file and this is just database connection string so let's configure a database for this i had created one previously with postgres i'm connecting to localhost so this is fine and then votes and the schema by default is public so that's fine now let's go to prisma schema.prisma you can find more about good practices and advanced features in the documentation for now let's just be basic and start declaring some of the models that we need so the user is going to be needing an id that's going to be a string we need to indicate prisma that this is an id and the default is going to be a cuid first name string and this is gonna be for last name two you may also need uh an email so let's just put things in here and more time likely should be good to put unique password that's going to be a string 2 whoops then model board we can reuse this it's gonna have a name it's gonna have a description and it will have relations to other items but let's declare those first so model item it's gonna be an id it's gonna have content oh and also item is going to have a relation to the board so you can use board id which is going to be a string or type board and if you like me use a visual serial code prisma has this pretty cool extension that you can use for out of formatting so if i run this now you can see that it completed the rest of my statement using the right variable so that's a pretty cool feature of vs code with prisma it definitely helps you speed up the development process so in here the only thing that i have left to do let's fix these two items so that it's more human readable and let's add model vote and vote is going to be as i mentioned previously a relation between a user and an item so we're gonna need a user id string oh no oh yeah it's a string and then item id and that's going to point to a user and an item but since we don't have a single id in here we need user id and item id to be the identifier so let's just format boom magic so now let's go to user make these votes it's a list of votes and in here let's uh check okay so let's just confirm with the models in case we skip anything okay seems like we have covered and now under the local development prisma has the command mpx prisma dev note that this is only intended to be used in development mode and that's because it will create a shadow database to make sure that the changes that you are making will not affect in a negative way the structure that you have on the database and if that passes it will actually apply it to the the database that we configured on the dmv file so i will describe later in this video series how to do a production at production level but for now let's just run this oh sorry it's prisma migrate yeah so this is gonna create a folder inside the migrations folder that is about to show up here and oops seems like i already had one yeah no worries so in your local you should already went straight to the question of how you want to name your migration and once that's done it will create this record in here with the actual sql that it was run also there is a possibility to just create the file and not run it and that's by adding the flag create only so you may want to do that in the future later on if you have let's say a table and you're going to make a drastic change to that table so your old data may not be directly transformable to the new change so you can add your own sql in here and that should be run and for performing that migration process successfully so yeah now that we are running this process of generation we not only get all the outputs from nexus but we also get the prisma client which is going to allow us to connect to the database safely since all the methods and types inside are going to match our schema.prisma declaration so seems like we entered into a loop in here anyway let's close these files oh my bad seems like i have forgotten to install this dependency and from what i see it turns out that we don't have the prisma client yet so let's add it it should be the same version as the prisma package that we added to the dev dependencies and now we can see at the generation point that it's gonna generate everything for nexus and prisma okay yeah it completed all right so let's to yarn dev and it's gonna uh tell you uh that we are using the localhost slash graph deal so if you actually open that in your browser you're going to see this ui where you can see the queries that we defined previously seems like we may have forgotten to export the mutations yeah so let's update that and as you can see the restore process it's going to take care of our server that's why we set up that plugin earlier and yep now we can see the mutations now that we have prisma set up correctly and that we have checked that our server works we actually can start initializing prisma as a client i want to make the note as well that you should be following the naming conventions that prisma recommends that will make sure that the generation process works as suspected so if you have already a database which the name of the table in plural or with the other thing that is not camel case try to use this property map to map it to that name but in the end at the end of the day you should have these same format of singular and pascal case actually for the name of your models you can find more about the good practices inside the prisma docs okay so now after stating that let's create a clients folder in here and inside clients let's create prisma dot yes so we are gonna import from prisma so this will be prisma client and then the approach is to define a singleton so it will be const prisma equal new prisma client and here as well you can extend later on your configuration but i'm just gonna go ahead with the basics in here export cons the prisma okay so i had created these now let's export as you can see it's just exporting everything from that file later on we can add more clients like to contact other apis or databases even so in here let's uh go to the context and this is gonna be prisma the type is the class prisma client and the only thing left in here will be to have christmas which is going to be our singleton from the clients okay so now as mentioned previously these it's going to be passed to all our resolvers in the tree so to do that we need to get to the root of the tree and in the index index.ts so inside the configuration you can see context so that will be create context and if you remember correctly this is going to receive the express context so every time there is an incoming request to our api it's going to create a context using this function where there's going to be a singleton instance of prisma and that will be passed down the tree to all resolvers so that the operations can be handled on the database now let's go to our models to start using what we implemented let's define the relationships so in here for boards we may want to lease the items as well so let's add a non-new list that's gonna have non-null fields and that's gonna be items on the type it's going to be item so the result it's going to be using the context that we just declared and as we can see in here we have access to the different models that were defined so that's going to give you a lot of safety at least more than declaring your own sql queries so if you make a change everything will be generated again well the the the process itself make your api safe in the sense that no code is going to be possible to be built if it doesn't make sense with that declaration so in here we may want to just get bored find [Music] where no sorry yeah fine first or well this can actually be fine unique and the id will be the id of the root [Music] and if we find any we can return the items directly and we may want to reject if we cannot find that so let's add these okay so now let's do the same for the other items so i'm gonna copy these this is not gonna be a list uh for board but it's gonna be but it's gonna be a list for both for both uh sorry so in here gold self-time mode and here's another an item [Music] um any here votes okay so now we can worry about the other ones we can do an item and of course that's not gonna be a list so let's remove that okay and we're just gonna return that item now let's just add the user it's gonna be obtained user user id also try to have your server running these will make sure to regenerate everything for you okay so looks like we have everything set up now we can proceed to the actual logic of our application so let's go to these queries we're going to return uh brisbane boards dot find many and we don't care about filtering right now so let's just return everything and this is a non-null list of non-no words in this case is going to return ctx christmas board and here is where things are gonna get interesting because we need arguments to allow users to filter this so in here let's create an input well actually it's going to be a where clause so the goal uh with this is that we can get filtering and in here it will be something like rx dot word id for proceeding with this let's go to inputs and declare index dot ts and we are actually going to learn about another option from nexus so in here there is an input object type and we are going to be needing a filter for the specific board right so it's going to be board where unique input and that's going to be an input object type so just copy these keep in mind that this is a really common way to name the inputs for uniqueness if we were looking for a more generic filter which may return multiple things just take the unique out we can take this putting here and the definition will be similar to how we define just regular objects and here we just pass id and that's it so let's just make sure that this is enforced all the time okay so now i will export it and i can use it in here so word where you need input it's going to provide us what we need sometimes that happens the the code may have errors and that makes the running process of our api fail but but if you run it again it should be good so as you can see now there are no more errors on the syntax and we have access to the argument that is being passed to the query now let's repeat the process but for mutations so in mutations we need to pass information to each of these and one of the best practices for the graphql apis is to pass a single input so if you have create board the only argument that will be passed is input and the name of that will be create board input so it's always taking the name of the mutation and then adding the word input to the end if it was something like update like in this case is removed well the good practice for that is to add the where clause and we will be passing the non no board where input or unique input so as you can see we are reusing something that we already have and well now let's do the same for items so in here i'm just gonna change these to item it's going to be id let's copy this actually into here it's going to be it's an item where unique input okay so the next two things uh in here will be the create board and create board item let's copy one of these as reference create board item input okay so some uh times uh you need to be really careful on the things that you're passing to the input because you may be allowing on desired behavior so if there are some protected fields that you don't want your client to mess up with i just don't include it in the inputs for this of course we don't want an id to be sent but what we do want is the name also forgot to put this in here okay so we want the name and we want the description all right looking good so let's copy this and we need it for a board as well board item is going to be more simple because it's just content the one that has description is the the board now let's paste this okay okay yeah looking good and now we can go back to our mutations and it will be create board input all right and in here will be ctx dot prisma dot board dot create data and that's just going to be arcs input okay so let's uh run this again for create word item is going to be similar so just copy paste this or item input just maybe okay and here copy these it's gonna be item and then input but guess what we are missing information so in here we don't know to which board these should point to so we have two ways uh we could put it inside here or we could use a where i'm gonna use aware so that there is reusability in the code and that will be board where you next input okay so now in here the board id it's going to be args dot where the id and taking a look at this yep it went fine all right so now if we did everything correctly we should be having a functional graphql api so the next step on this will be to test our result and see what's going on oh wait we forgot about fault items so yeah let's add that quickly so this is going to be vote item delete this is going to be aware and this is actually going to be equal to our software oh okay yeah now it's fine and in here it's gonna have a item for these we're just oh um yeah so we can do update or create its up to you and here i'll be using create the data in here will just be the user id which for now let's just leave it empty we are also going to be using the item id so in here let's say args dot oh sorry yeah i forgot i renamed this rx.were dot id so ideally when we implement authentication we are gonna be having a function that provides us information about the user so that's what it's going to give us the user id and we can replace that so for now let's keep it simple and do not worry about that and in here we may want to just return a vote rather than an item before proceeding to testing our api in the browser there is something else that i would like to mention so right now we have the removal mutation and that's gonna cause a problem since uh by default uh our model uh it's gonna detect a dependency of the items uh behind scenes we need to make sure to set up something to tell the the orm that when there is an item being removed all the votes of that item should be removed as well so let's talk about how to do that usually that's something that we defined when we are creating our database so actually the name of that in prisma is referential actions and with referential actions we can achieve that same behavior pretty easily the only thing that we need to do is setting the relations with that behavior that we want so in here in the case of vote if we delete an item or we delete a user we should be deleting this these both as well right because otherwise the required dependency is not going to fulfill so let's set in this relationship on delete and this is going to be equal to cascade okay so now we are telling that if the user gets deleted then propagate that to vote so it will be deleted as well and in here do it is free item and if we delete the board it will make sense to also remove the items otherwise we are not going to be able to access those items anymore all right so now that we have this in place we can just open our terminal kill the api increase my grid def referential actions all right so enter it will apply the changes to our database let's make sure that the api is running and in the browser on the playground we can add a query to get the boards anyone the id name items and for the items i want the id content if we run it we should get an empty list yeah that's fine so now let's add the mutation to create a word so now let's run this we should be able to see it in words yep that's right okay and now let's do the same in fact i'm gonna copy paste this and we want create board item input okay and in here will be content we also need the aware okay and this is going to be required and here that's how they were okay and now in variables and where we specify the id of the board that we want and yeah we get the item so in here should be reflected let's create one more yes now let's copy these into a new one this is gonna be for the vote so both item we can remove this and in here we don't need the input just where and that can be just the item now let's let's be practical and just add the id hmm i'm gonna take the idea of the second one whoops so we got an error and you may be wondering why so um as i was pointing out before inside the mutations we don't have a way to determine the use already and this is actually something good about prisma that is showing us these validation of what went wrong on the query so what we could do in here is instead of passing the user id we can say user for this item and for item we will be connecting to an existing id whereas the user it's gonna be created and that's it then it's gonna have just anything that you want so let's put whatever in here last name uh email let's put a date since uh it's being unique uh it's gonna complain if we have other thing so just the delay the email and for this we're gonna need a password as well so it's not ideal but i mean it will be changed in the future when we have authentication sound so now let's wait until the api results and uh try it again okay so now we should be having a new vote in here so let's just list user id okay yeah and we indeed get a vote for the second item let's add a new one yup we got it okay and now the last piece of these will be to remove an item so i'm just gonna copy the previous one so i'm just gonna paste the previous one remove item and in here on their mutations this should actually be i don't wear unique input okay yeah some minor mistakes but it should be fine soon okay yeah so as you can see the error went away and now we only need to pass the appropriate variables so it's going to be where the item that we want to remove is the one that we just voted on so the id in here will be this okay so what's going to happen now is that this second item should go away along with the votes that are inside voila now the mutations behave as expected and we have the first working version of the api in the next videos i will show you how to keep improving our api with exciting features if there are any topics where you would like further explanation let me know in the comments below and they may appear in the next video don't forget to subscribe and share the video if you find it useful thanks for watching
Info
Channel: Victor Iris
Views: 726
Rating: undefined out of 5
Keywords: GraphQL, Prisma ORM, Prisma.io, Apollo Server, API, Typescript, ExpressJS, NexusJS
Id: sWlzqRB5Xro
Channel Id: undefined
Length: 76min 38sec (4598 seconds)
Published: Thu Sep 30 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.