In this workshop, you will get a first-hand look at what end-to-end type safety is and why it is important. To accomplish this, you’ll be building a GraphQL API using modern, relevant tools which will be consumed by a React client.
Prerequisites:
- Node.js installed on your machine (12.2.X / 14.X)
- It is recommended (but not required) to use VS Code for the practical tasks
- An IDE installed (VSCode recommended)
- (Good to have)*A basic understanding of Node.js, React, and TypeScript
This workshop has been presented at React Advanced Conference 2022, check out the latest edition of this React Conference.
FAQ
GraphQL Codegen is a tool that analyzes GraphQL queries and generates corresponding TypeScript types and query objects. It ensures type safety by validating queries against the GraphQL schema, ensuring that the queries are correct and the expected data types are returned. This bridges the gap between frontend and backend, ensuring consistency and reducing errors.
To configure GraphQL Codegen, create a 'codegen.yml' file in your project's root directory. Specify the GraphQL API's schema URL, the directory of GraphQL documents, and the output location for generated files. Include plugins like 'typescript' and 'typescript-operations' to generate TypeScript definitions and operations.
The workshop utilizes Prisma for database management, GraphQL for API queries, and GraphQL Codegen to generate TypeScript types from GraphQL queries. Together, these tools ensure that types are consistent across the database, server, and client, enhancing reliability and developer experience.
The useQuery hook from URQL is used to execute GraphQL queries in a React application. It requires a GraphQL query and returns an object with properties like 'data' and 'error' to manage the query's results and state. It leverages the React context provided by the URQL Provider to access the GraphQL client configuration.
Prisma provides a powerful ORM for handling database operations with support for strong typing and model relations. It simplifies data access and manipulation, integrates seamlessly with GraphQL, and automatically handles migrations. Prisma enhances developer productivity and application reliability by ensuring type safety and simplifying database workflows.
The workshop materials, including the Notion document and relevant code, are openly available for reuse. You can use these resources to host your own workshop, teach others, or integrate the knowledge into your projects. The materials are designed to be accessible and adaptable for various educational and development purposes.
This Workshop is about building a full-stack application with end-to-end type safety. It covers setting up a SQLite database, creating a GraphQL server, and connecting the client. The focus is on using Prisma, Pathos, and GraphQL Codegen to ensure type safety and improve the developer experience. The Workshop provides step-by-step instructions and resources for participants to follow along and ask questions. It emphasizes the importance of type safety in both the backend and frontend development process.
Thank you for joining me for this full-stack application workshop. We'll be building an application with end-to-end type safety. I'll provide you with all the information and resources you need. Make sure you have Node.js installed and consider using VS Code as your IDE. Basic knowledge of Node.js, React, and TypeScript is recommended. Today's agenda includes building a SQLite database, setting up a GraphQL server, and connecting the client. There will be a repository available. Let's get started!
♪ Thank you guys for joining me here. I see that there's a couple participants and people are still filtering in. So that's super cool to see. I'm glad you could all join me this morning. It's morning here. I'm in California. But I know this is a remote global conference, so you never know where people are from. So good morning, good afternoon, good night to everybody. I hope you're ready to jump into some coding.
We're going to be looking at a full-stack application, and we're going to be building, basically, an application that has full end-to-end type safety, and we're going to do it from the ground up and just sort of talk about all the different pieces that you need to worry about and that you need to think about when you're implementing end-to-end type safety, and we're going to look at how a couple of these tools make it pretty easy for us to do and make it actually quite a joy to do. Once you set up an application like this, if you're anything like me, you're going to end up nerding out about it afterwards and just have a whole lot of fun setting all of this stuff up.
I see there's a question already, what's the link to the Discord server? The one on the email doesn't work. Let me see if I can get you a link there as more people are filtering in. Let's see. Not like people. There is the link there. I've posted it. So if anyone else needs the Discord link, I have posted it in the answered questions, so it should be there. But, OK, moving on a little bit more on the agenda for today, I'm going to post a link in the chat, and this is just going to be a link to a Notion document. And what this is going to be is sort of the source of truth for this whole workshop that we've got going here. And this is going to be where we have all the information about what we're going to be doing. And I've actually split up this workshop into different lessons, so we'll take them one at a time. We'll start off with this databases section and we'll slowly move on to the bottom. But just some information about this, about the workshop. Here's the resources up here. This is the link just to this notion doc that I just posted. If you have any issues opening it, please let me know in the chat or in the questions and I can help you out with that. And this is going to be the GitHub repository that we're going to be working with. So we can, if you pop that open, just be able to get that ready once we get going. You can't see my screen. That's probably an issue. Let's see. How's that? Is that better? I see chats disabled for some people, let's see. Here's the link to that Notion documentation and themes for how you can get around your GitHub repository. Here's the link to that Notion document in the public channel. Cool, great. I see that people are chatting on the Discord about the screen. So, that's perfect. You can all see me now. Yeah, I'll send the link there. We could just default to the Discord chat if that other chat isn't working. There you go. Sweet. Looks like people are thumbs upping that, so the link's working. So, I'll just continue to talk through this first page as people get that open and get going.
There are some prerequisites to this. The prerequisites are just that you need to have Node.js installed on your machine. Everything that we're going to be doing is going to use Node. So, we'll just want to make sure we have that installed. And I'll give you time to install that if you don't have it already as we move forward. And then, it is recommended that you use VS Code for this. There are a couple plugins that just make life a little bit easier with working with Prisma, specifically. So, VS Code is sort of my suggested IDE. But if you have another one that you like to use, that you prefer, feel free to go ahead and use that. And then, just the basic understanding of Node.js, React, and TypeScript is good to have. It's not necessary, because I will be walking you through everything, but it would be good to know at least a little bit and have some experience with Node.js, React, and TypeScript.
And then, moving on through this What Will You Do section, if you want to read through this in more detail, feel free to take a minute just to read it. But essentially, this just goes over sort of the agenda we have for today. This goes over all of the different pieces of the workshop that we're going to do, so it sort of breaks it down into little descriptions. So for example, we're going to be starting off by building out our SQLite database. And then we're going to be setting up GraphQL Yoga, which is a GraphQL server, and then building out that server, and then finally building out the client and connecting all of those different pieces. I see more hands being raised, so let me see what's going on here. So I see a question, will there be a repo after? Yes, there will be a repo. And then it looks like chat's disabled for people. Yeah, so we'll just default to the Discord chat. Where's the link exactly? Okay. So for everybody looking for the link, I'm going to go into the chat again. Or actually, I think the... Let's see. Let's see.
2. Workshop Introduction and First Lesson
Short description:
We'll get everybody in the Discord chat. We'll set up Prisma and the GraphQL server. We'll build the React client and bridge the type gap between the backend and the front-end. I'll walk you through the different lessons and provide tips and tricks. After each lesson, you'll have time to work on your own and ask questions via chat. I'm Steven Adams, a Developer Advocate at Prisma. Feel free to contact me in any of the provided formats. You can also host this workshop yourself using the provided link. Let's jump into the first lesson, where we'll set up the database using Prisma to manage the schema and interact with it in a type safe manner.
We'll get everybody in the Discord chat. I posted a link to the Discord in our chat. Join that and then go to the RADV workshops channel, and then if you go into the channel for this workshop, you should see the link to The Notion. You'll see the link to The Notion. I'll give just a couple minutes for people to get into there. I clicked the link. It looks like it's working there, that's good. Okay, cool.
So then down here to the actual timeline agenda. So we are just gonna be kicking this off like we're doing right now. And then we're going to be setting up Prisma. We're gonna set up the GraphQL server after that. We're gonna actually build out the GraphQL resolvers and all the different pieces there. And then we're going to build the React client and sort of bridge the type gap between the backend and the front-end. And then I am gonna be splitting up these lessons into two different parts. There's gonna be the first part where I'm just going to walk you through everything here. So I'll be walking you through the different lessons. I'll walk through each little step that we're gonna do, and I'll try to explain the process as we go. And for this part, I encourage you not to code along with me. I encourage you to just sort of listen and sort of take in the information I'm giving you as I will have some little tips and tricks in there as I go. And then afterwards, I'll provide you about 20 minutes after each little lesson to work through all the pieces on your own and then get some feedback from me via the chat if you have any questions. So that's gonna be the time where I encourage you to do your coding then. That way you get the information and you get some time to code afterwards. It's sort of a win-win situation.
And then before I get started, just a little bit about me. My name is Steven Adams. I'm a Developer Advocate at Prisma. And I've spent the last roughly eight years working as a Full-Stack Developer. And I've just recently sort of jumped into Developer Advocacy. And it's been awesome because I get to do cool workshops like this and get to hang out with people like you every day and just teach you how to code and do different random coding things. And it's been great. I'm loving it. And I'm super excited to be giving this workshop. I have all of my contact information here. So if you wanna message me about anything, after this conference or just after maybe an individual section and you need to reach out to me, feel free to reach out to me in any of these formats here and I will try to respond as soon as possible. And then lastly, if you wanna host this workshop yourself, this document, I've provided the link that way everyone can just sort of have access to it. This is something that I'll just be keeping up. And if you ever wanna just steal this and give this workshop, maybe at a local meetup or if you are just interested in maybe getting into some speaking and you wanna use what you learned today to sort of teach your friends, feel free to steal this workshop from me. I definitely believe that all of these resources should be open and available for everybody. So this isn't mine explicitly, this is for everybody to use if you would like, so please feel free to do so. So with that, all out of the way, I think we'll go ahead and jump into the first lesson. But before we do, I'm just gonna double-check some chats and make sure everything is looking good here. So I see a question from Ben about joining the Discord. In case you haven't already, I posted a link in the live chat. So that should be good. A link to the repository that's gonna be in the Notion document, and that's all the way up at the top in the resources section. And the link to the Notion document is in Discord. So I'll actually go in there and pin it. I see Alex added a good link that looks like it's working for people. So yeah, go ahead and get it there. Some of you can't join Discord. Let's see. I will post the good link to this chat and go ahead and try to access that one. So that should be in the chat now, Ben. And while you're doing that, I'll go ahead and just start talking through the very first lesson. So jumping in here, the way this is gonna work is we're just gonna go over the different goals. We're gonna go over the setup for this project. And then we're gonna go through each of the tasks. And once I get through all of these tasks, I will give you some time to do this on your own. So come on over here. I'm gonna bump the size up for you guys a little bit. And right up at the top. So the goal of this first lesson is just to set up your database. We're gonna be modeling out our schema. We're going to be seeding our database, which will be a SQLite database. And we're setting it up using Prisma. So that way we can actually manage our database schema and our applications database client all through one tool, and that is Prisma. So by the end of this, what you're gonna have is a backend. We're gonna start off our backend servers project. We're gonna know the shape of our database and how to interact with it in a type safe manner, because Prisma will give you a nice type safe client to do all this with.
3. Cloning and Initializing the Project
Short description:
To start, clone the starter project from GitHub and open it in a terminal. The project contains initial setup for both the client and server folders. Install the dependencies in the server folder using npm install. Next, install Prisma as a development dependency using mpx Prisma init. Initialize Prisma with the data source provider flag to specify a SQLite database. The Prisma folder will be created, containing the schema.prisma file and the database configuration. Now, we can move on to creating our user models in the Prisma schema.
So the very first thing we're gonna do is we're going to clone the starter project from GitHub. So there's gonna be a link here for you guys. Once we get to that point, I will copy that over and I'll pop open a terminal. And in this terminal, I'm just going to be on my desktop and I will run git clone and I'll paste this here. So this is just going to clone down the repository. This has some initial setup for us and it should just have a couple of different things and I'll go through what it's got in the beginning.
So within here, you're going to see a couple of different folders, you're going to see a client folder and this is gonna have our React application. This is just sort of the start of it. We're going to add to this later, but this is just a starting point that I have put together for you guys. And then the same thing with the server. All we have right now is a package.json with a couple of packages already installed to get TypeScript going and the TypeScript config. And that's about it for now, we're gonna be adding everything else on our own.
Let me move this chat over so that I can move some things. Cool. All right, so back in the Notion document, so we've got this and so we've cloned it and then we can follow the instructions on the readme just to get all the packages installed and whatnot. So I'll go through that in just a second, but important, we need to make sure that we have part one checked out. This is a branch that I put together and every different lesson in this workshop is gonna have a branch associated with it as a starting point. So for example, we're in the first lesson, so we're gonna check out part one, that'll give us the starting point. If you get stuck in any of these lessons, so for example, if you get stuck in part one and you have a problem with it, then you can just check out the part two branch if I move on and maybe you haven't finished the previous section and it'll get you to the starting point that you need to be at to start that next section. If you have any questions about that or any concerns, feel free to shoot a message if you do get stuck and you don't remember how to get there, but that's generally how you're gonna do it. You can check out the part branches for the specific sections. And for this specific section of the workshop, we will be working in the server directory for the entirety of the lesson, so we're just gonna be working on the server code.
So let's go ahead and go here to the read me and I'll pop this open as a preview so that we can see it a little bit better. So we've gone to the repository. Now we just need to install dependencies in both of the projects and then we can start the servers. I won't actually start the servers right now because there's no need for it, but I will go into the server and I will npm install. So that just installs all the dependencies that we already have to use TypeScript. And those will be found here. And then we can move on to the first task. So these set of tasks are going to define what we do in this section. So the very first thing that we're going to do is install Prisma as a development dependency. We're going to use Prisma to scaffold out the database as I mentioned before. And we need to have this CLI tool installed to actually initialize Prisma in our project. And you're going to see the solution that you might use for one of these tasks. And so, Prisma is going to actually install Prisma as a development dependency. So you're going to need to install Prisma to begin with, or in the case of a run元 if you're going to run this internally without actual user or provider, you need to actually have the pick and choose to say, Hey, I'm going to use Prisma as a network dependency. So you're going to need to pick that, find yourkeep and put it into your cache. So let's say I have I have just one target and it wants to be full. Okay. And then we're going to do mpx Prisma init, and then we can use this data source provider flag to tell it that we want a SQLite database. And there we go. So we're going to see that our Prisma was initialized. We're going to see that Prisma schema was created for us. It's going to have a little starter schema, and then it gives us some next steps, which we don't really need to follow here for this piece. I'm going to move this over to the right. So it's a little readable, but let's go look at what it gave us. So we're going to see that we now have this Prisma folder and it has the schema.prisma filing it. And this file is just going to have a generator defined for us, and this is what's going to define how to generate Prisma Client, which is how we're going to access our database. Once we run Prisma Generate, it's going to use our database model to generate that client for us. We also have our database, actually our data source block here. And this is how we define how to connect to our database and what kind of database we're connecting to. So we could see here that we are connecting to a SQLite database and it's going to look in an environment variable called database url to find our connection stream. And Prisma noticed that we didn't have a.env file already, so it created one and it went ahead and created a default database file for us. And that's what we're going to use. This is a database file that SQLite uses to store its data. So, going back to the steps here, it looks like we've got that first step done. And if you do want any more help with the init commands or just any commands in general, I do have docs linked throughout this document, so feel free to look at those. So that's the first task we've done that, and now we can move on to creating our user models. So our database is going to have a user table and this user table is going to have a couple of different things. We want it to have an ID and a name. And so the way we define that in our Prisma schema is we have to set up a model block. So I'll go ahead and pop open the schema.prisma here. And we're going to create a new model and it's going to be called, user. And what we want this to have is the ID, like we've said. So I'll do an integer for the ID. And we're going to say that this is the primary key of our model. So I use this at ID. And then we want to give this a default value of auto increment, that way, every time a new record is created, it will just increment one onto that. So the first record will have an ID of one, the next one will have two and so on and so forth. And then the next piece that we needed was a name, I believe.
4. Adding Name Field and Creating Note Model
Short description:
We've added a name field to the user model and created the note model with message, createAt, updatedAt, and userID fields. We've established a one-to-many relationship between the user and note models using the relation attribute. Now, we need to create a migration to apply the changes to the database using the migrate dev command.
So a name. So we're going to add a name field here and we'll make that name a stream. So now we've got a user model that has an ID and a name. And currently we don't actually have a generated database client to actually work with this data or store it in our database, and actually this model isn't even in our database yet. We've simply defined it in our Prism schema and we can go from there.
But before we do that, what we need next is to actually create the note model. So what we're gonna have is a user and a note in our database, and the way this is gonna work by the end of this is that a user will have many notes associated with it, and they can go ahead and create a note on the React client that we add. So let's go ahead and add a note model as well, and that note model is gonna need to have all of these fields. So I'm gonna go back into here. I'll create a model called note. And then I'll copy over this ID because it's gonna be the exact same ID for this. And then we're gonna need a message field. So I'll create a message. This will be a string. And then create a dat and update a dat. So when a record is created, we wanna take a timestamp of when the record was created. And then whenever the record gets updated, we also want an updated timestamp. So let's go ahead and do the create a dat, this will be a date time field, and we're gonna set a default value to the function now. So this is just gonna say, by default, when a record is created, stick a current timestamp into it. And then similarly, we'll do updated at. This will be a date time as well. And we have an attribute in Prisma called updated at that will keep this updated to a current timestamp anytime the record is updated or changed. So that's helpful there. We've got that ready to go. And then finally, we need to have a user ID. And what this user ID is gonna do is this is gonna be the foreign key that points to a user so that we know which user this note actually belongs to. So we're gonna say that this is a user ID and it's an integer. Cool, so that finishes up our note model for now.
The last thing that we really need to do is create a one-to-many relationship between the user and the note. That way we can have a user that has many notes associated with it. So in order to do that, we need to actually use the relation attribute and set up the relations in our schema. So let's go ahead and do that here. And I'll just take a quick second to look at the chat and it looks like there is no current questions so I will move on, cool. So for the note, let's go ahead and set the relationship up on the note side. So the relationship is gonna be related to a user. So I'm gonna add a field called user. And then the user field will be of the type user, which we defined up here in our model. And then we can use the relation attribute to actually set up this relationship. And this is gonna take two different values. It's gonna take fields, which will be an array, and this is gonna be user ID. This is specifying which fields on the current model are gonna point to the relation model. And then next, we need to do references. So this is gonna be what a column on the relationship model are foreign key references. So we want to reference the ID. And then this is gonna say that there's an issue because our relation hasn't been defined on the opposite side. Within Prisma, you do need to define relationships on both sides, so within both models, that way, when you're using Prisma Client, you know how to get from a user to a note and you know how to get from a note to a user. So in order to do that, we simply need to do notes. So I'm creating a field called Notes and it's gonna be note and it's an array. So that sets up our relationship. We now have a user that has many notes and each note has a single user and knows how to get to its user. I'm looking over at the chat. Let's see, NPX, Prisma, Ennit, data source provider, SQLite. Okay, so Alex, I see your question there and the problem is that before data source provider, you need two dashes. So I'll actually post the fixed snippet here. There you go. So that should do it for you, but moving on. So we've got that relationship set up now and I'll pop open our tasks again. Now that we have this done, we need to actually create a migration. So this is currently only in our database schema. This isn't actually in our database. It's in our Prisma schema, sorry. So we need to actually apply this to our database using a migration. So to do that, we can actually use the migrate dev command and this command is going to create a new migration file and then apply it to your database. And this migrate dev command is specifically the command that you're going to use while you're on development, because it will just simply apply the whole migration to your database. So I'm going to go ahead and go back to our terminal. If I pop this terminal open and I can type in npx Prisma migrate dev. And then we want to give this migration a name, so dash dash name. And then I'll just call it init. This is our initial migration. Oh, this you hear is that I'm not in the server folder.
5. Adding Data to the Database
Short description:
After applying the schema to our database, we need to add data. To do this, we create a seed.ts file and run the command to add new data. Once the command is executed, our database is fully seeded and ready to work with. Now, it's time for you to work through the tasks on your own. If you have any questions, please post them in the Q&A or on Discord. I'll be available to answer them. Take your time, and I'll be back in 20 minutes to continue.
So CVs server and then I'll run that migration. Cool. So this went to our Prisma schema, it loaded it up, it created a set of migrations for us and then it actually applied it to our database. If you want to actually look at the migration that got created, you can go into migrations, you can pop open the SQL file. And this is what actually got generated and applied to our database.
So now that we've got that done, we now have a database with our schema applied to it. Now it simply needs some data. So in order to actually get some data into it, we're going to see the database and to do so we need to add a new file called seed.ts and this file is just gonna contain this code that I provided here to throw in some data for us. So I'll copy this over. If I can get the copy to work. Let's see. Oops. There we go. Copy, cool. And then head over into our code again. And then within this prisma folder, I'm gonna create a file called seed.ts. This is just gonna be the file that we run to add all of our data in. You don't need to know too much about what this is actually doing, although it is pretty easy to follow. This is just deleting all the records out of our database and then adding new data in. And to actually run this, we're gonna go into our package.json and we're gonna add a key in here called prisma. And this will be an object that has a value in it called seed. And what this is gonna do is this gonna run ts node dev prisma seed.ts. And the reason I set it up this way is because we can now go into our terminal and I'm gonna move this to the bottom and we can run npx prisma db seed, and that'll actually run this command. So it's gonna go into here and it's gonna run this seed command. So if we run this, let's see, configure seed in your project need to add prisma.seed. I thought I did that. Oh, again, I'm not in the server directory. There we go. So now it's running that seed command. We can see here that it actually ran our seed.ts file and that our data has been seeded and that the command was executed. So at this point, we have a database now that's fully seeded with data and ready to work with.
And so at this point, this is where I'm gonna turn it over to you guys. So I've walked through all these steps and sort of walked through how these are working. At this point, I'm gonna let you have a couple of minutes to work through this on your own. So go to this lesson one page and just follow through all the tasks. And what I'm gonna actually do is I'm gonna turn off my audio for a bit and I'll just be quiet and I'll let you work through this. And if you have any questions at all, please post them in the Q&A or post them on Discord. I'll be monitoring both and I will answer them as soon as I can. And I'm gonna give you about 20 minutes to do this and then I will be back for you guys to follow along. So at this point I'll turn it over to you guys.
6. Setting up GraphQL Server with Prisma and Pathos
Short description:
In this section, we'll set up our GraphQL Server to expose the data in our database. We'll use Prisma and Pathos to build the GraphQL schema and set up the server. The first task is to set up the Prisma client by creating a new file and instantiating the client. We'll also set up Pathos, a generator that helps build the GraphQL schema using code. We'll install the necessary packages and add a new generator in the Prisma schema. This generator will use the Prisma Pathos types to generate types for the Pathos setup.
All right, so that's time on that. So I hope you guys all had fun working through that first piece. And if you did have any problems with it, don't worry. I'll be walking through how to get caught up in just a second.
So let's go ahead and look at this second section. So if you go back to the homepage, we're gonna be on this backend set up a GraphQL Server section. And the goal of this lesson is to just set up our GraphQL Server. This is what's gonna expose our data that we set up in our database. That way we can actually query it from our React application. GraphQL is the strongly typed language and it's going to allow the frontend to make requests for exactly the data that it needs in a way that's type-safe and self-documented using the GraphQL schema that we generate.
So in order to set up this, if you did miss the first section, this is gonna be our GitHub repository here. This is the link. And we're gonna be working in the Server Directory. And if you are just now starting, you can check out the part two branch. This is the starting point for this section. Otherwise, just keep working on the project that you started in the previous one.
So basically what we're doing in this piece here is we're gonna set up Prisma in our application. We're gonna set up some of the initial tools to set up the type safety that we're gonna use in this application. And one of those tools is gonna be called Pathos. And we're gonna use that to actually build out our GraphQL schema, and set up our GraphQL server with GraphQL Yoga. So let's go ahead and get started on that.
The first step that I have here is task one. What we need to do is actually set up an instance of our Prisma client that is built in a way that's reusable and shareable throughout the application. So I'm gonna pop open the codebase here. I'll get rid of this. And within our server directory, I'm just going to create a new file and I'm going to call it dv.ts. This is where we're gonna hold our Prisma client. We're going to instantiate it and we're gonna export it from here. So I'll import Prisma client. And this is gonna be from Prisma slash client. This is where Prisma client gets generated when you actually run a migration. What the migration did was it applied our database schema to our database, but as a side effect, it also generated Prisma for us without us having to run the command. So what that does is it takes our Prisma schema and it generates this client here, which is called Prisma client JS. And that's what we're going to use to query our database. So I've imported Prisma client. Next step is gonna be to actually instantiate it. So I'm gonna say, export const Prisma equals new Prisma client. I'm exporting it because we do want to export this and use it in different places in our application. And then after that, just as a little helper, I'm also going to export the Prisma client type because we will need it a little bit further down the line. So I'll export just Prisma client. There we go, so that should be all we needed to do for that first task. We just instantiated and exported Prisma client, that way it's its own module. And if you actually look at the solution here, it should be similar to what we have. And now getting into more of the meat and bones of our GraphQL server. In this next task, we're gonna set up Pathos, which is a generator that we're gonna use with Prisma schema. There's a nice plugin. And if you aren't familiar with Pathos, and actually, I think even before explaining Pathos, I would like to go into what a GraphQL schema is and sort of how they can be generated. So generally, there's two different ways that you can build your GraphQL schema. You can either actually write it yourself, you can manually write the GraphQL schema language and build out your different types and different queries and mutations and all of that, and then apply that to your GraphQL server. And the GraphQL server will parse that and set up the different types and stuff in the playground for you. But there's another way called code first GraphQL schemas, which is where you actually use a generator libraries similar to Pathos. And what this is gonna do is, we're gonna be able to actually build our GraphQL schema via our code. So it has a set of helper functions that help us sort of build our GraphQL schema up in the code. And what the generated asset of that is, is an actual GraphQL schema that we can then apply to our GraphQL server. Now, if that doesn't make sense, that's totally fine. We're gonna be walking through all the steps and I'll be explaining it as we go. But just know that we're gonna take this code first approach using Pathos, where we're actually gonna build our schema via the code. And we're gonna use a nice plugin with Pathos called Pathos Prisma that actually builds... It adds some type safety into how we actually build our GraphQL resolvers and types based on our Prisma schema. So we get that nice safety even within our backend server. So let's get this started. I'm gonna take this command here and I'm just gonna copy it so I don't mess up on the typing. What this is gonna do is it's gonna install Pathos in our application and it's gonna install the Prisma plugin which we will be using. So I'm gonna go into here, pop open a terminal and I will cd into the server part and paste this. So I'm just installing those packages there. And what this is gonna do specifically with the plugin Prisma package is this is gonna give us access to another custom generator that we can use in our Prisma schema that actually generates some types that will be used in the Pathos setup to actually build out these GraphQL types and mutations and queries and whatnot. So in order to actually make use of that, let's go over to our Prisma schema and we can go into our generator box and I'm gonna add a new generator and I'm just gonna call this Pathos. And this generator is simply going to have a provider and the provider is gonna be called Prisma Pathos and we're gonna call this generator and it's gonna parse this Prisma Pathos types. So this Prisma Pathos types, this is what we just installed with that Pathos plugin. This is the generator that's gonna get used.
7. Generating Prisma Client and Pathos Types
Short description:
When we parse the Prisma schema and run Prisma generate, it applies the parsed syntax AST to build types. Running mpx Prisma generate regenerates the Prisma client and generates Pathos types for the first time. The success messages indicate that the client and pathos integration were generated correctly, providing type safety for our GraphQL schema.
And essentially what that means is when we parse this Prisma schema, when we run Prisma generate, it's going to take that parsed like syntax AST from this and it's gonna apply it to whatever function this provides and builds types based off of that. And so what I've provided here is we need to go into our project and we need to run mpx Prisma generate again. That way it'll regenerate our Prisma client and it's gonna regenerate or it's going to for the first time generate these Pathos types. So I'll do this and we can see now that we have two success messages here instead of one like we had last time. We have generated Prisma client. So our client was regenerated correctly and it's also generated our pathos integration for us. And so these are like I've been saying, it's just a set of types that pathos is gonna use to actually give us type safety when building our GraphQL schema.
8. Configuring the Schema Builder
Short description:
We're going to configure the schema builder in the server by creating a new file called builder.ts. We'll import the necessary modules and types, including the schema builder, Prisma plugin, and Prisma client. These imports will allow us to build our GraphQL schema in a type-safe manner.
And so going back over to the tasks that finishes up this task for us. And now we can go in and actually configure the scheme of builder that pathos gives us. The scheme of builder class is what gives us the set of functions that actually gets used to build our GraphQL schema.
So let's create a new file. I'm gonna go in here into the server and I'm gonna create a file called builder.ts. And within this file, we're gonna need to do a couple of things. We're gonna need to import schema builder. We're gonna need to import the Prisma plugin. And we're gonna need to import all the generated types and our Prisma client. So let's go ahead and do that, I'll import schema builder from pathos core. And then we also need the Prisma plugin from pathos plugin Prisma, and then our type. So import type Prisma types from Pathos plugin Prisma generated.
And if you're wondering how I remember where all of these paths are, I don't. I have some notes over here that I'm looking at, and I've also provided some links in this document. So if you need to know where all these generated types get put at the very bottom, which you're gonna see down here is a link to the plugin docs. So you can actually know where all of these things are. So don't worry too much about memorizing those. I definitely don't have them memorized either. But we did, we missed one. So I'll import the Prisma client that we have. So I will import Prisma, and we can go ahead and import Prisma client as well. The type, from, this is going to be from BB. There we go.
9. Setting Up the Schema Builder for Type Safety
Short description:
To build our schema builder, we provide the Prisma types and configure the plugins and Prisma instance. This setup ensures type safety when building resolvers. Now, let's move on to further configuration.
So those are all the imports that we're gonna need for now to actually build out our schema builder. And what we need to do next is actually create an instance of that schema builder. So let's go ahead and do that. We're gonna export this because we are going to use it elsewhere in our application. It's just gonna be called builder, and it's gonna be a new schema builder. And the schema builder takes in a generic, which is gonna allow us to provide some types and safety in there. And what we're gonna provide there right now is these Prisma types because we are using this Prisma Pothos plugin. We need to provide the Prisma types to the schema builder so that it's actually aware of what types we're gonna be using and what our model looks like. So we're gonna do Prisma types here, and this is just gonna be Prisma types. Here we go. So we've applied those and now it's gonna be aware of what we're actually using for our Prisma types. And then within the actual functions object, this is the first parameter of the schema builder constructor. It's just gonna take in an object with a set of configuration. And the very first one is plugins. This is just where we're gonna define which plugins we're using. So we are using this Prisma plugin we imported. And the next is gonna be our actual Prisma instance. So now that we have this plugin Prisma, we see that we get this red squiggly lines and it's letting us know that we need this Prisma property. Pothos is smart enough to know that if we are using the Prisma plugin, we might want Prisma instantiated in here. So let's go ahead and provide that. We have to give it a client and we're gonna give it our Prisma client. So that's our builder. We've set it up with all the different types we need, and we're gonna see a little bit later down the line why it was important to actually provide all of these different types once we're building out a resolver. But for now, just know that that provides Pothos with the set of types that we need to actually build our resolvers in a type safe way. And I think for now, that actually finishes up the task three here. All we needed to do was build out this schema builder. And the next thing we need to do is actually configure it a little bit further.
10. Adding Custom Type for Date Fields
Short description:
We need to add a custom type called GraphQL scalers to handle date fields in our Prisma schema. This package provides additional types that are not supported by default in GraphQL. Let's install GraphQL scalers and proceed with the setup.
If you remember in our Prisma schema, and I'll actually go over to it now just to show you, we have these date times. We have created at and updated out within our notes. These are date fields and GraphQL by default does not actually support the date fields. It supports a certain set of scalers, which include integers, streams, Booleans, and things like that, but it doesn't by default have a date type. So we're gonna need to add a custom type into our project and apply it to our GraphQL schema builder. That way we can handle these dates. And to do that, we're gonna need to first install a package and this is called GraphQL scalers. This is a community tool that has been just upkept by the community and has a set of different types that maybe GraphQL default doesn't support by default. So let's go ahead and install that. It's gonna be called GraphQL scalers. And it looks like that installed properly. So that's good there.
11. Configuring Builder and Setting Up GraphQL Server
Short description:
We need to add a date scaler type and apply it to our builder. Then, we register the scalar type within the builder itself. Next, we add a root query called hello to our GraphQL schema. Moving on, we create a module that exports a generated schema in a file called schema.ts. We set up our GraphQL server using GraphQL yoga and create an index.ts file as the root of our application. Finally, we start the server and run npm run dev.
And then now within our builder.ts, I'll pop open the notion page again, just to show where we're at. We need to add this date scaler type and it's going to implement the date resolver scaler that gets given to us by GraphQL scalers. So we're gonna need to import that first of all. So if we get this date resolver, I'll just copy this over, and import date resolver, from GraphQL scalers. Cool. And then what we're gonna need to do is we're gonna need to actually apply that to our builder. So the way we can do that is we can go back into the generics here. We can go scalers, this is how we're gonna add our scalar values, and then we're gonna add a date scaler. And I'm gonna type this out real quick and then I'll explain what this actually means. So we're gonna have an input and an output. And so what this is gonna be is this is gonna be how we're going to actually define this date scalar in our resolvers and in our query responses, actually. So the inputs and outputs are just defining how to handle this date time field in those different situations in our schema. The next thing that we need to do is we need to actually register the scalar type within the builder itself. So this just set up the types for it, but we need to actually handle the resolver, how to actually resolve this. So we'll do the builder.addscalar. And we're just gonna call this a date and this is going to use the date resolver. And then there is a configuration object at the end, but we're just gonna leave that empty for now. So that's all the setup that you need to actually set up this date resolver. You just set up the scalar type for it and then actually apply the resolver to the server. And so at this point, now we have it all set up and configured with the proper types that we're gonna need to run our queries. And we need to actually build a root query so we can get our server going. So in builder.ts, we're just going to add a root query that's called hello, and it's gonna return the street world just so that we can actually get something working for now so that we can pop open the playground and see what's going on in there. So I'm going to just paste this little snippet here. Feel free to copy this over, this is in the notion document. But what this is doing is it's just adding a query. So it's adding the query type to our GraphQL schema, and it's creating a hello query, and all it does is resolves to the word stream.
And so with that, we can move on to task six, we need to create a module that actually exports a generated schema. So right now we're just defining how to generate the schema and we need to actually run the generate function, generate the schema and return that. And to do that, we're gonna create a new file once again and this one's gonna be called schema.ts. So in our server, create a new file called schema.ts, and we're gonna import the builder here. So import builder. from builder. And this one's gonna be really easy, we're just gonna do export const schema equals builder.to schema. So this is just taking our GraphQL schema configuration, it's actually running the to schema function, which is gonna generate the GraphQL schema abstract syntax tree, which is what gets used by our GraphQL server to actually spin up the server and define how you can interact with the server. And then we're exporting that so that we can use it elsewhere. So that's all we needed to do there. Next we need to actually set up our GraphQL server. Up until now, we've just been configuring the schema, but we don't actually have the server running. So we're gonna use GraphQL yoga to do that. This is just a popular GraphQL server that you can run and get set up very easily. So I'm copying over this install command to install GraphQL yoga. And I'll install that there. And then we need an index file. This is gonna be the root of our application. So let's create index.ts, and we're gonna import a function from GraphQL yoga that's actually gonna spin up our server, and this is called create server. So Import, Create server, from, oops, there we go. From GraphQL yoga slash node, because we are using the node implementation. And then we also need to import our schema that gets generated from the schema. And then we need to actually use the create server function. So let's do const server equals create server. So what we're doing here is we're creating a new server and we are storing it within a server variable. And this takes a bunch of different options that you can scroll through here. But the one that we care about is the schema because we need to actually provide a schema to this application. So we're giving it our schema. This is just shorthand for writing something like this. And then lastly, we need to actually start the server. So we can do server.start. And so at this point, we have defined our GraphQL schema, or at least the very start of it with the simple hello world query, we are generating our GraphQL schema here. And now we're importing it into our index file and actually applying it to our GraphQL yoga server. So with that, I think we are just about ready to actually run the server and it looks like we are. So if you head over to a terminal, you can run npm run dev. And let's see what this package missing is. Cannot find module source index ts from, okay, package JSON. This might be the piece that, oh, is it not in the source folder? Let me see. Oh, you're very right, yes. I apologize everybody for this mistake here. All of these files that we've been creating, all these ts files should have been in the source folder. Thanks for pointing that out, Jason. Let's move. Everything over.
12. Setting up GraphQL Server and Playground
Short description:
Make sure all files are in a source folder. Run npm install to fix any package issues. Open the GraphQL playground to explore the schema and run queries. Finish the section on your own and indicate when you're done. Take 20 minutes.
I think I had skipped that over in the document, but yes, they should all be in a source folder just to keep things nice and organized. And now I can run npm run dev, hopefully. Now it says, string, oh, I think this was something to do with one of these packages. I'm going to delete that. Just bear with me here, this was an issue that I had found before this workshop and had foreseen this coming up. So I'm gonna do npm install. Cool, so that looks good. Now I just need to update some of these, some of these here, unable to find module../., in the builder. Cool. So thanks for bearing with me there. There was some packages in this specific branch that were messed up and I just had to reinstall them. So now we've got our GraphQL server running at localhost 4000 and we can pop that open. I'll just do Ctrl click to open this. And what we're gonna see is the GraphQL playground. This is given to us by default by GraphQL yoga. And it comes with a bunch of cool different features. We have some docs on what's actually available within our GraphQL schema so we could see our hello query here. And then we have an explorer where we can actually look at all of our queries and mutation. So I'm just going to run this, hello query. And we see that we get data back and it's the world response. And that's exactly what we defined in our resolver. So that looks good there. And if we go back to our section here, it looks like that's the last step in this section. This is the last task here. So at this point, I'm going to, once again, turn it over to you guys. I'm gonna give you about 20 minutes to go through this section on your own. If you finish early, feel free to just play around in the GraphQL playground and explore what's available there. I will be posting a message in the Discord chat prompting you guys for thumbs up when you guys are finished. That way I kind of can gauge how many people have worked through the whole solution and see if we can maybe get started a little bit earlier. But so for now I'll just turn my camera and off and mute my microphone and give you guys 20 minutes.
13. Using Pothos to Build GraphQL Schema
Short description:
We're ready to move on to section three, where we'll use Pothos to build our GraphQL schema. After this section, we'll focus on the React client. Part three will give us a fully functioning GraphQL server to use with the client.
All right guys, I am back. And I think we're ready to move on to section three. Thank you for all the help in the chat, fixing all these issues with the npm run dev and the package versions. You never know when a package is gonna update and it's gonna take your stuff and that happened here. So yeah, thanks for all the help there and hopefully everyone's getting that resolved. If not, feel free to check out part three and you can follow along as we go. So part three is where we're gonna actually use Pothos, which we just set up to build our GraphQL schema. So we're gonna actually use it to build out our types and models and queries and apply it to our GraphQL schema and we'll have a fully functioning GraphQL server that we can then use from our React client.
14. Setting up Note Type in GraphQL API
Short description:
After this section, we are gonna be focusing completely on the React client because the server will be complete. So the goal of this, like I just said, is to set up a server that exposes our data in a dataset that we set up in the previous lesson. And so as for task one, we can build this note type. We have our note defined in our Prisma schema. So that defines how it's gonna look in our database. But we now need to define how we want a note to look through our GraphQL API. Those are very different things. You may not want to expose everything from your database via your GraphQL API. So that's why you have to do it separately here. To do that, we're gonna use our builder instance and define a note type. We're gonna organize this in a way that would be sort of maintainable if the application ever grew. The very first thing we're gonna do is create a new folder called Models. This is where we're gonna write any of our GraphQL schema types and models. The very first one we're gonna do is called Note.ts. This is just gonna define our Note model. In order to build up our Note, we need to import our Builder instance. We import the Builder from ./Builder. We provide a configuration object to define the fields we wanna expose through our API and how to handle some searching functionality. We need to add Find Unique to help with performance. Then we define our fields, including ID, message, created at, and updated at. This is now defining our note model, how we wanna expose it through our GraphQL API. Let's move on to the next section.
After this section, we are gonna be focusing completely on the React client because the server will be complete. So the goal of this, like I just said, is to set up a server that exposes our data in a dataset that we set up in the previous lesson. And so as for task one, we can build this note type. So we have our note defined in our Prisma schema. So that defines how it's gonna look in our database. But we now need to define how we want a note to look through our GraphQL API. Those are very different things. You may not want to expose everything from your database via your GraphQL API. So that's why you have to do it separately here.
And to do that, we're gonna use our builder instance and we're gonna define a note type and we're gonna actually organize this in a way that would be sort of maintainable if the application ever grew, which it won't. But it's good to follow those practices whenever you can. So the very first thing that I'm gonna do is I'm gonna go into our source folder here and create a new folder called Models. This is where we're gonna write any of our GraphQL schema types and models. And the very first one that we're gonna do is gonna be called Note.ts. So this is just gonna define our Note model. And this model is going to define how our GraphQL scheme, that sort of exposes and defines this data. So in order to actually build up our Note though, we need to import our Builder instance. So at the very top, I'm gonna import the Builder. And this is gonna be from./.Builder. So we have our Builder instance and this comes with a set of functions that we can use to actually build out our GraphQL schema.
The very first thing we're gonna do here is do Builder. And we get this set of functions, but because we are using the Prisma plugin, we get this Prisma object function. So this is the one we're gonna use for creating a object based off of one of our Prisma models. And we get some nice type safety here. And this is all because of that Prisma plugin that we set up. Pothos now knows that within our Prisma schema, we only have a note model and a user model. So we can just select this note model here. And then we provide this a configuration object. And this is gonna be how we define the fields that we wanna expose through our API and how to handle some of these searching functionality.
The very first thing that we're gonna need to add here is Find Unique. And what this does, this is just sort of a convention that we're gonna be doing in Pothos. And it sort of defines how if we have to do like a nested read in, in GraphQL, you run into like the N plus one problem where you might have a bunch of nested queries that all ended up building one huge giant query that tanks your database. This is something that helps with that performance. It helps with performance around that. This is something that the creator of Pothos has sort of let me know to do. And it's also documented in their docs which I've linked to here. So if you're curious about why this is here, feel free to look at that. But for now, just know that we have to add this find unique definition there. But the part that we really need to add right here is our fields. So fields is going to be a function that takes in T. It's gonna have one argument, but I've just named T. That's also a convention that Pothos uses. And we're gonna expose a couple of different fields. We're gonna expose the ID, the message, the created at and the updated at. So we just want these fields to be available via our API. And to actually expose those fields, we can use this T arguments functions. So this has a set of functions that allow you to expose these fields via your schema. So other T.expose, and this is gonna be an ID, and you could pass this ID. And then as you see here, we get this nice type safety once again based on our prism schema because we're using that Pathos plugin. So it knows which fields we actually have available. So I'll do expose ID, and I'll say we wanna expose the ID field from our database and then T.expose string. So I'll do expose string, and then it's gonna list out all of our string fields that we can use, and we only have one, it's a message. And then we can do our created that. So, once again, T.expose, and then this one's just gonna be exposed because this is our custom date type. So this doesn't have a built-in function. So what we're gonna do here instead is we're gonna do expose, and this is gonna be the created at, and we're gonna have to pass it this configuration object. So the type here is gonna be date, and that's it. That's how we tell it to use our custom date type. And then we're gonna do the same thing for updated at. We just have to update this name here, so updated at. There we go, cool. And so this is now defining our note model, how we wanna expose it through our GraphQL API. And what this is gonna result in is a note type on our GraphQL schema. And so we'll see that in a little bit, but for now this is all we need to do on our note type. Let's move on to the next section here. I'm gonna look at the questions real quick. I see Corinne has one. I switched to part three, but still have this error. Corinne.
15. Defining User Type and Resolver
Short description:
We just built our note type and now need to build the type for our users. We define the fields we want to return, including ID, name, and notes relations. We add Prisma to the GraphQL server's context so it's available throughout our application. We define the type for Prisma in the builder and update the context to include Prisma. Finally, we add a user query type to fetch all users and resolve the query using Prisma.
Corinne. Yeah, just exactly what Stephanie said. If you are still running into that error, look at the complete branch, copy over the package JSON there, or copy of the package JSON that I pasted above and follow those steps. There was an issue with a package that updated and it broke a couple of things. So okay, moving on to the second task, building the user type. So we just built our note type. We now need to build the type for our users. So I'm gonna create a new file here called user.ds and this file is also going to need the builder imported at the top. And this builder is going to run the same function as before. We're gonna run the Prisma object function and we're gonna create the user object. And then we need this, find unique function which I'm just gonna paste in so I don't have to type the whole thing again. And then finally, we can define our fields. So this is gonna be the T function and we can define the fields we wanna return. So in our case here, we just want to return the ID, the name, and then the actual notes relations so that we can drill down from the users into the notes and sort of create that relationship here. So to do the ID, it'll be the same as before, we'll be exposed ID. And for the name, we can do p.exposedString and we can see our name populated there. And then this is the one where it's a little bit different. We have our notes and we can do t.relation. So this is how you define a relation that set up. And then once again, we get this type safety because the prism plugins, so it knows that notes is the only relation we have. So this is the only one to care about. And that there's all we needed to do for our user object. This one was a little bit simpler than before. And now we can move on to the next section. I think I put a space of my name here for some reason. There we go, cool. So now pop open the the notion document again and we can move on to the next piece. So now at this point, we've defined our graph QL types and our models. And we need to actually define the resolver now. So we need to tell our server how we want to actually query for this data once a query is sent to our GraphQL server for this data. In order to do that though, we need to have access to our Prisma object. That way we can get access to its type save database client. And to do that, we need to add Prisma to the GraphQL servers context, that way it's available everywhere. So to do that, we're gonna go into our index.ts file. And here we need to import Prisma from our db file. And within our createServer, we can then go into our context and we can provide it Prisma. So now Prisma is gonna be available throughout our application and we can actually use it in our resolvers. But currently, this isn't done in a type seg way. This is now available, but none of our codes can actually know that this is available until we define the type for it. So in order to actually define that, we need to go into our builder. And once again, we're going to update this a little bit so that it will know what the shape of the context should be. So in this generic up here, I'm going to add a context key. And this is going to have Prisma client, which we imported previously. So this is just saying that there's going to be something in here within the context with the key of Prisma. And it's going to be of the type Prisma client. And I think that's all we need to do for that piece. And now we can actually move on to adding the user query. So task four, add a user query type within your Prisma instance. With your Prisma instance in the context and the types defined, you have all these, you need to write a query, add a query type for your user data to user.ts. So within our user.ts, we can define that type. And to do this, we're going to do builder.queryField. This is the function to add a field to your query type, which we defined in the builder.ts. We have this root query type here. So now we're adding a specific query to it. So we're going to call this query users because this will just fetch all of our users. And then this will follow the same convention as before, where it has a T. And then this T is going to do T.prismaField. So this is a function on the T that actually allows you to define a field within your query type. And what this is going to take in is a type. So we're going to give it this type of user because we are querying for users. And then we need to define how to actually resolve this query. And this is going to take in a bunch of different parameters. The first one is query root args-ctxinfo. So a lot of parameters here, and I'll walk through what the important ones are doing. So what we're going to do is we're going to return, and then we need to do ctx. And we can see now that we have Prisma here. This is because if in the builder, we defined our context, and we said that there's a Prisma key in it of the type of Prisma Client. So now we know that within our context, we have Prisma. And we're going to look for a user. So within Prisma Client, you can just drill down into the model you want to look for, and we're going to find menu.
16. Using Generated Query for findMany Function
Short description:
We can pass the generated query into the find menu function, which handles query generation for us. Avron net successfully completed the task. Workshop is going well.
And what we're going to pass into this instead of actually manually writing our query is we're going to do dot dot dot query. And this is some more magic with that Pathos Prisma plugin. It sort of under the hood handles generating this query in a performant way for us based on the GraphQL request that comes in, and we can simply pass that into the find menu function as the structured object, and it will handle this for us. So that's super cool. And that actually finishes up our resolver and this query for us. I see Avron net got it working. Great workshop so far. We'll catch up the rest of the recording. Avron, if you are listening, if you had already left, feel free to reach out if you run into any issues and let me know what you think about the rest of it. Good to have you.
17. Registering Queries and Types in Schema
Short description:
We've registered our queries and types in the schema.ts file to ensure they're applied to our GraphQL server. The schema now includes the data we've defined, allowing us to query it in the playground. We've set up our database schema using Prisma and the pathos plugin for type safety. The GraphQL API will provide type safety on the front end in the upcoming sections. Take 20 minutes to work on this section, and let me know if you have any questions.
So now that we've got this piece done, I'm gonna move on to the registering of these queries and types. So we now have all of these types defined, but this code doesn't actually get run anywhere. It's not referenced by anything that happens when our server starts up. So we need to actually import these somewhere where we can be sure that this is gonna get registered and applied to our builder before our server actually runs. And to do that, we'll go into our schema.ts file. This file here is always gonna be run when our server starts up because we use it on create server. So what we need to do is we need to import our different models here that we just defined, that way they're available within the builder before we generate our schema and before we apply it to our GraphQL server. So what we're gonna do is we're gonna simply import our models, note. And the user one as well. And I'm actually gonna change the order of that. So yeah, this just imports them. We're not actually doing anything with them. The reason we're doing this is because it's gonna simply import that which will trigger this code to be run and the objects will be added to our builder instance. So we've instantiated those since applied them. And at this point this is now applied to our graph QL schema. So we've updated our schema to have all of this data available. So if we run our server now, we can. I'll just restart this just in case. We can actually see in the playground what we've got. So now if we look at the playground and I'll pump the size up a little bit. If we look over here in the document Explorer, we could see we have this query type and we have a new query here, our users. And this is gonna return a list of users that follows this type. So if we go into our Explorer and we look for users, we can add the ID, the name. Unless let's say we add the message from the note. I'll close this guy. If we run this, we should actually get our data back at this point. So this is working now and we have a, our GraphQL server up and running with the API going. And just to sort of recap, what we've done so far, we have set up our database schema using Prisma and Prisma was sort of the source of truth. It's defining our database schema and the set of types we're using in our application code. And then we're using those types to, with the path those plugin to sort of give us some type safety as well. When we're actually generating our GraphQL types and models and resolvers. And the GraphQL API itself is gonna expose the GraphQL schema, which is gonna allow us to get some nice type safety on the front end, which we're gonna do in the next sections. So at this point, I am once again, gonna turn it over to you guys. I'll give you 20 minutes, so I'll be back on the hour and you can go through this section. If you finish it, feel free to let me know in the chat so I can gauge where we're at and I hope it goes well for you.
18. Setting up GraphQL Client and Writing Query
Short description:
In this section, we'll install the GraphQL client and provide it to our application. We'll write a GraphQL query to retrieve data from our API and organize it in a separate file. We'll use the gql helper from Urql to define the query and export it. Finally, we'll list the fields we want to retrieve from the users query.
All right, everybody, I'm popping back in a little bit early here. The next section that we have is gonna be a pretty quick one. So I think we'll save a little bit of time on there. So I'm gonna go ahead and jump right into that. I'm popping open the fourth lesson, which is called GraphQL on the Client. This is where we're gonna jump into our Reacts front end code, and we're gonna actually run our GraphQL queries and get some data to actually render some data onto the screen. And the goal of this lesson is to actually put our GraphQL server that we just got done building to use by hooking the Client side application up to it. We're gonna make a request to the GraphQL server and the GraphQL schema itself is gonna allow us to do this in a way where we can define exactly what fields we want, and that gives us the ability to create some nice type safety around it. So for the setup, once again, if you joined late and you're wanting to get started, there is the part four branch. You could start from there. If you have issues actually running the projects, feel free to look up in the Discord chat to the NPM run dev issues that we've fixed. But I'm gonna go ahead and get started here. The very first thing that we're gonna need to do is install our GraphQL client. But before I do that, actually, let's go through the client code and see what we actually have to start with. I'm gonna pop this open. We've got this client and now we need to provide it to our application. So to do that we're gonna go in here and we need to wrap our app component in this provider. So I'll do provider, and we need to give this the value of the actual client that we've started up, so we'll give it value equals plan. So to recap what we just did here, we have instantiated our GraphQL client, we can now use this to query our GraphQL API, and in order to actually use it within our application, we needed to provide it here. If you aren't familiar with providers, I think in the document I'm looking at it right now, I have some information about the urql docs and it's gonna walk you through sort of what the provider does and what the create server options are, so you can look at that if you're curious. But now that we've got that, let's head back to our document here. So now that we've provided urql to our application and now we need to actually write a GraphQL query for our data and we're gonna write this in an organized way because in the next section, we're gonna actually use these queries to generate types based off of the individual queries we're running, that way we could get that type safety between the server side and the front end. But for this section, what we're gonna do is we're just gonna write the query and run it as is without that safety so that we can see sort of why it's important later on. So what we're gonna do to organize this is we're gonna go into our source directory within our client folder, and we're gonna create a new folder called GraphQL. And within that folder, we're gonna add a file called users.query.ps. And this is where we're going to write our GraphQL query and within that, within this file we're gonna actually export the query as well so that we can export the string and use that within our generated client. So to write GraphQL, we're gonna import this helper from Urql called gql. And we need to export const and we're gonna call this get users. And this is where we're gonna write our GraphQL query. So we're gonna run a query called get users. And once again, in the document, I have a link to this to GraphQL syntax documentation that way if you aren't familiar with how to write a GraphQL query by hand, you can go and look at that and it'll walk you through the different ways you can set up queries and fields and parameters and all those kinds of things. But I've got this users query now that I've defined and we're gonna run the users query from our GraphQL API that's the one that we just got done writing in the previous section. And now we can list out the fields that we want from it.
19. Setting up GraphQL Client and Provider
Short description:
We installed the urql and GraphQL packages and instantiated a GraphQL client using the createClient function. We provided the client to our application by wrapping the app component in the Provider component. The client is now ready to be used for querying our GraphQL API.
And it's one that is super easy to use. It's pretty popular and it's pretty well maintained. So I like to use this one a lot. So let's go ahead and install that as well as the actual GraphQL package. So I'll do NPM i urql and GraphQL. So those are installed. Now we need to actually instantiate a client for us so that we can use it to query GraphQL, and we need to provide it to our application. And we're gonna do that in main.tsx. So what we need to do here is import the create client function and the provider component from urql. And so this is just the function that allows us to configure and create a client connection here. And this provider component is just a React provider that allows us to use this client throughout our application. So if we actually use this, I'll do constant client equals create client, and this is going to take in options. And there's a bunch of different options here. There's a bunch of different options here that we can set, but the only one that we need to actually worry about is this URL option. This is gonna be the URL that points to our GraphQL API. So this is HTTP localhost 4000 slash GraphQL. This is the endpoint where if we go to our server, when we run NPM run there, that's where this gets spun up. The 000 is another way of saying localhost, and it's at the GraphQL endpoint. I see a question from kilowatts, is this a client running server side? This is going to be not server side, this is the front end, this is gonna be completely a front end application, but it will be accessing our GraphQL API. So we've got this client and now we need to provide it to our application. So to do that we're gonna go in here and we need to wrap our app component in this provider. So I'll do provider, and we need to give this the value of the actual client that we've started up, so we'll give it value equals plan. So to recap what we just did here, we have instantiated our GraphQL client, we can now use this to query our GraphQL API, and in order to actually use it within our application, we needed to provide it here. If you aren't familiar with providers, I think in the document I'm looking at it right now, I have some information about the urql docs and it's gonna walk you through sort of what the provider does and what the create server options are, so you can look at that if you're curious. But now that we've got that, let's head back to our document here.
20. Writing and Running a GraphQL Query
Short description:
We write a GraphQL query for our data and run it without type safety. We organize the query in a new folder called GraphQL, create a file users.query.ps, and export the query. The query retrieves the ID, name, and notes for each user. We run the query using the useQuery function from the Oracle library. The results are stored in an array, with the data nested in an object. We have type safety within our database schema and GraphQL schema. The GraphQL schema allows us to safely query our API, and we manually define types on the front end to correspond with the backend types.
So now that we've provided urql to our application and now we need to actually write a GraphQL query for our data and we're gonna write this in an organized way because in the next section, we're gonna actually use these queries to generate types based off of the individual queries we're running, that way we could get that type safety between the server side and the front end. But for this section, what we're gonna do is we're just gonna write the query and run it as is without that safety so that we can see sort of why it's important later on.
So what we're gonna do to organize this is we're gonna go into our source directory within our client folder, and we're gonna create a new folder called GraphQL. And within that folder, we're gonna add a file called users.query.ps. And this is where we're going to write our GraphQL query and within that, within this file we're gonna actually export the query as well so that we can export the string and use that within our generated client. So to write GraphQL, we're gonna import this helper from Urql called gql. And we need to export const and we're gonna call this get users. And this is where we're gonna write our GraphQL query. So we're gonna run a query called get users. And once again, in the document, I have a link to this to GraphQL syntax documentation that way if you aren't familiar with how to write a GraphQL query by hand, you can go and look at that and it'll walk you through the different ways you can set up queries and fields and parameters and all those kinds of things. But I've got this users query now that I've defined and we're gonna run the users query from our GraphQL API that's the one that we just got done writing in the previous section. And now we can list out the fields that we want from it. So I'm gonna get the ID, I'm gonna get the name and then I wanna drill down into each user's notes. And for each note, I wanna get a message and a created at. So these are all the fields that we're gonna need to actually run our application. If we look through the types that we have to find right now, these are manually written, our user has a name and notes defined and message and created at. And you might already be noticing that it's a little bit different than what we're querying for, we have the ID and users as well, but it's not here. And that is a problem, that's actually one reason why type safety between the backend API and your frontend is difficult is because you sort of have to manually keep those in sync. But in the next section, we're gonna set up a tool that allows us to do this automatically. But for now, we'll just keep it this way and accept the fact that it's not the best it can be and it will be shortly.
So we've got that query written and I think we can move on to the next section now and this is where we actually run that query. So to run this, now that we have our GraphQL Client provided to our application, we can simply go to where we wanna run our query, which will be an app.tsx and we can import the client. So I'm gonna do import and there's gonna be a function called useQuery. And this is from the Oracle library. And this is gonna use our provided client to actually run our query once we use this function. I'm also gonna import the users query that we wrote from GraphQL users query. So this is just importing that query string here. And then with those available, we can actually get rid of this const and we're gonna replace it with the actual query that we wanna run. So I'm gonna do results equals, and then we're going to use query and we're gonna pass it the, oh, actually we need to pass it an object. And this object has a couple of options available and we're gonna use the query option and we're gonna pass the get users query. And now this isn't actually gonna be typed. This is of the type any right now, because we haven't defined what's actually gonna get returned from this GraphQL API. So what we're gonna need to do is use a generic in here as a generic parameter. And we're gonna say that this query is gonna return users, and it's gonna be of the user type and it's returning a list of those. Now keep in mind that this isn't ideal, that we have to define this manually. And also, this is the manually defined type that we're sort of updating by hand. This isn't based off of the actual query we're running. So this will get out of sync if your query ever changes. And that is a problem we'll be fixing later on, but for now, that's how it is. This results that it returns is actually an array. And the first index of the array is your actual results. And then your data from those results are nested in an object. So if we do that, what I'm doing here is I'm pulling out the data key from this results object, and I'm storing it in a variable called data. So now we can see that we're getting our data. It's of this type here, and it's got a list of users and each user is gonna be of the user type. So that looks good. And if we actually open up another terminal, let's see here. I'm gonna go into the server directory and I will npm run dev. That way we get our server up and running. Was it already running? Let's see. Oh, I think it was. So I'm going to just close everything. I'll cd into the server. npm run dev. I'll cd into the client. npm run dev. There we go, cool. So now we've got our server and our client running and we can actually go look at those. Let me make sure I'm not skipping anything. Doesn't look like it. So if we pop this guy open, now we can see that we have three listings and this is all the data that we cd'd into our database in the very first section of the workshop. So this is our actual data from our actual database being queried via our GraphQL API. So at this point, let's take a look at what we have as far as type safety. So we have type safety within our database schema. So we have safety there. So we define our schema through our Prisma schema and it gets applied to our database. What that does is it generates a set of types that we can use within our server's application code and those types get used with it pathos so that we can actually have type safety when we're building our GraphQL schema. So now we've got type safety across all of that. The GraphQL schema provides a way to safely query your API in a way where you know exactly which fields you need and which fields you're using and we're using TypeScript on the front end currently to manually define types that correspond with our types in the backend. So that's where the one missing link is there.
21. Setting up GraphQL Codegen
Short description:
In this section, we'll set up GraphQL Codegen to generate types for our client based on the queries we run against our GraphQL API. This tool ensures type safety by analyzing valid requests and generating corresponding types. To start, we'll install the necessary libraries and configure Codegen by creating a code gen.yml file. In this file, we'll define the schema URL, specify the files to look for GraphQL queries, and determine the assets to generate. We'll generate types and query objects for TypeScript and Oracle. Once the configuration is complete, Codegen will generate the assets into a file called generated.ts in the GraphQL folder. Let's get started on the first task by installing the required libraries and configuring Codegen.
We're still manually defining those. In the next section what we're gonna do is we're gonna actually set up that to where it's automated where it'll generate those types for us based off of the actual queries we're running against our GraphQL API. But for now, go ahead and walk through these steps. This one was a little bit shorter so I'll give 10 minutes. I'll be back at let's see what time it would be. I think it's 7.20 CET or 8.20 CET. I'm getting my time zones mixed up but I'll be back in 10 minutes. And if you have any questions while you're following along with this, feel free to reach out on the Discord. I would love to answer any of those. So I'll see you in a minute.
Alright, everybody, I am back. I hope all the questions have been answered so far. Corinne, I see that you were having those issues with the different ports. Like I'd put in the message, I would recommend just shutting down the servers and then starting the backend server first, which should have started up at localhost 4000. And then the client should be at localhost 3000 when you started up. When you open up the client within the code snippet I gave you, the client should connect to localhost 4000 and slash graphic URL. So if you have more issues, feel free to let me know. Otherwise, you could start off on the part five branch, which is where we're going to be starting off on this last section. So I'm gonna head over there, walk down here, and this one's called bridging the gap. And that's because as I mentioned at the end of the previous section, we still have this gap in types where our backend has its set of types, but then our front end has sort of a manually defined set of types that are supposed to match what we have on the backend. But if the backend ever changes, or if any of our queries ever change, we won't really know what happened. And you can see that as was shown at the bottom of the fourth section, where you remove the created app field, and then you don't get any TypeScript complaints. TypeScript as far as it was aware, you never had a created app field. So, yeah, so it just didn't know what was going on, and then your UI just spit out the not a number, that's how you ended up with the Batman, nananana message. So in this section, we're gonna be setting up a tool called GraphQL Codegen. And what this is gonna do is it's gonna actually analyze the queries that we're running against our GraphQL API, and it's gonna generate types for our client based off of those queries. And this allows you to have type safety because you can't even run a GraphQL request to a GraphQL API that's malformed. So it's gonna take these requests, and so long as they're valid, it's gonna generate types for those. This is gonna allow us to have these automatically generated types, and they're gonna be based on exactly what you're querying for. So to do that, let's start off on task one here. We first need to actually install all of those libraries. And to do that, I'm gonna copy and paste this. So if you go into your client project, let's see, I'm gonna copy and paste this install script. This install thing here, if you don't want to type out all of this, is in the solution dropdown for the first task. Feel free to copy and paste that one since it's just an install, and that's a lot of text to write. But what we're essentially doing is we're installing the GraphQL code gen CLI tool. And then these are a couple of plugins for it that we're gonna need to actually generate our generated types for TypeScript and the query objects for us. So I'm gonna install those here, and that should take just a second. Let's see how that's going. Good. Cool, so that's all installed. And that's all we needed to do for that first task. And the next one is actually configuring GraphQL code gen so that it will generate those types for us. And to do that, what you have to do is you have to set up a file where you provide your configuration, and it's called code gen.yml. So let's go into our code here. And outside of the source folder, this is gonna be in the very root of your client directory. You're gonna create a new file called code gen.yml. This is a configuration file where you can just provide different things like where your GraphQL API even is, which files you want to look through to look for your GraphQL query so you can generate your types, and then the assets it should generate. So we're gonna be generating types. We're gonna be generating query objects that we're gonna use with the Oracle. We're gonna be generating all that stuff. So let's define that here. The very first thing that we're gonna do is we're gonna define a schema. This is gonna be the URL location of our GraphQL schema. So in order to actually run CodeGen, your GraphQL server needs to be up and running, and ours is at a ttp localhost 4000 slash GraphQL. That's where our GraphQL schema lives, so long as your server is up and running. And now we need to tell it where to look in our front end code for these GraphQL queries. And what we're gonna tell it to do is look for any TypeScript files in our GraphQL folder, because this is where we've put all of our queries. So it's only gonna find this one query for now and it's gonna generate types that let our application know how to actually run this query in a type safe way and how the response should look based off of what we wrote here. So to do that, we're going to add a documents key here. And what this is gonna be is this is gonna be a string and we're gonna tell it to look in the source GraphQL directory, and then we're gonna tell it to look for any file with a.ts extension. And then after that, now we can define what this library is actually gonna generate for us. So we're gonna do Generates, and then we're going to drop down to the next line. And we're gonna say that we want to generate source GraphQL generated.ts. So we want to generate all of the assets from this CLI tool into a file called generated.ts within our GraphQL folder. This is where all of our types are gonna end up and all of our query objects. And then after that, we need to define which plugins we wanna actually apply to this generated output. And we're just gonna do all of the plugins that we installed. And I'm just gonna paste those here because it's a little bit easier. Oops.
22. Setting up GraphQL Codegen and Automatic Typing
Short description:
We set up the GraphQL Codegen to automatically generate types and objects based on our queries. This ensures type safety and prevents incorrect queries from being written. We replaced our manually written types and queries with the generated objects, eliminating the need for generic queries and reducing the chance of errors. The generated objects provide automatic typing for the query responses, ensuring that the data is available and preventing UI issues. We also removed unnecessary code and improved the overall cleanliness of our codebase. The GraphQL Codegen provides a high level of type safety between the front-end and back-end, allowing for a smooth and error-free development process.
Get this over. Let's see. Cool. So we're telling it that we are using the TypeScript plugin and the TypeScript Operations plugin, which helps us generate the query objects and the Type Document node, which is how we're going to actually provide our query to the UseQuery hook that we've used. So with that all in place, we should be ready to move on to the next piece, which is actually adding a script where we can run this command.
So if we head to package.json, we want to define a way to actually run this command. And to do that, we're going to add a new script here. We're just gonna call it CodeGen. And all it's going to do is run the command GraphQL CodeGen, which has a dash between it. So, GraphQL CodeGen. And with that, we can now run this command and it should generate all of our assets for us. So long as our server is up and running. So let me make sure it is. It looks like it's still up. So now I can run npm run CodeGen. And so this is what it did. It parsed our configuration file, and then it went really quickly, so you couldn't really see, but it actually reached out to our GraphQL API. And it said, hey, does this query look like it's a valid query based on what your schema looks like? It got a response back that said, yes. So now we have that safety where we know our query will actually work. And then finally, it used this query to generate a set of types and objects that define that query and its responses. So we've got all that available now. All of the types, all the query objects are available to us. So we can now start replacing our manually written types and queries with the generated objects. So if we go back into the code, we're gonna head over to app.tsx. This is where we've done all of our querying, and what we can do is we can actually get rid of all of our generic queries, so we can now get rid of all of our generic queries and we can actually get rid of this get users query string because we won't need that anymore. We can also get rid of this user type, or actually sorry, no, we still need the user type I believe. No, we don't, we can get rid of that. We'll get rid of that. And then we can actually get rid of this generic as well because the response is going to be known automatically. What we need to do though, is we need to import the query object that got generated for us and passed into here instead of this query string that we've removed. And the query object is this get users document, it's a JSON version of what a normal GraphQL query string would look like. So I'm going to copy this over. And if I go to app.tsx again, users document from GraphQL generated. So we're importing that document and we are getting the results out the exact same way we did before. But the difference here is that we don't have to manually type the response to this query anymore. If you look at the results, you should see that the data is of the type of get users query. And let's see. Now I was wondering why there's an error here, but actually this is exactly what we were hoping for. So now these results are automatically typed because we're using this get users document, use query now knows what our typed response should be and exactly what type it is based on this query. So now what we're seeing is this is saying, hey, the property created at is missing. If you follow through the last section, you remove the created at field at the end and TypeScript never complained, but it broke your UI. Now TypeScript is gonna complain and say, hey, something's wrong here. You aren't even querying for created at, you're trying to use it in your application. So what we can do now is if we actually add created at back and we run npm run CodeGen, now the issue goes away. So it knows, hey, now you're querying for that field, the data is actually gonna be available. So that's super cool. That's where we get some of that nice type safety there. I'm gonna remove it for now because that's how I've set it up in the workshop. So I'll just run Codegen again. And what we're gonna actually do is go into our components to the user message, to the message.tsx file here. And we're just gonna remove this. Makes it look a little bit cleaner. We don't really need the dates there. I'm just gonna get rid of it. But that is super cool though, how you get that nice type safety between the front end and back end. And the way this is set up right now, it won't even let you write a query that's incorrect. So you get that safety, not only with the types, but with actually writing your queries too, because Codegen is gonna complain if you write a query that couldn't be accepted by the GraphQL API. So I've removed that and we're still getting this error because I did revert it back to not having that created at type. And that's because if we go into the user display component, this is using the manually written type. This is using the user type from our types file, which doesn't have those automatically generated types for us. So we need to fix that. I'm gonna get rid of this, get rid of this. And what we're gonna do instead is we're going to import the type called get user query from our generated file. So these are the types that were generated for us based off of our query. And you can see the type there. So what we're gonna see in there is that we have a couple of different arrays. We have our users array and the notes array as you'd expect. And so what we want to do is we want to save that the note type. Or actually, I guess, we'll do the user type first. The user type is going to be of the type get user query and users.
23. Defining User and Note Types
Short description:
To ensure type safety, we define the user and note types based on the data retrieved from our GraphQL API. This makes our application completely type safe, from the backend to the frontend and even the writing of query strings. The developer experience is amazing, with tools like Prisma, GraphQL CodeGen, and Pathos working together to provide a comfortable coding experience. Take 15 minutes to work on this section, and I'll be back to check your progress.
And we just want to save that a user is going to be of the type users and just the first index of it. So it doesn't really matter which index you use here, but we'll just say that the first index of it is what we want our user type to be. So as you can see on this type now, it has the IB the name and the notes because that's what we've queried for. And then we're gonna need to do something similar to grab the note type. We're gonna go into the first user of the array. And then we're gonna go into its notes property. And we'll just take the type of the first note in that array. That way we can get that note type out. And so at this point, this should be not complaining anymore because now it knows that a user should be of the generated type based off of our GraphQL API. And with that, we now have our application completely type safe. The backend from the database all the way to this front end piece and even the writing of your query strings is completely type safe. You will get some sort of error if you make any mistake with the types or with actually writing your query. And that just makes it super easy to use. The developer experience here is amazing. The tools like Prisma, GraphQL CodeGen and Pathos all working together give you a superpower as a developer that just make you feel very comfortable writing code without having to worry and stress about breaking things. So with this section, I think I will give you another, we'll say 15 minutes to work on this and then I'll pop back on and see how everybody did and then we can wrap it up from there. So I'll be back in 15 minutes and let me know if you have any questions along the way.
Comments