GraphQL Microservice Setup with Apollo Server, Codegen, Prisma, Typescript, and PostgreSQL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hey guys and welcome back to another tutorial and today we're going to be looking at how to set up a graphql microservice using typescript prisma cogen and apollo server so that's going to be the setup and let's get started so first thing we're going to do is we're going to go ahead and create our project so let's just use my cli here and we're going to be using the node uh typescript project name and then here we're just going to make it gqlts video let's go ahead do that let's switch into that oh i need to finish it out there we go and let's go ahead and open it and the first thing we're going to want to do with oh this is new i don't think i've ever seen this before but yeah i trust myself i think probably shouldn't though but all right and first thing we want to do let's just go ahead and install the project dependencies cool so and if you're not familiar with this project i'll put a link in the description below to the github as well as the the blog article and the other youtube video i made to how to set up this project correctly so or at least for the starter project so let's go ahead and actually start installing some of the dependencies that we're going to need so the first two dependencies that we're going to want are graphql and apollo server and let's go ahead and add those and while those are being added let's go ahead and actually do a little bit of cleanup here so let's just remove these example tests and i guess we can also kind of discuss what graphql is just in case you're not too familiar with it so graphql pretty much is just another type of way to fetch your data essentially so it's kind of compared to rest apis a lot it's just another way that you can write your apis but the reason that it's so popular is because you can actually only fetch the data that you need so if let's say for example you have an object that has around 10 fields but you really only care about two of them like the name and the title maybe of a book so what you could do then is you can construct your query your request to only ask for those two so that way when you return that to the front end all it's returning is the fields that you care about and all of the other stuff that you don't really care about you're not even having to worry about and with this comes another cool uh benefit of graphql which means that you don't really have to version your api so essentially you'll have all these fields in the back end but you can kind of change it how you want to construct it on the front end meaning that you shouldn't ever really have to um version your apis which is awesome and with that being finished installing let's go ahead and actually modify our index.ts to kind of uh do a couple of things with um apollo server and graphql just a quick setup so what we're doing here is we're importing our uh apollo server as well as gpl from apollo server i went ahead and created this kind of this is kind of based off of their documentation so um just kind of creating this list of books then what we're going to do is we're going to define some type definitions and within here we're going to define the type of a book which is just title and author so it's matching up here and we're also going to define a query type that has this query called books so what this query type is pretty much is just going to say it's just like a function and queries usually represent get functions if you're familiar with rest so what we're doing is anytime we call books we just want to really return a list of books and then we're going to actually go ahead and implement that books query in this resolver so this is pretty much where you define the function and then so books here what we're saying is all we're going to do is we're going to return books and then we're going to create our server and we're going to pass these type definitions as well as these resolvers and then we're going to tell our server like oh hey let's go ahead and actually start listening so what we can do is we can actually start up our um dev server here oh that's not the right place and what we can then do after that is we can navigate to this localhost 4000 graphql and here you can see if you're not familiar with this playground it's pretty much just kind of like another api testing tool like postman or insomnia but it's built directly into your graphql server so and it's got some pretty cool things so for example you can see some schema here so you can see the types that are defined as well as the queries it has some cache control things if you are interested in that as well as you can uh kind of open up some documentation uh with regards to your queries and mutations so let's just see that everything is working correctly and we're going to do that just by creating a simple by calling our simple query so in order to construct the query all you got to do is just have this opening and close and you can use some other kind of syntax but this is just the simplest way to do it and the query name and then the fields that you expect so then if i were to go run this you can see that the data return is a list of books and that is the two books that we define and then you can also kind of customize it so let's say for example i don't want title but i only want author i can go ahead and change my query to only return that field so cool it looks like everything's working one other thing that i kind of like to do as well is i actually like to add a little bit more so this playground can also obviously it's calling your back in directly so in order to add just a little bit of security what i like to do is in my server instantiation here i actually go ahead and you can pass in a couple of different options so you can look at the documentation for all the different options you have but one of them is actually playground and what i usually like to do is just i like to make sure that it's only available um outside of so lower environments so making sure that it's not available in our production environment okay so we have that set up so the next thing we're gonna do is we're going to if you don't have this extension set up actually uh let's go ahead and you can install this graphql extension which would allow us to kind of just get a couple of high syntax highlighting and things like that in our visual studio and so that'll just kind of make it easier for us to work with uh graphql files but so first thing we're going to do is we're going to actually create this folder structure and um so the folder structure i'm going to create it's probably easier just for me to show you guys actually after i run this command and this what i'd like to do with my microservice graphql i like to kind of organize it in a couple of ways so we're going to have a generated since we're going to be using cogen i like to put my generated files within this folder and then i also like to keep my schema kind of separate from everything else and within the schema there are two things usually that kind of construct the schema there are your resolvers and they're your type definitions so type definitions for now is empty since we don't have any defined but then resolvers i like to separate them out into two different types of resolvers so you either have your mutations so think mutations as create update delete anything that kind of alters data and then just hence the name mutation and query is kind of your get request anytime you're fetching data they usually are queries and this is kind of the structure that i like to go with but and we're going to be using that for this video but you can feel free to play around with it um if you'd like another kind of uh folder structure that i like to keep um is also i like to kind of create this data folder and this data folder is going to kind of represent like a data layer so i like to define anything that kind of goes out and fetches um information from a database we're going to be using postgres with prisma to kind of uh have those functions defined here and the reason that i do this is just for kind of the separation of concerns as well as just making it easier to test so i can actually just test the functions in here individually unit testing without necessarily having to test my resolvers because resolver testing is kind of a sometimes you need to do uh most of the time i usually just do integration testing with resolvers but yeah so with that being set up let's actually go ahead and create our first type definition and what we're going to create is we're going to create a graphql book type definition and uh one of the things that you'll notice is that my type definition files are all capitalized and i like to keep types and interfaces uh to start off at least their file name capitalize and just it makes me understand that like okay this is a type this is a interface and things like that so other things like mutations and queries will actually be lowercased so yeah let's create that and let's define that and let's kind of explain what's going on here so graphql has a set things of different types that you can actually import and what we're going to be doing is we're going to be creating this uh graphql book and i actually need to change this to gql book and so let's change that here let's change that there and let me change that here really quick and what you can do with this is you can kind of add a little bit stronger typing to your graphql without necessarily having to go into the whole code first solution so what i'm defining here is i'm telling everybody that this is a graphql book is a graphql object type the name of this graphql book is a book and so this is uh actually also defined as so later on we'll you'll see we can actually uh call uh underscore underscore type name and we'll actually return this so it's kind of useful to have this similar to your typing the description of it you can just it just adds a comment to your playground and then the fields that are included in this graphql object type so we're having an id this id cannot be null and it's going to be of type graphql id um we also have title which is graphql string and then the author graphql string and since i don't define these as non-null they can actually not be null um if you want to have a stronger type safety just define it as null or non-null the next type that i want to create as you've noticed before i went ahead and created this uh data dot types and the reason for this is um pretty simple i wanna with cojon you kind of have to manually define your types but before you like jump out of this video or like start saying like oh why would you do that just just for right now until i have prisma set up later on we're going to actually be using prisma as kind of like a source of truth for our models within our database to also include as our types that we return from different functions so this is just for now for just to kind of it kind of eases into the implementation of prisma so let's just define it let's export it as book like this and the next thing we're going to do is we're actually going to also go ahead and create our first query and this is going to be our get all books query similar to the way that we have it already defined in index but we're going to kind of just export it out or just kind of separate it from our index ts to kind of this its own separate entity and here you can see we're doing something pretty similar we're just going ahead and we have the same list of books but now we have an id since we're expecting id in this book type and then what we're also doing is we're defining it as an object with type uh the so this type here is a reference to the output type so like what do we expect to get back from this query and we're expecting a graphql list so it's just another type that is available from graphql and it's going to return this graphql book right and so graphql book as you remember is our type definition of id title and author and it's just a graphql object so we're expecting that as our return type but with then we can also pass in a resolve function and what this resolve function is is pretty much a it's just how the graphql uh resolver that we expect to actually execute anytime we kind of use this query so and if you're not familiar with the typing of uh this resolver i had it kind of simplified but you can actually pass in a couple of other fields here like context and this is unknown for right now and you can also have info so these are kind of the four parameters that a resolve function expects since we're not really doing anything with it right now i just left it as just let's just have it source as unknown return a list of books return my array of books so with that all done what we're going to have to do next is we're actually going to go ahead and create this query object and this query object is going to kind of just act as uh similar to how we had it in here or whereas in our index so here you can see we have this type query so this is pretty much just going to act as kind of like a kind of like as a rapper or a holder where in here because like one of my least favorite things about graphql sometimes is that you have these giant type definition files or giant resolver files or you even sometimes have them both in the same file and it gets huge and you have to start searching and control f and there's a bunch of like i just don't like that so i like to kind of separate my queries my mutations everything into their own separate files this does lead to a little bit more of a completed folder structure but i think in the end it kind of makes everything a little bit easier to understand so what this query object is doing actually so we're defining it as a graphql object type similar to before but we're actually naming it query so similar to index we're type query name query and then what we can actually do is we'll pass in fields and then in these fields similar to here or where is it similar to here as we're passing in our books uh function to return a list of books what we're doing is we're actually passing in our query which is also a graphql object type with the output expected to be that of where is it ah there it is of a graphql list of books so it's kind of expecting this as the output which is pretty much if you look at here a list of books so cool so everything is set up that way and so the next thing that we need to do is we also need to create our schema and the reason for that is since we're not going to be using resolvers and type definitions directly into creating our apollo server another way to create it is actually via a schema so the way to do that is let's just go ahead and create this so like if we minimize these i just have this is in the root of the schema folder and then we're going to actually go ahead and create it like this and a quick explanation of this is graphql schema is a type we define it as that we create a new one and then query you can also do like if you want to name it something different query and pass it a name since i decided to actually just call it query lowercase i can just pass it in here and it knows where to put it so this creates our schema and with that set up we can actually then go back into our index and instead of having everything kind of in here we can uh obfuscate it out to its respective resolver type definitions and things like that and all we got to do is just change it around so that now instead of it calling uh with our type definitions and resolver we actually just pass in our schema and with that let's see and make sure that everything is working the same and see if we can still get that list of books right so making sure that we didn't break anything so now if i go back in here we can see that nothing really changed like our schema changed up a little bit so before we had all those other kind of like the enums and scalars and things like that now we kind of just have the only things that are related to our schema but we still have this book's documentation you can see here now that it accepts an id so if i go ahead and add id to this what you'll see here is that it actually goes ahead and returns um id of one so cool so everything is still working as intended and after that we can start looking into setting up our code generation and before that the first thing i'm going to do is i'm actually going to go into our eslint ignore here and add slurs source source source slash graphql slash generated um to our uh eslint ignore and the reason for this is just because i don't want our linting to go through and try to lint our generated files it doesn't really need to do that they're generated and there's going to be probably some issues with that however our eslint is set up so let's just quickly add that and so next thing we're going to do is we're going to look into setting up code gen and as i mentioned before there's really two ways that you can kind of define your graphql back in and the two ways that are kind of the most popular are schema first i'm not entirely sure why it's called schema first but it's sdl which is pretty much it's generated by cogen and graphql or there's code first and there's two really popular libraries or the most popular being type graphql that's been around for a little bit i've used it i really enjoy it i don't really necessarily enjoy using it in a microservice structure i think it's just too heavily bloated and the class system and everything kind of just gets a little confusing if you are using um i can kind of let me just pull that up real quick so type graphql so this is kind of just a it's a pretty nice library to work with if uh you're using uh one apollo server for multiple types kind of keeping everything in a single server i think type graphql is the way to go it's really really powerful the next kind of player in um i actually need to do next graphql not the mods get some skyrim mods um yeah so the next kind of the younger player is called graphql nexus and this is actually being worked on uh also with by the prisma team but pretty much it's another code for solution to kind of make it a little bit not as classy as typegraphql the only problem with this is that it is still relatively young and um i didn't really necessarily want to use it because the documentation could use a little bit of work as well as there's a bunch of issues here as you can see like 178 issues and that's probably just because it is it was just released at the end of 2020 so for my micro services if this gets a little bit more improved i'll look back into seeing how uh to use it and how it really because i can i bet it probably plays in really well with prisma but with the current setup that i have i actually really enjoy it so cogeneration let's get started into installing that so the first thing we got to do to install code generation is we actually have to install the cli for it and you can just install it as a dev dependency and another kind of cool reason i like using code gen is because i've used it on the front end before and what you can do is you can actually point your front end to wherever your graphql endpoint is so whether it be a microservice or a gateway if you're using federation and you can actually point it there and it'll generate all of these hooks and everything else for you so it's really really powerful and after you install that what you can then do is you can actually run this graphql cogent initializer and here you can see it starts giving us a bunch of different options so for example if i were to application built with react like a front end it will actually go ahead and build hooks and build different things for me but we don't need to worry about that right now i'll make a different video for that but for our application we're going to be using our back end so let's set it to that and i actually have a little bit of a little thing that i like to do here that might be a little different than using the local hose so one of the things that really bugs me is with localhost you have to have your server running in order for generate to work and i don't like that so what we're going to do is we're actually going to say we're going to point it to a graphql file and we're going to then go ahead and uh pick these two plugins we want those and then i'm just going to change this output a little bit to add since i have this generated folder i'm just going to add that as to part of the path um i don't necessarily want introspection name it that and let's make it generate as the script and that's going to go ahead and um we actually need to then go ahead and run install because uh those uh it doesn't install them for you adds it to your dependency list but it kind of expects you to run install not too sure why that's done but that kind of that's what it does so um so what does that do for us it creates this code gen dot yaml and this koji nadine ambo is what's going to be executed every time we run this script that was added so if you look at generate you can see here that it calls graphql cogen and it points to that uh cogen yemel and what this is doing is pretty much saying okay if there if this uh file exists or whatever oh if this exists overwrite it every time we run it um schema is like where should i look to figure out how to generate this file and so it kind of you can use it at localhost but i like using it as a graphql file document set it as null and generates is what are we going to generate what plugins to use where is it going to be generated so everything looks like it installed and so let's go ahead and actually run that script with yarn generate and if it should yeah okay so it should fail as expected and the reason for that is because like as i mentioned before with one we don't have a server running and two we don't actually have anything within generated right now so it's trying to pick up our schema it doesn't exist of course it's gonna fail so there's two ways that we can go about creating this schema and one of the ways that i mentioned was via you could change this to localhost start your server start a new another win or another terminal uh run generate it'll pick it up and i just hate that workflow um i don't i don't know why it just it kind of bugs me so not that this different workflow is too different but we can actually programmatically call a schema generation um every time we start a server and so that way if our server is down and we want to generate it based off the last server run um we can actually use just yarn generate and it'll go ahead and be able to pick that up so here's some of the libraries we need to install first before we actually go ahead and programmatically add that and pretty much what we're doing is we're just adding this ast plugin what this pretty much does is just helps print your merge schema to string so it just kind of just prints that out plug-in helpers there's just some other helpers for your plugins and the core is pretty much mainly used for the code actual code gen function so that we can uh pretty much just the library core functions that are used and so let's make sure that those were installed correctly and we're actually installing those as dependencies not dev dependencies because we're going to have to then create this source cogen file and within here we're going to create a function uh called perform ast code gen and i'm going to actually i don't want to prefer x export default in here just because i didn't want to just write it as a function it kind of makes me understand a little bit better that this is the cogen file so let's kind of look at what we're doing here right so we're importing the file system we're also importing so this is why we needed it as a dependency because we're going to import the whole plugin as this plugin then from graphql we're going to parse and also print schema we're bringing in types from the plugin helpers we're bringing the actual cogen function and then path and the um schema from our schema file here right so we're actually bringing this in so what we're doing then is oh let me close all these it's a little easier to understand where i'm at is we have this function called perform cogen so pretty much what this is doing is we're taking in some options and we're just going to wait for our output to kind of generate this uh code for us and then we're going to write that file to graphql generated with our file name so the file name here being schema.graphql and then we're going to perform ast cojon here so this is go this is what's going to generate our schema for us whenever we start our server we're going to actually pass in a couple of options so config numerical numbers is true documents i just found this through another repo i'm not too entirely sure what this does but it's kind of like setting up the same options as here right so then our schema what we're doing is we're actually parsing and printing the schema that we're defining within our schema.ts we're telling them to like hey the plugins that we're going to use is schema ast and it's going to be an object and then the plugin map is schema ast and then we're going to just import that library to use there so and then we're actually going to go ahead and call our perform cogen file and it's going to do that for us so once that's done what we can then do is we can actually go back to our index.ts and let us just add pretty much all i did here is that i'm importing our cogen from our cogen file and i'm performing it before we start our server and so now what we can do is we can actually go ahead and run yarndev and what this is going to do is going to start up our server for us but it's also going to generate that graphql file that we're expecting so if we take a look here you can see the outputs are generated and if i close my server here we can see we have our type of book and our type of query which defines books and that's pretty much what our scheme was set up to be right now what i can then do is i can actually run yarn generate with no server running and it'll actually be able to pick it up from the schema.graphql and it'll generate this graphql type file for us which just comes with a bunch of resolvers and everything like that which you can use later on if you if you'd like so yeah really powerful really cool it's just a little bit of a you don't have to have a server running but you see you still kind of technically need a server running but this is just kind of the flow that i've chosen to go with for the next part of this video we're going to go ahead and actually start uh setting up the database and the database i'm going to be using for this tutorial is actually postgresql postgresql um not sure how to say that but so pretty much if you don't have that installed just go to their download page download uh whatever and install it and i already have it installed so i don't really need to go ahead and do that but then what we can do is we can actually open up our uh terminal here and run the command post sql dash u and my i'm just going to use the postgres user go ahead and do this and then what i want to do right is in order for me to start create creating these tables that i'm going to be using i actually need to go ahead and create the database and i'm going just to name it book db and there you go and so if i actually go ahead and list these out you can see here that now i have my book database created things are ready to go and i can actually go ahead and quit out of that so with that set up now we can actually start looking into setting up prisma so what is prisma um let's i can kind of explain that once i start adding these uh dependencies so if you're not familiar with prisma it's pretty much like kind of like this weird combination between schema generation and um an orm but it's not necessarily like a full orm it's really really cool and um it's really well maintained um i really enjoy using it i've kind of only started using it as of late but pretty much there's two big players that i usually kind of look into and it's prisma and type orm um as of late i've kind of been doing a little bit more research at least for this video and i noticed that there's been some kind of like issues with typo rm where they don't really know who's going to maintain it the main guy doesn't necessarily want to maintain it there's some like funding issues and has some big bugs that i guess i aren't taking being taken care of i could be wrong in that um that's just kind of from the research that i did so i went ahead and decided to go with prisma i know prisma is also uh really involved in the graphql community so for example graphql nexus that i showed the code first solution is also they're helping out with that so um why not use them so first thing we got to do is we're going to install if you missed it the prisma client so we need to add that as a dependency and another thing that we want to do if you don't have it installed is a let's just go ahead and also add the prisma syntax highlighting extension you don't really need to do that i just like having syntax highlighting on my different types of files so once you add those let's actually also go ahead and add the prisma library as a dev dependency and then after that's added so it's going to take a little bit here what we can actually do is go ahead and uh initialize it and the way to do that is npx prisma init so what this will do is it's gonna load environment variables from our env file so um if you followed along with the starter project you can see that we have this dot env and dot env example but we didn't have any database url or anything uh configured in that previously so after that's initialized what it does for us is that it creates this data source as our db it tells us that we're going to be using postgresql which i think is the default but we're also going to uh use this url from our environment variables and then we're also going to generate a client and we're going to be using the prismaclient.js so in order to get this to work let's go ahead and um add uh instead of my name which is just kind of what i was using we're going to add database url to the example so that we know that we need this defined whenever we're going to start our server and uh this adds some kind of stuff but what we're going to do is we're going to add this the database url and so what i'm actually going to do is i'm going to actually go ahead and change that outside of your guys's field of view just because i don't know if that password is used anywhere and if there's some hackers or some bad people in here so i'm going to go and change that and then okay so we change that environment variables are ready to go and then what we got to do is we don't need to run the initializer anymore we can actually just update our prisma file and by updated we don't really update these parts what we're actually updating is we're adding a book model and so uh think about this as a table and so like the model for a book is pretty much a book table what we're passing in is that it needs an id and so this id is going to be a an int and you this doesn't necessarily need to be id like this you can actually do book id um whatever you kind of whatever your naming scheme is so id uh and then also another thing with like uh databases i guess they say best practices for like column names is underscore or to use snake case as opposed to um uh camel case but i'm just gonna have a single word as id for now and we're just gonna specify it as an int it's id it's a default and it's gonna auto increment auto increment and then we're also passing in the title and the author so very similar to our types here so why are we defining these in two different ways right so before we had this prisma schema set up we had to rely on this type because our graphql resolvers and things like that they didn't know what else to actually map the graphql type output too so previously we're outputting our graphql type definition to this but now we can actually go ahead and get rid of this but after first thing we're going to do is we actually need to go ahead and run them quick migration and if you're not familiar with what migrations are pretty much migrations are just ways to um whatever changes you've made within your schema it kind of picks that up and it creates a new migration so that you can run it and change your uh uh your database so if i add a column i can run a migration and so then it'll add that column to every single reel that exists or to that database schema and so for now since i don't have any other migrations what it's doing for me is actually if we can go we can actually go ahead and look through here what you can see here is that it's creating the table for me so it noticed that there was no table so pretty much what it's doing is that it's creating a book table primary key is id with id title and author and it gives that text as serial text text so it just creates that for me it makes everything nice and then if i go back into uh my uh post sequel here and let me log in i bet i typed that in wrong i knew it um yeah so then if i use my book db and then if i first oops it's a connect is the actual syntax and then if i go ahead and list my data tables here you can see we have this book table as well as a migrations table for prisma so cool everything is set up as it should be and now we are ready to show another kind of cool thing with prisma this other cool thing with prisma is actually called the prisma studio and um if you're not familiar with this i'm going to show you guys really quick so if you run the command npx prisma studio it kind of creates this uh gui for you to kind of start interacting with your database and your data so for example here you could see before we have all of our models which is just book this is very similar to i don't know if you're familiar it's just another kind of like tool if you're uh kind of new to the postgres world you can actually have this kind of um pg admin that does something kind of similar right so it's just another gui that you can interact with your database so here you can see i can go here and i can see my book table as well as my migrations table but this is just kind of something so you don't necessarily need to use pg admin you can do directly um from your uh application so we're gonna come back to this though and so another thing i like to do before getting ahead of myself is that i actually like to add uh two more scripts to my package json for the first one being uh the prisma studio so i can just use yarn prisma studio to call it because sometimes i forget what the command is so let's just might as well just write it down so i don't so i can forget it pretty much and the other one is generate migration so anytime i do some changes instead of having to write this whole npx prisma migrate blah blah blah i can just run yarn generate migration and then it'll generate it for me and with those set up what we're going to do now is we're actually going to go ahead and create a prisma folder under source and also instantiate a client so what i want to do here is i'm going to kind of since i'm going to be using this prisma client in quite a bit of different places i'm going to just kind of put it in a single place where i can instead of always having to import it and create a new prisma client all i gotta do is just import uh this default prisma from it and it'll already do that for me so um let's do that and then what i'm also going to create is i'm going to create an interface for the iprismacontext and the reason that i'm putting it in within prism as opposed to like an interfaces folder is because this context is going to kind of just be related specifically to uh prisma just in case something else needs to be added but pretty much all we're saying is hey just expect this context to include prisma which is going to be of a prismaclient type and then similar to this prismacontext what we're actually going to then do is create this interfaces folder but we're actually since this is going to be more apt wide i'm going to go ahead and create this apollo server context within it and then i'm going to pretty much initialize the same thing um the reason that i didn't want to kind of combine the two into a single context is i'm not sure if any kind of issues would occur because i do plan on later on adding um jwt or access tokens or things like that to this context and i don't want that to necessarily mess with the prisma context so i like to keep them separate just because of that and you if you have experience with that and you kind of know that it wouldn't really mess anything up please let me know in the comment section below um but for now i'm just going to kind of just leave it since i don't really want to mess with it um so yeah what we can then do is we can actually go ahead and update our index.ts here and we're going to do a couple of different updates so the first one is we're going to import our new server context our prisma client and then we're also going to kind of change this instead of it just being a single run file we're actually just going to put it into a function called start server and then we're going to create our apollo server context we're going to include our prismaclient within it and then what we can do in our apollo server is we can actually just pass this context in and so then our apollo server will have access to this context so what this allows us to do kind of is if i were to go into our resolver um in a graphql generated nope that's not it and query blah blah blah within here what i could actually have is as if i mentioned earlier what you can have access to right is this context object so then with this context object i could do context.prisma and i would have access to it to then do like book and then uh go ahead and start doing things like that so we'll get back to that but for now what we need to do is yeah so that's pretty much it and then the change is that instead of just leaving it as the server is listening and running i'm pretty much just gonna start the server and we're good to go so um yeah so that's done and with that done updating what we're going to do is we now need to add our prismacontext to a couple of different places so the first place we're gonna add it to is uh to our actual code gen here oh let me paste that in here so what we added here is just these two to our configuration what we're doing is we're adding context type and we're just going to pass in this object prisma and prismaclient similar to our uh context here and then we're also going to uh pass in this used index signature to true and then the other thing we need to uh pass the context to is our actual code jen yaml and what we're going to pass it in here is we're going to pass it as similar to how we did it in the typescript file we're just passing in a configuration with the type being the iprismacontext as used as well as the use index signature to true so cool so we have everything set up now we're going to go ahead and create a data book service and the way that we do that is actually let's just create this book service and let me start closing some of these out and we're going to do that and it's going to be within our data folder we're going to have this new book service and within here what we're going to do is we're going to um create this get all books as i mentioned earlier i kind of like to split out my data fetching from my resolvers from things like that so i can easily test them in separate parts and pretty much so sometimes what you'll notice in different examples is that they'll actually per resolver they had to go before and prisma equals new prismaclient right and per resolver they have to kind of go ahead and do that um the suggested way to kind of create this prisma is to actually just do it one time within whenever you're gonna need to use it so for example if i'm gonna be using it a bunch within book service let's just create it one time and this is actually being created because as we saw earlier is uh when we import this pretty much what it's doing is just creating a new prisma client and so we want to be using the same instance of the prisma client throughout different types of requests and that's just kind of sometimes it's just kind of like the best practice for how to necessarily just work with different databases so let's go ahead and do that so let's import that and then we're going to do this get all books function is actually going to return the book type but if you've noticed this book type is no longer being imported from our data types right we're actually importing this book type from our prisma client so this book type as you can see is very similar it's pretty much id title and not author but it's actually being picked up from our uh schema so that's kind of like one of the cool things about using um prisma with cogent is that one of the kind of issues with cogen is that you have to manually keep defining these types right but if you think about it you're already going to be defining these types when you define your database uh if you're using a relational database so that's kind of a one way to keep the source of truth is gonna be your database you're gonna be able to import that from um your kind of your prisma file right from your client and so you can start using that as the types that you're returning so um here pretty much all we're doing is we're returning our prismaclient book so if you're not familiar with this this is just kind of how like orms kind of are structured so we're going to go prisma look for our book model and then within this book model it gives you a bunch of different functions like find many find one create update delete um so pretty much all we're doing for this specific function is we're going to just use find many this is just going to return all of the books that exist in our database and once that's done we're also going to update our actual uh query that we set up earlier right so previously this is still looking at the book from our source data type so we don't want that it's just returning an array of books that we kind of predefined we actually want to now communicate with our database so let's look into what's going on with this newly updated query so pretty much we're just importing our type so you can see here that we're actually importing book from our at prismaclient right and what we can actually do is uh let me first go ahead and change that in here and then what we can then do is since this type book isn't being used and we don't really expect to be using types let's just get rid of it we don't need it anymore we're using prisma as our typing um but yeah so then what we're doing is we're passing in this get all books query and similar to before our output type is expected to be a graphql list of graphql books if you're looking to graphql book it's just a graphql object with these fields bada bing bada boom cool and then as i mentioned earlier our resolve expects four arguments and you don't need to pass these in but if you want to for example only use context you do need to pass in the tube before it i just kind of like including them just because um here i just wanted to show an example that you can actually use context within this right so what i could then do is uh if this wasn't underscored i could then access instead of doing it like this as i mentioned earlier i could do book dot find many within here right but i do like to split it up into um kind of its own data layer and then everything like that so it's easier to test so i'm not going to really use any of these right now but i'll just include them um in this example just so you know that they're there but pretty much all we're doing is calling this resolver the resolver is going to just return our data function called get all books and once that's done first thing we're going to do is let's actually run our prisma studio call here to create this prisma studio and what we're going to do is we're going to go into our model and let's actually go ahead and create a couple of records so let's do book one and we're gonna do the author as your mom and then we're gonna also add another book and we're going to do it's uh your mom's boyfriend and then if we add those save your changes cool now they're actually within our database if we were to go into pc cool or pg admin things like that we'll actually be able to see that but now that that's actually within our database we're going to test to make sure that that's still working correctly so how do we do that let's start our graphql server and just in a development example and while we wait for that to kind of pick up let us go in here and now let's see if anything's changed so nothing's changed here scheme is still the same uh docs are still the same nothing's changed one thing also like i'll add that you can actually also uh access this type name here and as i mentioned earlier so now if we see here if i do that you can see that these are the books being returned are the actual books from within our database so now we have everything configured everything is connected awesome and this quick quick little thing this type name is what we defined in um gql book here as the name i usually like to keep it similar between uh my model and my database and whatever i'm kind of expecting um this is just because um once you start working with apollo client you'll notice that sometimes they'll have that type name and then it'll have that and you can kind of do some like caching and things like that with that so cool everything's set up prisma set up let's go ahead and create a mutation so what we're going to first need to do before we create that mutation is we're going to create another type definition and this type definition is going to be an input type so pretty much we're having this create book input because we're going to create a create book mutation so pretty much what this is telling us is that similar to our gql book we expect this to be an input object type as opposed to just an object type the name of it is going to be create book input just kind of a quick description of what it is and then what fields does this input expect so it expects title to be non-null and it's expected to be a string so it's not arbitrary integer would that know so this is just a book title and then uh author and this is just the book author cool so that's set up we have our type there um we're exporting it and as i mentioned before i like keeping my type definitions capitalized just so i know that it's a type so then what we're going to do is we're going to create our oh that's not it i don't know what happened there we're going to create our um create book uh we need to create the method right for our book service to actually create the book so similar to get all books we're going to export a create book this expects a title and an author and it's going to return a book that was created you can do this in a couple of ways you can do cons book is equal to await prisma.book. so it's just kind of whatever you want just for this tutorial i'm going to just return the creation and then we're going to pass in what data expects and then the data is going to be the title and the author which is what this book needs right so like if i don't include title uh you can see here that we're starting to get some errors saying like hey you're missing uh you're this type is not assignable to what uh the property that you're missing which is property of title is missing so um cool that's good to go uh and then once we have that set up we can actually go ahead and create the book mutation and we're going to put that within our resolvers here so create book mutation we're going to then go ahead and quickly just let's go over what this is what's happening here so similar to our get all books query we're expecting this as just an object right so we don't need to define any kind of uh anything crazy so pretty much and let me make sure that's prismaclient i'm not entirely sure why that keeps adding a dot but so let's look what's happening here so we have our create book mutation we're expecting the output type to be a gql book meaning that we expect this mutation to result in our graphql book which again blah blah blah it's a book it's a object and blah blah yeah so it's pretty much yes it's a graphql object that we're expecting to be returned with id title author um so and then we can actually pass in args so what is args args is going to be expected what are the arguments that this mutation is going to take and pretty much what i like to do is i like to keep it kind of a little bit separate so you could go ahead and in here um define uh these right so you could put these title and author it within our mutation here but i actually like to create different input types so what we're expecting here is that we're going to actually take an input and this type is going to be expected so it can't be null and it's going to take that create book input that i created this type definition which is just title and author so it goes through the whole uh kind of the tree of everything where it should be and then the description of it is just like book input to be created similar to before we have our resolve function here and so we have like source now that we're actually going to be using args we actually do need to define source but we're not going to use it so let's just uh have this underscore syntax but then what we're going to do is we expect args to be similar to here right so you can break it down if you want you don't have to i usually like to break it down a little bit so it just makes it a little bit easier to work with and i understand what's kind of going into uh this resolve function but pretty much what we're saying is like hey args we expect that we can destructure it to include input and input we can then expect to destructure it to title and author from our create book input type and um i haven't really figured out a way to actually be able to strongly type these args i keep getting a bunch of weird errors and it seems like it expects it to be kind of like a format like a json array of any type at least from kind of what i was noticing so just leave it as any for now i have to add this no explicit boundary eslint things just because i have a rule saying like don't explicitly or just like give me a warning if it if you're using um any type or something like that i'm not what happens when i remove this um yeah just give me uh it shouldn't be typed with a non-any type so if i just add that i just don't like the warning um context again we don't really need it you can kind of just remove it i just keep it in there just in case but pretty much what we're doing is we expect the book to be returned the book is going to be the schema prisma type and we're going to call our book service create book and return it cool bada bing awesome what we then need to do is similar to our query we actually need to create a mutation kind of wrapper holder of all of our different mutations that we expect and all we're going to pass in here is that since we only have one we want to create it as an object type similar to query it's an object type of name query so type query name mutation and pretty much what we're doing here is we're passing in create book so in other type of definitions you would see kind of like type mutation and then here you would have create book and then uh just kind of like like that kind of syntax but so that's pretty much what this is doing but here we're passing in create book and what it's going to expect is this create book mutation and which is just this entire thing and so this is kind of where i get back to like why i like this structure because like i would then have to kind of go in and define this entire thing all within a single resolver's file and then like it gets really long and then you're searching and like i don't i don't like that i like to just i do it it does suck to have to click around on different folders but in the end it just kind of looks a little bit better once you're which you know what you're working with and once we have that mutation we have to update our schema to actually import mutation and similar with query it expects a mutation field since it's actually named the same we can just pass it in like that cool and now instead of not test we can let's test to make sure that create book mutation actually works and after that started we can go here and now if we look at our docs we can see now that we actually have these mutations here with create book and what does it expect it creates expects our input of create book input which is of type create book input title string which are both expected it returns a book um so really cool these docs features are awesome that's why the playground is really cool to work with with graphql and then also if we look at our schema here we can actually see now that we have this type mutation that was generated as well as our create book input and if we go back to the code if we look out at our generated schema you can see here that this was generated because we started the server with this new schema right so that's generated awesome things like that are working so again if i were to run this it works but then if i were to do create book takes a little bit of a different type of syntax so i'm going to go ahead and create that and show you guys how this works so then what we're going to do is create book and it's expected input of type input and what we're going to expect back right we're going to expect an id a title an author i'll include type name and then if you're not familiar with the playground down here you can actually create different kinds of query variables and so what we're passing in is input so this dollar input here is this input here so it's expecting this and then what we're going to do is pass in a title um hold on title as uh book three and then author is me myself and i and once that's created what this is going to do is it's going to take this input pass it into this mutation this mutation is then going to pass this input variable to here which is what our mutation expects and then it's going to return the book that we create field so you can see here that it's working awesome and we didn't need to pass in an id to our query parameters because it's auto-incremented and since we had two books before you can see now that this is the third book that is being inserted so you can see that that's working correctly and then we can also see uh book three me myself and i and then if i go back to our books uh query there it is our third book that is being returned and everything is working beautifully so we can then also close this and then since we have this new schema that was generated we can actually use yarn generate to create a new graphql uh typescript file and with that that's pretty much the entire microservice setup the last thing i want to show you guys is uh let's set up uh some unit testing and testing graphql can be a little bit tricky and like i've said a couple times in this video that i like to break up my uh testing to a couple of different things like i like to have this data layer where i can uh manually test these functions and unit tests and things like that resolvers gets a little bit trickier because uh you then need to kind of do this like integration testing or where you create a um a test database to kind of run your resolvers against and because you can't really necessarily unit test the resolver because it's like it's object type and it's like it gets a little weird confusing um i'm actually going to make another video on how to add integration testing using docker and like you can spin up uh docker compose postgres um kind of like a quick container then you can run your unit or your integration test against it make sure everything works and then uh tear that container down um so look out for that video coming out soon so yeah let's actually go ahead and first thing we're going to do is we're going to add this just mock extended just because we're going to create a uh singleton mock for our prisma client so that it just makes it easier for us to uh anytime we kind of have a prismacall we can actually mock out what it returns without necessarily actually having to talk to a database or things like that so how do we do that so as you saw before we kind of deleted our test folder but this is kind of the structure that i like to go with graphql microservices is that i will actually create a test folder and then for our mock is going to lie in here and so then within mocks we're going to go ahead and create this prisma singleton and there's a you can do dependency injection if you want i'm going to use singleton just because it's a little bit easier uh at least in my eyes um so what are we doing here so prisma singleton we're creating these uh mock deep right so we're going to go pretty much what mock deep lets us do is it lets us go into this prisma client and mock everything under it so like it just makes it pretty much what it says mock deep so it's like not a shallow mock you go go through and that way we have access to like the create to find many the tables the returns like it's really really cool and just mocking part of unit testing is always the most annoying part so this is just kind of like i'm just trying to help you guys set that up because unit tests are really important um so what are we doing here just.mock passed in our prisma client that we're kind of bringing in similar to our our own not just a new prisma client um default it so we're going to mock deep to the actual prismaclient type and then um since we're going to be using our prismaclient everywhere else we're just mocking it to this type but we're going to be able to mock it that way and so then what we're doing is we're defining this prisma mock kind of uh um function and then uh not necessarily a function but just kind of like our our prism lock and then before each of them before each test that is run we're actually going to reset that mock so we don't have to do it before each um go ahead and reset everything like if we were to resolve the value or implement the value as like to return this or to run it like this so this way it just kind of makes it easier so we can skip a step of having to reset that mock and then we're going to export default prisma mock and then in order to get that to work we actually also need to update our just.config.js to add a couple of things so the first thing we need to do is we need to set up files after environment so uh we actually need to run this singleton uh anytime after the environment is uh it's just a setup file and then we're gonna also add uh our mocks to our ignore patterns because we don't really need to test our mocks i guess we can if you want i just added that because i don't really want it i didn't really want to deal with testing our mocks right now um i i guess that doesn't make sense i don't know why you would want to test a mock um but yeah so cool so with that installed we can then let's just write a quick unit test to make sure that everything is working so what we're doing is we're going to go ahead and similar to i don't like this this kind of annoys me but um since i'm already having this kind of mocks i'm just gonna use this test folder you can go ahead and write bookservice.test.cs within here it would work the same um for the sake of this video i'm just going to use it like this separate it out so let's create our book service test and then within this book service test we're going to uh go ahead and we're going to describe so actually i want to add an extra describe here as um book service test and then let's actually just put this describe within it so then we can have different describes so we can also just like kind of keep it like that you can split it up um i'm just going to add a single test you can go ahead and change it around if you want so what are we doing here we're describing our book service test which then we're going to describe all of our create book tests and we're going to write a single test here and what is this test doing it should create a book with the past in arguments and what we're doing here is we're going to create a mock book so this is the book that we kind of expect to be returned um so we're just passing it in as id1 book title author super guy and then here kind of comes the cool part of that prisma mock that we're using is that we can actually go and do prisma mock book create and we can mock a resolved value so what is the value that whenever we call create what should that return and without actually going out and creating something so we just pass in our mock book it should just return this and with that we can actually go ahead and test it by calling the create book method from our book service get this created book and then we just add some assertions here so we expect it to actually be the same book so meaning that the id title and author should be the same we also expect the create call from prisma to be called once and we also expect that call to be called with this data object so and then let's make sure that i wrote that test correctly or that like it's actually working as expected so if we go ahead and run yarn test you can see that our book service test got picked up and it should give us the pass so yep everything's working correctly you have uh unit testing ready to go and that's it and one last thing kind of to kind of talk about is um if you've noticed this kind of this graphql generation file it comes up with a bunch of different um like query types and like different kind of like resolvers and things like that one thing that i've noticed is that i haven't necessarily i've kept trying a bunch but i haven't necessarily been able to kind of like pass in a a type here so for example like i haven't been able to kind of like let's say i want this to so like before some of the things that when i used to keep it all in one file i could actually pass in the resolver's type like this from um kind of like our generated and then it would kind of give me this nice auto completion of like how like for example like this book uh expects these types of arguments and things like that but i haven't been able to kind of keep it like that within the structure and the reason for that is because when i do pass in resolvers um this kind of create book starts giving me like property type is uh is missing in this resolver's eye prisma context but required in this and so i haven't necessarily been able to really use that to its full potential what's happening here oh oh yeah so then let's go back let's go back let's just remove that and so yeah um yeah so that's all there really is to setting up a microservice with graphql cogen um apollo server as well as typescript being able to use kind of the built-in types from graphql as well as kind of playing around with the prisma studio using prisma as our source of truth for typing as well as playing around with our uh pretty cool uh christmas uh not prisma studio um the apollo playground that they provide you and kind of just understanding a little bit more the mutations creating the schema being able to auto generate some things doing that via code so that's all there is to this tutorial and um i hope you guys enjoyed it if i missed anything please let me know if you guys have any suggestions or kind of like or any ways that maybe this uh infrastructure could be improved i'm always looking to kind of find new ways to do different things but i found that this is kind of the overall structure and flow that at least to me helps me understand what really is going on with uh my graphql resolvers and type definitions without any kind of like black box magic that i don't really know what's happening in the background right so like i really like enjoying knowing what's actually happening and what's defined and what am i like i i like that control as opposed to like kind of some black box stuff like maybe some like how some code for solutions do it so um yeah so i hope you guys enjoyed the video if you guys did please leave a like as well as comment subscribe and um look out for the next couple of videos thanks [Music] you
Info
Channel: Leo Roese
Views: 2,327
Rating: 5 out of 5
Keywords: graphql, apollo, apollo server, graphql.js, typescript, prisma, prisma.js, postgresql, microservice, javascript, codegen, graphql codegen, graphql codegenerator, type-graphql, graphql nexus, postgres
Id: K8k0fXnDAnA
Channel Id: undefined
Length: 62min 1sec (3721 seconds)
Published: Fri Jun 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.