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.
1. Welcome & kickoff
2. Set up Prisma (lesson 1)
3. Set up a GraphQL server (lesson 2)
4. Build the GraphQL schema (lesson 3)
5. GraphQL on the client (lesson 4)
6. Bridge the gap (lesson 5)
7. Wrap up
- 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 focuses on building a full-stack application with end-to-end type safety using React, GraphQL, and Prisma. It covers topics such as setting up Prisma, modeling the database schema, and applying database migrations. The Workshop also covers setting up the GraphQL server, querying the API, and bridging the gap between the frontend and backend with code generation. The goal is to ensure type safety and provide a smoother developer experience when making changes to the application.
1. Introduction to Workshop
Welcome to the workshop on end-to-end type safety with React, GraphQL, and Prisma. We'll be setting up a full stack application and using a notion document as our guideline. We'll start with the database setup and modeling data using Prisma. I'll walk you through the code and explain the details. Then, you'll have time to work on your own and ask questions.
♪♪ All right, I think we'll go ahead and get started. Well, first of all, welcome, everybody. Thank you for joining me. For me it's the morning time right now, but I know it's probably evening for a lot of you or mid-afternoon, so thanks for taking the time to come out and hang out with me for a bit.
In this workshop, we're going to be learning about end-to-end type safety with React, GraphQL, and Prisma as the title suggests. And we're going to be going through sort of all the different pieces of setting up a full stack application, setting up end-to-end type safety to do all this. But before we get into the actual work of all this, as you guys are joining that though, I'll go through sort of the details of what today is going to look like. We're going to be using this notion document that I've put together as sort of our guideline. It's going to be our road map through the whole workshop. And it's set up in different sections. And we're going to take them one at a time.
So, for example, at the beginning, we're going to be looking at our database setup. We're going to be modeling out some data using Prisma. And then we'll move on to the next pieces. And as we go through this, what is going to look like is I'll do a walkthrough. So I'm going to go through some of the code one section at a time, and I'm going to walk through the whole section and sort of code it out on my own in front of you guys, talking through my thought process as I go. And during these periods, I encourage you to sort of just watch and listen, take in the information that I'm sharing, and try not to code along during those times.
What this is really for is for me to explain cool little details that might be showing up in your editor or something and explain how individual pieces work. And if you're coding along, while it may be tempting, you may miss some of that information. So I encourage you not to go along during those times and to just sort of listen. And after I do that, I'll give you guys like, I think 30 minutes for each section, to sort of do it on your own and ask questions as you do so.
2. Introduction to Workshop
I'm Sabin Adams, a developer advocate at Prisma. We'll build a full-stack application implementing end-to-end type safety. We'll start by modeling our database and setting up Prisma. Then, we'll build a GraphQL server and connect it with a front-end application. The workshop may vary in duration, but it's approximately two hours and 40 minutes. Please make sure you have the Notion document and join the Discord server. Let's get started!
A little bit about myself. My name's Sabin Adams. I'm a developer advocate at Prisma. And as this thing says I've spent the last couple of years working as a full stack developer on a bunch of different teams. So I've got to touch a bunch of different stacks. And as I was working through my software development career, I found that I really enjoyed teaching. So doing things like this was always something that I enjoyed doing. So I became a developer advocate and now I get to do this full time. So I'm super excited to be here this morning and do some more with you guys.
If you want to find me for any questions that you might have about this workshop before or after the workshop, feel free to shoot me a message on any of these mediums here. And while I'm at it, actually I'm going to scroll up. And I'm going to post a link to this Notion document in the Discord. That way you guys will have access to this because this is going to be what you're going to follow through this whole process.
So what we're going to do in this workshop, as I mentioned, we're building a full-stack application. So we're going to be implementing end-to-end type safety in this application using a bunch of cool different tools. We're going to start off by doing the backend and more specifically, we're going to be modeling out our database and setting up Prisma in our server. And then we're going to be building a GraphQL server using a couple of different tools that make this end-to-end type safety glue sort of work together. Well, and if you don't understand what end-to-end type safety is yet, don't worry, I'll explain it throughout the workshop. And once we've got the server built, we're going to actually build our GraphQL schema and then use it from a front-end application that I've prepared. And then finally we're going to sort of glue the backend and the front-end together with some more tools that sort of piece together this end-to-end type safety puzzle. And if you're curious about the agenda for the workshop, it should be right here. I'm going to try to follow it as closely as possible, but there's always some variance. I've given this workshop multiple times. And there's always something that comes up that we have to work through. And it may add a couple minutes here, or I may go fast on a couple sections. So the time varies, but it should be roughly two hours and 40 minutes.
That's what it has been in the past usually. So I'm going to give you guys maybe just two or three minutes to make sure you've got the Notion document and you're in the Discord server. And then if you want to, in the Discord server, if you want to leave a reaction on my last message with the Notion document, just so I can be sure you guys are ready for me, I will be back in about three minutes just to make sure that everyone gets in there. If you have any questions, put them in the chat and we'll help you out, for sure. I see people reacting already, so that's good. It looks like people are able to get to it. Good sign. Also, if my voice goes out a little bit during this talk, fear not, I've been fighting off the cold. I'm just about over it now, but I've got the leftover messed up throat. So, bear with me, please. Very cool, getting more and more reactions on there. And I'm not seeing any comments or questions, so I think I'll just go ahead and jump in then.
3. Setting up Prisma and Modeling Database Schema
We'll start with the first lesson, which gives an overview of the goals and setup steps. In this step, we'll set up Prisma, clone the repository, and initialize Prisma. We'll model our database schema and create a user model with an ID field.
The very first part that we're going to do is we're going to jump into the first lesson. So, as I mentioned before, this is organized into lessons and we're just going to pop open the very first lesson here. And each lesson, as you're going to see, has a goal, so this is just a general overview of what you're going to accomplish in the lesson. It's going to have a setup step, which is the steps required to get caught up to this point, just in case maybe someone joins the workshop late, or if you're watching a recording of it and you just want to do certain parts. You can start each step with a branch that is associated with the step on the GitHub repository. And then after the setup step, there's going to be a set of tasks and that's what we're going to accomplish in each step. And this is the part that you're going to work through during the time that I give you.
So in this first step, we're going to be setting up Prisma. We've already got a GitHub repository up that has a server project and a front-end project. And we'll be cloning that down in a minute, but what we're going to be doing in this section is using Prisma to model out our database. We're going to actually create our SQLite database and then perform a migration so we get that schema up and ready and that database up and ready for us to start querying and adding data to. So for the setup, let's go through this. And also I just want to mention here if the font size on any of my windows or documents is too small, shoot me a message in the Discord, and I'll be sure to adjust it. But for now, the starter project, we should clone down the repository. So I'm going to pop open this link that's in the in the Notion dock, and I'm going to grab the link here. So you can just go to the GitHub page, hit the code tab and copy the link over. And from there what I'm going to do is just clone down the repository, and this will get us to our starting point. So I'm going to do git clone and just clone down the repository. So this is going to have our starting point, we're going to go into the directory and I'll show you what it comes with. So you're going to see two folders in here, you're going to see a client folder and a server folder. The server folders where we're going to be working for the first couple of lessons, this is where we're going to have our GraphQL API. This is where we're going to set up Prisma and model out our data. And then as we get towards the end of the workshop, we're going to move over to this client folder that has a prebuilt application that we're going to use to query our GraphQL API. So let's start jumping into what we're doing in this server folder. I think I will bump this font size just a little bit. Got to head back over to my notion document. And so here's our tasks. After cloning a repository, we've got this starting point, and we now need to set up Prisma. In this section, we're going to be setting up the database, we're going to be modeling out the data. So we need Prisma to do that. And in order to use Prisma, what we're going to need to do is actually install the dependency of the PrismaCLI. The PrismaCLI. So if you go into a terminal within your project, I'm going to cd into the server directory, because we are setting up Prisma within the server directory, and I'm going to npm install Prisma. And this should be a pretty quick install. And now that that's installed, what we need to do is actually initialize Prisma. And I'm going to do this from vs code so that we can see what happens as we initialize Prisma. So to do this, there's a command called npx prisma init and what this is going to do is this is going to initialize a Prisma folder in your project, which is going to be where your schema lifts, your Prisma schema. And the Prisma schema is where you actually model out your database schema. So I ran this, I ran npx prisma init, and we get this nice Prisma schema here within a Prisma folder. And we're going to be using a SQLite database. So if you pop open the schema file, we're going to change this provider to SQLite and what you're going to see below that is a URL. This is going to be the connection string to our SQLite database. But currently, this lives in an environment variable called database URL. And that's going to be in the env folder that Prisma created for you. And here, I'm just going to do file dev.db. This is going to say that the SQLite database lives in the current route, which will be in the Prisma directory, and it's going to be a file called dev.db. So that's all we need to do for that set up there. And if we head back to our notion document, we can now move on to the next steps. So these steps are pretty small, they're pretty simple. I try to break it out into small, easy to digest chunks that cover just sort of a main concept like that. So in the next one, now that we have Prisma initialized, this is what we're going to use to model our database schema. So we're going to be modeling our whole database. Using this, we're going to perform a migration and create the database. But before we do any of that, what we need to do is actually model out the schema. As we saw in the code, see if I can go back to it, we start off with a schema that looks something like this. We have a generator, which is what tells Prisma how to generate the Prisma client so that we can query our database. And we have our data source details. Next, what we need to do is actually define our database schema. And what this application that we're building is going to have is it's going to have a list of users that can be any user that we add to our database. And then each user is going to have a list of associated notes. So you can just set up a relation between the users and the notes. Each user is going to have multiple notes. So the very first thing that we're going to do with this is we're going to create the user. And within the instructions here, I've specified which fields that we want for each user. So for now, all we need is an ID, that's going to be an auto incrementing integer and a name, which is going to be the name of each user. So let's head back to the code here. And we'll start to model that out. So to do this, you're going to use the model keyword that sets up a database model, which basically is a database table. And we're going to call this a user. And what we need is the ID field, which we said was going to be an integer.
4. Setting up User Table
We're setting up the user table in our database schema. The ID field is the primary key with an auto-incrementing integer value. We also have a name field of type string. That's all we need for the user model.
So we can use the int. And if you have the Prisma VS code extension, you're going to get nice autocomplete like this. That's going to autocomplete some of these things for you and make things a little bit easier. I definitely recommend installing that if you haven't already. But this is going to be an integer, and this is the primary key of our table. So I'm going to do ID just to specify that. And then we also want to set a default value. And this value is going to be autocomplete. So this is a function within Prisma schema that says anytime a new record is added, we can prefill this column with an auto incrementing integer. So that's the ID, we've now got our primary key, and we can set up our name. And the name's a little bit simpler than that one. We're just going to call this column a name, and it's going to be a string. So I think that's all we needed for our user model. So we've now modeled out our user table.
5. Modeling Note and Database Migration
We've modeled the note and set up a relationship to link notes to users. Each note has a message, created at date, updated at date, and a user ID. We've created the necessary fields and used special keywords for default values and automatic updates. We've also established a one-to-many relationship between users and notes. Our database schema is now complete. Next, we need to apply the schema using Prisma migrate, which provides migration tools to handle database migrations. After running the migration command, we'll have a SQLite database file and a migrations folder with the applied migration. You can view the generated SQL in the migration file.
And now we need to model out the note. And the notes going to be very similar, except it has just a couple of extra columns. And then afterwards, we're going to need to set up a relationship so that we can actually link notes to their users. So once again, we're going to go back into our schema. And we're going to create another model and call it a note. And what I'm going to do is copy over this ID, because it's going to be exactly the same. We definitely want an ID with an auto-incrementing integer. And then we can look at the rest of the columns. So we need a message, we need a created at date, a updated at date, and a user ID, which is going to be how we link to each user. So we can go ahead and work through these now. We go into the model, let's go ahead and create the message. This is just a string again, so that's another easy one there. The next one was a created at column. So I'm going to call this created at, this will be a date time. And this is actually going to be a little bit different. So we're going to use the default keyword again to set a default value. And we want the default value to be now. And what that's going to do is it's going to cause Prisma to pre-fill this created at field with a current timestamp whenever a new record is added, essentially creating a created at date. And then very similarly, we have updated at. It's going to be another date time. But with this one, we actually have a special keyword. We have a updated at attribute here. And what this is going to do is automatically update this with the current timestamp anytime a note record is updated or changed. That way, we get a sort of look into the history of when the record was last updated. And then finally, we need a user ID, and this is going to be an integer. And the reason I'm picking integer is because it's supposed to match up with this up here. This is how we're going to link the records together. So those are all the columns, we now have two separate tables with all of their columns set up. However, we still need a way to link them. Each user has multiple notes. Each note has one user. So we need to set up a one to many relationship to to show this. And in order to do that, what we're going to do is we're going to create a user field on this model. And it's going to be of the type user, which points to this this model up here, we're using the model type user. And we're going to say that this field is actually a relation field. And then we're going to provide it a little bit of information, which is fields and references. And what that is is how, or sorry, which columns we're using to create this link. Now, fields is going to be the fields on the current model that are supposed to point to a reference on the reference model. So our field is user ID, that's this field here that we set up. And we want the user ID to reference an ID in our user model. And now that we've done that, this is still going to complain. And that's because we need to actually create the opposite relation as well. So within our user model, we need a way to drill down into a note. And Prisma is going to enforce this just to make sure you have your relations set up properly. So we'll go ahead and create notes. And we're just going to say this is going to be an array of notes. So there we go. We've now got our database modeled. It's a pretty simple data model. We've simply got the user, we've got our note, and then we've got the link between the two. And this is what we're going to be building our whole application based off of. But before we get ahead of ourselves, let's go back to our tasks and see what's next. Yes, the migration. So we've set up our database schema. But currently, this is just basically a definition of what the schema should look like. There's no database associated with it yet. And this hasn't been applied anywhere. So what we need to do is actually apply this by using Prisma migrate, which is a set of migration tools that Prisma provides to handle these database migrations for you. And just make it a little bit easier on you. And to do that, we're going to do npx Prisma migrate dev. And then I'm going to use this flag called name so that we can name our migration. And I'll just call it init. Because this is our first migration. And once that happens, we're going to see we get these, this dev.db file, which is our SQLite database. This lives on the file system. So it moves along with your code. And then we've got this migrations folder. And this is the migration that was actually performed. And you can actually go into it and see the SQL that Prisma generated for you. It created your database, and then it actually applied the SQL to your database.
6. Setting up Prisma and Seeding Data
We now have a working database and can move on to the next step, which is adding data. We'll cheat a bit by creating a seed.ts file in the Prisma folder, which will run when we run the seed command. It imports Prisma Client, clears the database, and adds new data. To run the seed script, we add a prisma key to package.json and run npx prisma db seed. We've now modeled the data, created the database, applied the schema, and seeded the database. In the next section, we'll set up the GraphQL server and have solutions and instructions available.
So we now have a database with these tables and the constraints defining the relations set up. So this is all ready to go. We now have a good working database and we can move on to the next step, which is actually giving it some data that we can play with in our application.
So back over here, what we're going to do for this is we're actually going to cheat because I don't want you guys to have to type out a whole seed file. So what I'm going to do is create a new file in the Prisma folder called seed.ts. And then I'm just going to paste this in. So let's copy this guy over. Let's see if I can do it. There we go, cool. Copy that guy over, go into the Prisma folder and create a seed.ts file. And then I'll paste this in. I think it copied the whole Notion block, so I just had to take those out. So this is what's going to run when we actually run our seed command. What we're doing is we're importing Prisma Client, which if you didn't see from the output down here, when we ran Prisma Migrate, after it created our database and applied our migration, it also ran the command called Prisma generate, which generates Prisma Client, and that's what you use to query and interact with your database. So we're importing that here, we're instantiating it, and then we're just running a function that clears out our database and then adds some new data using the Prisma Client, so we can see we're creating a user here, prisma.user.create, we're giving it, excuse me, we're giving it a name, and then we are giving it a couple of notes, which will create these notes and automatically set up the relations. So with that in place, what we need to do to actually run this seed script is go into our package.json, and we're going to add a prisma key to it, and within that, we're going to have a seed key, and this is gonna run, ts-node-dev-prisma-seed.ts. So we're just saying when our command that we're gonna run is run, we simply want to run the seed file. And to actually run this, now that that's in package.json, you could do npx prisma db seed. And there we go, we can see our data was seeded and the seed command has been executed. So at this point, what's happened is you've modeled out your data, you have created your database, and then you've performed a migration to actually apply your database schema to the database and you've now seeded your database with the data that you need. So at this point, we're done with the first section and what I'm gonna do is I'm gonna turn it over to you guys to start going through this on your own. I'm gonna give you guys 30 minutes, so I'll be back roughly around 10am PST. So yeah, just about 30 minutes from now. Um, so let's go ahead and jump right into it. I'll pop open the, uh, the second lesson here. And the goal of this is gonna be to set up the GraphQL server. So we have the server folder. We have Prism setup, but we don't actually have a GraphQL server yet. So that's what we're gonna be doing in this section. Um, I also want to point out that if you didn't notice in the first part, I forgot to mention that there are solutions available at the end of each task as well. So if you get stuck, um, and if we sort of talk through it on the Discord and we can't seem to find a solution, uh, for you, that you can also pop this open as sort of a cheat to, uh, see where maybe something went wrong. Um, also from here on out in the workshop, every setup step is going to have instructions on how to get, uh, caught up to the point where we're at. So if you got stuck on a section and then, and we move on, um, you can check out the branch associated with the next section. And it should have a working folder at this starting point. Uh, so instructions for that will be here. Uh, but let's go ahead and start, get, uh, start going on the tasks. Uh, the very first thing that I want us to do is to actually instantiate and export Prisma Client from a module within our server project. That way we can just get that out of the way. We're going to be using it a lot. Uh, so let's go ahead and get that going. Uh, to do this, we're going to create a new folder called source, which is where all of our code is going to live. And we're going to create a file called db.ts. And then we're going to actually instantiate our Prisma Client as we did in the seed file. Uh, and export it so that it's actually in a usable state. So I'll go back to the code. And as the instructions suggested, I'm creating a new folder here called source. And within there, we'll have a eb.ts. And this is simply going to import Prisma Client. It's going to export a const called Prisma. And this const is going to be a new Prisma Client. And then I'm also going to export the Prisma Client type. I think yeah, yeah. Because we will need it later on. And might as well get that out of the way now. So all this is doing is instantiating our Prisma Client and exporting it so that we can use it throughout our application. And that's all of the first part of the step here. So we have instantiated it and exported it and we should be good to move on to the next piece. And that's adding a tool called Pathos to our project. And this tool is what we're going to be using to build our GraphQL schema later on. This is a code-first schema builder. So through our code, we're going to be building our GraphQL types. But the cool thing about this tool is that it comes with a nice plugin for Prisma specifically that gives you type safety based on your Prisma schema within your GraphQL resolvers. If you aren't sure what any of those things mean, we're going to be going through it throughout the workshop so don't worry. But just know that we're adding this tool so that we can actually generate some extra types when we run Prisma Generate that are going to help us as we develop our GraphQL schema. So to get this going, though what we need to do is actually install these. So let's go ahead and copy this over. I think I can just copy that actually and go back to our project. And then I'll paste that in. So this is installing Pothos and the actual plugin that we need to get it to work with Prisma. And once those guys are installed, we're going to head over to our Prisma schema and I'm gonna close everything here so it's nice and clean.
7. Setting up Pothos and Schema Builder
We'll add a new generator called Pothos to our Prisma schema. This generator, provided by the Prisma Pothos types plugin, will generate types for us. After running 'npx prisma generate', we'll have the Prisma client and the Pothos integration generated. Next, we'll set up a schema builder by instantiating it, configuring it, and exporting it. The schema builder allows us to build our GraphQL schema using various functions. We'll also provide types to Pothos for type safety in our resolvers. To achieve this, we'll use the Prisma types and the Prisma plugin. We'll also need to provide the Prisma client. Additionally, we'll install the GraphQL Scalars library to handle the date type in our GraphQL API. We'll apply the library to the schema builder and provide the date resolver. These steps ensure end-to-end type safety in our database and API layer.
And we'll go back to our Prisma schema. Now what the plugin does is it provides us with a second generator and these generators are what generate assets for us based off of our Prisma schema when we run npx prisma generate or when we run a migration which runs the Generate command. So we're gonna add a new generator here and we're just gonna call this Pothos. And the provider for this is, actually forget what the provider's name is, let me look it up real quick. Prisma Pothos types, Prisma-pothos-types. So this is gonna say that, now that we've installed the Pothos plugin that comes with the Prisma generator, which is a custom generator built for Prisma, and we're saying when we run Prisma generate, we want to use that custom generator that Pothos provides to generate some output. This is gonna be some types Pothos is gonna use for us. But now that we've done that, we need to actually generate those. So we can run MPX Prisma generate. And this is gonna go through and generate all of our stuff again. So if I expand this a bit, we're gonna see Prisma client got generated again, like it was before, but now we also have the second one, the Pothos integration. So you can actually provide different generators in your Prisma schema to generate different types of assets. But with that in place, we can now move on to the next piece. And that's gonna be setting up a schema builder. So this is the class that Pothos provides that allows us to have access to a bunch of different functions that build up your GraphQL schema. And we need to actually instantiate a builder and configure it so that we can use it the way we want. So what we're gonna do is create a builder.ts file in that source directory that we created, and then we're going to instantiate the schema builder and export it. And then we're gonna configure it a little bit as we go through this. So let's go ahead and get going on that. Open up the code again and head over to source. And we're gonna do builder.ts. And then as the notion doc suggests, we should import the schema builder and a bunch of these other things here. And I think I'll copy and paste those for time's sake. Let's go ahead and import. There we go. So I've imported all of those things. These are the schema builder, the Prisma plugin that we installed, the Prisma types, which are the types that got generated from the Prisma pathos plugin generator that we just added, and then, of course, our Prismadatabase instance here. We're exporting it because we will be using it elsewhere in our application. And this is going to be a new schema builder. And the schema builder actually has a generic type parameter. Now, if you don't know what these are, don't worry. I'll sort of explain what it does. What this is going to do is allow us to provide some types to pathos. And these types are what's going to allow us to have some type safety within our actual resolvers. So the types that we put in here are going to give us type safety within the graphQL resolvers where we actually run our queries and expose data. So what we're doing with this is we're going to say prisma types here. This is a key that pathos is gonna look for. And we're going to pass it our prisma types. So this is going to say that... Oh, sorry, this is what pathos is going to use to actually know what our prisma schema looks like. So that as we're building our graphQL resolvers, and we're accessing fields from our database within prisma in there. It's going to allow us to do that in a type safe way. So if we try to expose a field through graphQL, that doesn't actually exist in our database, it's going to give us an error now, because we're setting this up. And that's what makes this integration with pathos and prisma super cool here. But in order to do this, we also need to have a plugin, which we installed. So this is going to be the prisma plugin. And then we need to provide prisma client. That's why this is still complaining at us with a red squiggly line is because we need to provide an actual client. So we've now got our schema builder instantiated, and it knows about our prisma types. So when we write resolvers, it's going to give us that nice type safety with our database within our actual GraphQL API. So that's already a bunch of the cool steps of end-to-end type safety just within the database and the API layer. And we're going to actually put it to use in a little bit here so we can see why it's so powerful. But before we do that, we've got a couple other things to do. If you think back to our prisma scheme, we had a couple of date fields. We had the created at date on the note, and we also had the updated app. And unfortunately, GraphQL doesn't by default support a date. It supports these here, a boolean float, ID, integer, and string. So in order to actually use a date within our GraphQL API, we need to install a custom scalar type to handle a date time. That way, our GraphQL resolvers are going to know how to convert to a date. So to do so, we need to copy over this install command. We're using a library called GraphQL Scalers. This is just a popular community led project that provides resolvers for different data types, and we'll install that. And then we're going to need to actually apply it to our schema builder so the schema builder knows what a date is. And then we need to provide the resolver to the schema builder as well. So we'll go through all those steps right now. First of all, I'll install GraphQL Scalers. And then now that that's installed, we can import that here. So we're going to import something from GraphQL Scalers. And the thing that we're going to import is called date resolver. This is what's going to resolve some value to a date for us.
8. Setting up Scalar Types for Dates
9. Creating the GraphQL Server
To run the server, we need to add a root query using the Schema Builder instance. We're setting up the root query type and building an initial query that returns the string 'world'. Next, we need to create a module called schema.ts to generate our schema using the 'toSchema' method. With the schema generated, we can now create the GraphQL server using GraphQL Yoga. We import 'createYoga' from GraphQL Yoga node and 'createServer' from the node HTTP library. Finally, we create the yoga server by passing in our schema.
And the next thing that we need to do in order to actually run the server, we need some sort of query or some sort of type. A GraphQL server won't run unless you have a query available to it. So we need to add just a root query using the Schema Builder instance. So I'll go ahead and do that. So I'll go ahead and do that. Let's see here. Just looking at my notes to make sure that I'm staying on track. And it looks like we are. So I'll head back over to the code and we can get this root query built. And to do that, we're going to use the Builder instance. We're going to add a query type. And this is going to be the root query type. So if you're familiar with GraphQL, this would be like the type query that you might have in your GraphQL schema. That's essentially what we're doing here. And this is going to take in a configuration object. And it's going to take in a list of fields. And the syntax for this is going to look like this. I'll just type this out and sort of explain what's going on afterwards. It's gonna be T.field. And this is gonna be a... We're just gonna return a string for now. And the resolver is going to resolve the string world. Okay. So I always have to double-check as I'm typing this one out just because the syntax doesn't like to stick in my brain. Doesn't like to stick in my brain. But here's what this is doing. We're setting up that root query type and then we're building an initial query. So this is just a simple GraphQL query that returns a string. And the string that it returns is just the string world. So now if we actually run our server and we had a GraphQL server running, we would be able to actually query this hello query and get the string world back. We won't do that quite yet, but we will later on just to see how it's working. The reason we can't do that yet is because we need to actually set up the GraphQL server and actually generate the schema based off of this code here. So to do that, well, first of all, let's go look back at our notion document. We now need to create a module that generates our schema. So it's going to use this code- first approach and it's going to generate the schema object for us. And we need to export that so that we can use it in different places. So, let's create a new file called schema.ts. It's going to import the builder that we've exported from builder.ts and then it's going to use the method on it called to schema. So it's going to be a really simple file for now, but it will be useful later on. So we'll create schema.ts. We'll import builder from our builder file. And then we're going to export a const called schema and it's going to equal builder.toSchema. So this builder variable here, the schema builder instance has a function on it called to schema. So now that we've defined a query, we've added scalar types, it knows what we want our GraphQL schema to look like. And when we run to schema, what it does is it actually generates it and returns it. So that's that, we now have our schema generated. And I believe the next step here is yes, actually creating the GraphQL server and using that schema so that we can run it and actually start exploring. So let's go ahead and install GraphQL yoga. That's what we're going to be using to run our GraphQL server today. I've chosen this one just because it's a pretty simple one to use. And I've always really liked working with it. So this is the one that we'll try out. Today. Cool, so that's installed. Now we need to actually create an index file, which is going to be sort of the root file of our server. And we'll import something from GraphQL yoga node. And that's something that we're importing is called create yoga. This is what's going to actually create a yoga server for us. But then to actually serve this, what we need to do is import another function called create server. And this comes from the node HTTP library. So we'll import create server from node HTTP. There we go. So we can now create a HTTP server and we can create a GraphQL server. What we need now to create the server is our schema that we just generated. So we can import the schema from that file. And then finally, we can get going creating our yoga server. So create a new const called yoga. It's going to be create server, sorry, create yoga. And what this is going to do is it's going to take in our schema. Cool.
10. Setting up Yoga Server and Running
We've created our yoga instance, run it on an HTTP server, and specified the port for it to listen on. Now, we just need to run it using 'npm run dev'. Opening localhost:4000 will display the playground with our 'Hello' query that returns the string 'world'.
So we've got the yoga instance there. And now we need to create a server that uses that yoga instance. That way, we can actually serve up this GraphQL API. So we'll create another constant called server. And it's going to equal create server. And we'll pass it our yoga server. So that's most of the setup. Now, we need to actually let the server listen to a port. So we'll do server.listen. And we'll just run this on port 4000 for now. And I'm going to copy over a console log that I have prepared just so that we can be sure that we know when this server is ready for us. I'll copy this. There we go. Cool. So now when our server starts, we're going to get this nice log. And I think that's all we needed to do for this step. So we've created our yoga instance. We've run it on an HTTP server. And we've specified how we want it to be listening to a port. So at this point all we need to do is actually run it. So I'll copy that guy. npm run dev is what we're going to do to run this. And that's going to run our server. And it's going to run it on localhost 4000. So if you open up localhost 4000, you're going to get the playground. And what you can see in here is if you go to the documentation, you're going to have your query type. And we're going to see our Hello query that we built here. And when we actually run this query, you get the string world. So that's the root query that we set up. And I think at this point, if I go back to the Notion document, I think this is all that we had for the second step. So once again, what I'm going to do is I'm going to give you 30 minutes. It is... Let me see the time. It's 10 my time right now. So at around 10.30, I'll come back and see what your progress is.
11. Building GraphQL Schema and Exposing Data
In this part, we build our GraphQL schema to expose data from the database. We set up the schema builder and create model files for each object. We use the Prisma plugin to ensure type safety and access the types generated from our Prisma schema. We define the fields we want to expose through our GraphQL API, starting with the ID field. This is the first step in building our GraphQL schema.
All right, everybody, I am back online and ready to get going on part three. So just to recap what we've done so far. We have set up our database. We've modeled it out. We've seeded it with some data. We've performed migrations. So we have a working database. We have a working GraphQL server now. And currently, this GraphQL server only has one query that simply returns the string world. I think it was world. So it's really simple. But in this next piece, what we're going to be doing is we're going to be building out our GraphQL schema and exposing the data from our database. So if we jump into the lesson here, the goal of this lesson is to set up a GraphQL server that exposes data from the database. GraphQL is strongly typed and it's going to allow our front end to request exactly the kind of data that we need in a type safe way. Once again, if you are joining late, you can get caught up to us by either going through the lessons previously, or by checking out part 3 branch of the github repository.
But jumping right in, we now have our GraphQL server and we've also set up all the infrastructure that we need to be able to build our GraphQL schema. We have our schema builder ready for us, we have the schema being generated for us. What we need to do is we need to actually build out models that represent the data we want to expose from our GraphQL API. To do that, we're going to need to use the schema builder that we instantiated and we're going to set up a couple of model files where we're going to model out these objects and we're going to do it in a type-safe way using this Prisma plugin that we use with Pathos. And while this may seem confusing as I'm saying it right now, it's going to make a lot more sense when we jump in. So I think I'll go ahead and do that.
The first task that we have is to actually build out our note type. So we're going to use the builder instance and what we're going to do here just to keep things organized is we're going to create a new directory called models within our source directory and we're going to create a file in there called note.ts. So let's go over and do that. I'll go into my source directory, I'm going to create models and a new file called note.ts. And this is where we're going to build out anything relating to a note. So the very first thing that we're going to need to do is we're going to import our builder. If I can type properly. There we go, cool. So this is our schema builder. This is our code first approach to building our GraphQL schema. And because we're using the Prisma plug-in with Pathos, which is how we're using this builder, this is the Pathos provided function for us. Because we're using the Prisma plug-in, we actually have access to a couple of Prisma specific functions on this builder instance. So we're going to use builder.prisma object. And what this is going to do is it's going to allow us direct access to the types generated for us from our Prisma schema that have representations of what our Prisma schema is. That way, we could build out our GraphQL schema in a way that's safe so that we're sure that we're exposing fields that actually exist in our database. And here's what I mean by that. So I'm going to open up this function. I'm going to give this Prisma object a name, and we're going to see we have two names available to us through autocomplete. We have note end user. And this is because we have no end user in our Prisma schema. Because we provided the types in our builder right here, these Prisma types, that's what gives us access to be able to see these autocompletions here. So we're building out the note type, and this is going to take in a configuration object. And one thing about pathos is that we're going to have to model out a function here. It's going to be called find unique. And what this is going to take is a single note, this is going to be pass a single note. Sorry. And it's just going to return the note.id. Now, this is like a boilerplate type of thing that we have to do here, because pathos needs to have a way to resolve an individual note to avoid the n plus 1 problem when you're querying through GraphQL. This is just a way for it to know how to drill down and find a note and grab an ID. So you don't really have to know why this, like in detail why this is here. Just know that this is needed so that it can resolve specific notes. But with that part out of the way, we can now set up the actual fields for this GraphQL object. And for some context, what we're doing is we're essentially building out what you would build out in a GraphQL schema by typing type, note, and putting all of your fields in here. We're doing that through code. So let's do our fields. This is going to take in a parameter that's called t. And this is essentially going to be some functions. This t is going to have some functions that Pathos provides us to actually expose fields that we want to expose. So this is going to do is return an object. And this is where we define all the fields that we want to expose through our GraphQL API. So the first one is going to be our ID. We want to expose that. So we'll do t. And it has a function called expose ID. So we'll do that. And what we're going to see here is we get a couple of options here. These are all different strings. So the ID is essentially a string here. So we can do this ID and then message.
12. Exposing Fields and Adding Prisma to Context
We expose the string field, message field, created at field, and updated at field in our Prisma schema. We set up a custom scalar for the date field. We also build the user object and expose the ID and name fields. Additionally, we expose the relation between the user and notes. Finally, we add the Prisma instance to the GraphQL context for resolving models and querying the database.
We'll do t.expose string. And this one, we're just going to get message because this is in our Prisma schema on the note. This is the only actual string field here. So that's why that's the only one that pops up in autocomplete. And that's also thanks to the type safety that we've set up. So we're already seeing why some of this end-to-end type safety is super useful to you as a developer because now you know exactly what's available to you and how to use it properly.
But the next one is our created at field. We want to expose that. And to do this, it'll be a little bit different because this is the date we had to set up a custom scalar for it. So we're just going to use a function called expose and we need to actually expose it as our custom type. So we're going to do quotes here. And once again, we get our list of available columns because of the end to end type safety. And we did pass this a configuration object where we're going to specify our type. And then because we provided that type to the schema builders, generic type scalar key, we actually get this date type here. And what I'm referring to is this that we set up. That's why we get the auto-complete there. So what I'm going to do next is just copy this guy over because we also want to expose the updated app field. And it's going to be exactly the same. So this is our note model. This is all that we want to expose to our front-end application. This is what our API is going to have available for note is the ID message and its two date fields. So with that done, we can go back to our document. And I posted a link to some documentation here on the pathos Prisma plug-in. If you're interested in some of the more in-depth details of what it offers, the link is there. But the very next thing we're gonna do is essentially the same thing, except we're gonna be building our user type out so that we can have a user and expose that through our API and expose the relation to the notes. So let's do that. We're gonna do essentially what we did before. We're gonna go to our models directory. We're gonna create a user.ds file and we'll import the builder. And then like before, we'll do builder.prismaobject. This is gonna be the user object. And I can get my notes open here. It crashed on me. There we go, cool. So then we're going to build up this configuration object. Like before, we need to do the find unique. And I'm actually gonna copy this cause I don't like to type it out. There we go, cool. Copied that over and we'll set up our fields. So this is just like before so far except we're building the user object. The difference now is the fields that we're going to expose. So we're gonna do the ID again. This will just be the ID field. And we're gonna do the name which is the string. This last one though is a little bit different. What we're exposing here is not an actual field. This is going to be a relation. So it's gonna be a way for a user of your GraphQL API to grab the user and grab some associated notes. So what we need to do here is create a field called Notes. And we're going to do T dot relation. And because of the end-to-end type safety we've set up, again, that's gonna be a common theme. You're gonna hear me mention that a lot because that's really the cool part about all of this that we're setting up. Because of that type safety, when we open up a string here, we can see notes. And that's because Pothos knows that the only relation we have on our user model in our Prisma schema is a notes relation. So it knows that we probably want a notes relation here to be exposed. So there we go. That one's a little bit easier. That's our user model, and we can move on to the next piece.
And that's going to be adding Prisma to the GraphQL context. So now that we have our model set up, we're gonna need a way to actually resolve these models once the GraphQL API is queried. And to do that, we're gonna need to be able to query our database using Prisma Client. So we're gonna add our Prisma instance to our GraphQL context so that we can use it in our resolvers. That's gonna make a little more sense as we go through this right now. So let's go back to our code, and I'm gonna hit Save here and just close everything so it's nice and clean. And then I'll pop open index.ts once again. So in this file, we're going to need to import our Prisma instance, which we created already. This is gonna be import prisma from.slash db. If you remember in one of the previous lessons, we created the db.ts file that actually exports the Prisma instance. So we've got that. And now within our yoga instance, here we can set up a constant, or sorry, a context.
13. Adding User Query and Registering Types
We add the Prisma client instance to our context so that it's available in our resolvers. We then build a GraphQL query called users that retrieves data based on the user's request. The Prisma integration in Pathos automatically generates the Prisma query object for us. We register our queries and types in our schema by importing the necessary files. This ensures that the builder applies the models to its instance. Finally, we run the server to test its functionality.
And this context is going to have a key called prisma. This is going to just hold our Prisma client instance. So at this point, Prisma is actually available through our context, but our resolvers don't know it's available. You can use it there, but TypeScript doesn't know that it's actually a key that's available in our context. So what we need to do is go into our builder.ts, into this over here, where we can set up all of those different types. And here, we can use another key that Pathos uses and looks for called context. And this context is going to have a Prisma key, and it's going to be of the type Prisma Client. And we can actually import the Prisma Client type from the.db file, because we exported it there. There we go. Cool. So now our GraphQL resolvers through Pathos are going to know that our context has a key called Prisma, and that Prisma key holds our Prisma Client. The reason we did that is because now we're going to actually build a GraphQL query that can query for our data. So let's go look at the lesson where we're going to do that. Task four, add a user query type. So within our user.ts file, we need to build a query, and that query is simply going to grab any data that the user requests, and return it back. So let's look at how to do that here. If you go to user.ts, here we can use the builder instance again, and this is going to be a query field. Now, we're not using the function query type because that sets up the root query type. Now we're just adding a new query to that root query type, the new query field. So this query is going to be called users. And this is going to take a T that's just a convention with Pathos that you name the arguments T and that just simply has the functions that are available to this specific callback. So what this is going to have is T.prisma field, and then that contains an object. So what this does is this is saying that our user's query is going to have a set of Prisma fields, and now we just need to define what this is going to be. So we're going to say this is of the type, and once again, with the type safety, it gives us the types that we know we care about. And so this is going to be the user type, and then we need to resolve this query. And this will be an asynchronous function. And there's a set of arguments. So there's query, there's root, there's args, there's context and there's info. We won't use all of those, but I like to add them all so I know what's available to us. And what this resolve function is going to do is it's going to return our data. So it's going to return the data that the user asked for based on their request. So we're going to return. And this is where we get to use our context. And we see we get Prisma because we added that typing. And then we're going to do user.findMany. And now normally in a resolver, what you'd have to do is you'd have to determine what the user was requesting from your API based off of the requests and the arguments and all that stuff in the query. However, POTHOS has a cool little thing that it does under the hood when you're using the Prisma integration with it, where it actually can inspect your request and build you out a Prisma query object based on the request that gets returned. So that's what this query argument is here, in a normal GraphQL API, you would not see this. This is only if you're using the Prisma integration. So all we need to do actually is spread that into the query. And this is going to automatically build up your Prisma query for you. And the reason we do it like this is because it actually builds it out in a nice and performant way where it's going to avoid refetching data wherever it can. So this is the only query we need. Our GraphQL API is only gonna have one query that you can query from because our front end is so simple. So that is all set up, I think. And we can move on to the next piece. And that's gonna be registering our queries and our types in our schema. So what this is, is if you look at the code right now, we have our schema.ts where we're using our builder now that we have all of our builder functions defined in our types and all of that. We're doing a two schema and exporting that. And then over in index.ts, we are using that schema to apply to our GraphQL API. However, the problem right now is that our code that defines our actual models and our types right here and our query types, this code isn't actually called anywhere, so it never gets run. And because of that, the builder instance doesn't know about any of these. So what we need to do is before we actually do the two schema, we need to simply import those files so that our runtime actually imports those and causes the code to run, which triggers the builder applying those models to its instance. That all sounds confusing, even in my head. As I say, it sounds confusing, but it will make sense in a second. So we're going to import models, user, and then we're going to import models net. So now that these are imported, as this code is being run on your server, it's going to get to index TS. It's going to say, hey, we need the schema. It's going to go to this file. It's going to actually run the code in these imports because they've been imported. And now that's going to trigger this builder function and this Prisma object function to actually apply these things to your schema. And then it will generate the schema afterwards. So all of this chain of events is going to end up resulting in your GraphQL schema being complete and ready to go. And at this point, we don't really need to change anything because we're already applying the schema to our yoga instance. And we're setting up the server here. So I think the next piece here is- Yep. So the next piece is going to be just running the server and making sure that it actually works. If I go back to the code, there we go. And I just want to double-check this. Yeah.
14. Querying the GraphQL API
We've successfully queried our database and obtained the seed data. Our resolvers are set up correctly, ensuring type safety and a smoother developer experience. This concludes the server section, and we'll now move on to the front end where we'll query our GraphQL API. Feel free to continue with step three and complete your GraphQL schema. I'm available on Discord for any questions.
Okay. So if we go back to the code, I'm going to shut the server down just in case and I'll do npm-run-dev. This will start it back up on localhost. And at this point, I'll go in here and we should see- We now have this users query available. And I'm going to say that I want to grab some information from it. I'll grab the ID message and say, updated at. And what we should see here is that we're going to actually get some data. And we do. This is the seed data. So we are actually querying from our database now so that means our resolvers are set up correctly. And as you saw, as we were building it, it was all done in a type safe way where we probably couldn't have exposed any of these columns if they didn't exist. So this is going to make sure that you're doing your job right and it just makes the developer experience a little bit easier. So with that being said, it looks like- Let me pull this up. I think we're- Done with this section. Yeah. So we're done with the section. We're now pretty much done with our server piece. In the next two lessons that we're doing, we're going to be focused on the front end and actually querying this GraphQL API that we've built. So go ahead and go through this step three and build out the rest of your GraphQL schema. Once again, I'll be around to answer questions on the Discord.
15. Setting up urql and Writing GraphQL Query
In this section, we move from the server side to the front end project. We install urql and GraphQL to query our API. We instantiate a urql-client and provide it to our application using the provider component. We write a GraphQL query to select the ID, message, and Create App fields of each note, as well as the user's name and all of their notes.
All right, my friends, I am back and ready to get going on the next section. So like I said before, up until now, we have been working on the server side. We've been working through the database and building the GraphQL API. And now we're finally moving over to the front end project where we're going to be querying our API. So the section that we're going to go through right now is a little bit shorter. It's, well, I'll pop it open and read the goal so that we can look through this. But with this lesson, we're going to be putting our GraphQL server to use. We're going to hook it up to our API and grab some data. What we're going to do though is we're going to build this and we're going to set this up and then we're going to query the data in a way that's not very type safe. We get the type safety of GraphQL, but we don't quite get the full type safety that bridges the gap between your GraphQL API and your front end. And we're doing that on purpose in this section so that in the next section we can show what it would look like to be set up properly and what the developer experience should look like for a fully end-to-end type safe application. So we're gonna go through that. Once again, the setup is this will show you how to get up and running at the starting point for this lesson if you are just joining us or if you got stuck on something, so feel free to do that if you were. Otherwise we're going to jump right into the tasks. So I'll head back over to my code and at this point we can leave the server directory and I will enter the client directory. And because I didn't do so already I will, well, actually no. I will install modules in just a second. The first section here is saying that we are going to use urql which is a GraphQL client that allows us to actually query a GraphQL API pretty easily from your react application. So we're going to need to install that as well as GraphQL itself, that way, we can actually query our API and get some data to work with in our front end. So let's go ahead and do that. And then to the code, I'm going to do npm install urql and we're also going to install GraphQL. And so that should have installed those modules as well as all of the other modules in your node modules and in your package.json. Let me make sure, yep. So those are all installed and we should be good to go on to the next piece. And for now I'll close these and I'll head to the main.tsx file.
So in the next section, now that we have urql installed, we need to actually provide it to our application. So as this says here, we need to instantiate a urql-client, which is how we're going to query our GraphQL API. We need to set up where our GraphQL API is gonna be, which in our case is localhost 4000. And then urql actually gives us a React component called provider, which is gonna be a way for us to provide the client to the entire application so we can use it wherever we need it. So let's go through and set all of that up. Going to my code over here. And so we're gonna be in main.tsx and the very first thing that we're gonna need to do is actually import all of the imports that we need from urql. So the first thing is going to be create client and the provider. And now that we have those available to us, we can go ahead and instantiate a client. So we'll create a new constant called client and it's going to equal a create client function. The return of it, and what we need to give it here is the url. So this is going to be the url of our running GraphQL server. So url, and I will preface this with in a normal case, you probably want this in an environment variable because you don't quite know where your url may be. However, for now, we're just going to hardcode it because we're working locally and this is just a example project. So we're gonna do HTTP, local host, if I could spell this properly, and we are on port 4000, and the end point of our GraphQL server is GraphQL. Slash GraphQL. So that's going to be our client. This is our instantiated client, and this will allow us to query our GraphQL API, but to use this in our react application, we need to wrap our application in the provider. So we'll do provider, and the value of this provider will be our client, because that's what we're providing. And then, we need to move our app within the tag there. So there we go. At this point, we are now providing uRQL to our entire application. The next thing we need to do is actually write a GraphQL query, and the way we're gonna do this is we're gonna set up a GraphQL folder and write our queries and individual files just to keep them organized, and it's also gonna help us a little bit later on in the next section to add some of that extra type safety that I had talked about previously, so all we need to know for now, though, is that we need to create a new folder called GraphQL. We need to create a new file where we're gonna write our GraphQL query, and we want to select the ID, message, and Create App fields of each note, and we need a user's name and all of their notes. So let's go ahead and do all that. Also, if you see me regularly looking over here, it's because I have notes on this side, and they keep crashing, so they keep closing every time I switch tabs, but that's all right. It's a minor inconvenience. So now that we're in the source of our client directory, let's create a new folder called GraphQL. And as the instructions asked us, we'll do users.query.ts. This is gonna be our file name. And this is where we're gonna write our GraphQL query. So the first thing that we're gonna do here is we're going to import gql from URQL. This is a helper that's gonna allow you to write your GraphQL and get some syntax highlighting and some help there. And then what we need to do here is import, or sorry, export a constant called getUsers. This is gonna be the name of our query variable, and we'll use that helper here. So this gql allows us to write a GraphQL query string. So we're gonna write query. GetUsers, that's just what we're gonna call this query. And what we're gonna do with this query is we're gonna run the user's query that we built on our API. And if you remember going through the GraphQL playground in the previous section, we were running that user's query. That's exactly what we're doing here. And as the instruction said, we want our ID, we want the name, we want the... Oh, sorry, not the message. We want notes, that's what the call. And then with each note, what we need is a message and we need the created at.
16. Running and Observing the Query
We write a GraphQL query to select user IDs, names, and associated notes. We import the necessary hooks and query. The useQuery hook returns an array with the query results. We extract the data from the results and define the expected type for the data.users variable. We query the data and replace the static data with the actual response. We run the application and see the queried data displayed. We remove a field from the GraphQL query and observe the consequences. TypeScript doesn't complain about accessing the removed field. In the next section, we'll address this issue with more comprehensive end-to-end type safety.
So what this is gonna do now is when we actually apply this query and when we run it, it's gonna grab all of our users, it's gonna get their IDs names and it's gonna grab each note associated with those users and stick them in an array and each array index will have the notes message and the created at. So we're grabbing all this data, but currently we're not actually doing anything with this query, we've simply written it.
So now we need to actually run it and to do so we're gonna head over to app.tsx and then now that I've got that open... Excuse me. Well, now that I've got that open, let's go to task four over here. So we need to put it to use. We're gonna use the use query hook that's provided by URQL and that's what we're gonna pass our GraphQL query into and it's gonna use that to run our query. And then we are currently loading up a couple of static variables. We're putting some static data into the application just to avoid any syntax errors right here and we're gonna be replacing this with the actual query response. So let's go ahead and do that.
Very first thing is we need to import all of those pieces that I'd mentioned. So we're gonna import the use query from URQL and then we also need to import the query that we just wrote. So we're gonna import getUsers from that. So we've got our use query hook. We've got our GraphQL query. Now we can actually write our query. So we're gonna say const, and the way the use query hooks works is it's going to return an array and the first index of that array is gonna be our query results. So I'm gonna do a const and I'm gonna do structure and just pull out the first index of that array. And it's gonna be used query and the query that we wanna use is our users query. So right now this is gonna be okay. This is going to get our results and then we need to actually grab our data from the results because the results right now is like an HTTP response. So we're gonna say const data equals results. We're just pulling out the data key. And this is gonna give us a problem because the data.users variable is currently not typed. So we're not getting a nice typed response back from this query. We can actually change that by adding a generic parameter to our use query hook and we can say that we're expecting a users variable to come back and that users variable should be an array of users. And these users are defined right here manually by us. So this is where I mentioned that the type safety isn't quite end-to-end type safe yet. We still have this set of manually defined types on our front-end side that could easily get out of sync with our back-end. If our API changed at all, our front-end wouldn't know about it because these are just manually defined. But for now in this section, this is gonna be just fine. So now we're querying our data, we're grabbing the results and we've replaced our static data with that data. So if we go back, we can go ahead and actually run it. So I'll go here and I'll do npm run dev. And then I'm also gonna open a second terminal up and I'm going to cd into the server directory and I'm gonna run that as well. That way we have our API actually up and running so we'll be able to grab some data. And our frontend client is gonna be at localhost 3000. So I'll pop that guy open and here's what we're gonna see. This is each user, and each user has two notes. You can look in the seed file and all this data should match up with what we had in our seed file. And, yeah, so that's what we get here. And it looks like it's all working. But the next piece is the fun piece where we can see what the problem actually is with this way of doing it. Now we get to break it. So in our GraphQL query, let's go over here and we're going to remove this field, this created at field. So notice we're removing the field, however, in our types, we're still expecting created at to be there. And because we're telling TypeScript that there's a created at field, it assumes that there's a created at field. If it's missing from the actual data object, TypeScript doesn't know that. It just knows that there should be one so it can just pick it up. And so types are going to be and so it's going to pretend there is. If we look at the front end, again, really quick before we actually save this, we see the created at here is spit out for each note, but if we go in and save, it's going to reload the application and we're going to see not a number. That's because that created at field is no longer available. We're not querying it from our API. So it's not returned in our responses, but what you're going to see is if you go into your components, if you go into the message, right here we're accessing note.createdAt and TypeScript isn't complaining. That's because it's available here. TypeScript thinks it is. So this is where you get into an issue if you change your query at all, or if your API changes at all, TypeScript on your front end still thinks that everything's fine and dandy. It doesn't know that anything gone wrong. So in the next section, we're actually gonna fix that by applying more in-depth end-to-end type safety. But for now, go ahead and go through all of these steps that we just went through. I think that was the end of this section. Let me pull it back up just to double check. Yep, so that was the end of this. So I'll give you, if I look at the time, it's 1123, my time. So I'll give you, let's say, 10 minutes to go through this part. This was a little bit shorter and just get this up and running. And again, if you have any questions at all, please put them in the chat. I'm sitting here monitoring it, so I'd love to talk through any problems with you. And then once we get through with this part, we'll go through the last section where we'll set up the last piece of end-to-end type safety. So have fun with it.
17. Bridging the Gap with Code Generation
In this section, we're going to bridge the gap between the types we saw and the queries in our GraphQL API. We'll use code generation to validate our GraphQL queries and ensure that we're querying available fields. We'll install the GraphQL CodeGen packages, configure the code-gen.yml file, and generate types based on our GraphQL schema. This will allow us to represent user and note types in our front-end project using the generated types. We'll also set up a CodeGen script in our package.json to run GraphQL CodeGen. For detailed instructions, refer to the documentation.
And I will be back very shortly. All right, everybody. So that one was a little bit shorter. There was only a couple steps there. If you didn't quite get to finish it, feel free to finish up that step while we do number five. So after I sort of walk through this piece. But in this next section, what we're gonna be doing is what I call bridging the gap. And that gap is what I had mentioned previously where those types that we saw aren't quite aligned with your queries and with your GraphQL API. So we're gonna be bridging that gap so that those types are always in sync with whatever query you're actually running and with whatever's actually available through your GraphQL API. So let's go ahead and pop open the lesson. And just really quickly, ThomasSack94. I do see your question and once I get through this, I'll see what's going on there. I think I've seen that issue before so I might be able to help you there. But yeah, so the goal of this lesson is to put what's called code generation in place to validate your GraphQL queries. And what this is gonna do is it's going to actually read your GraphQL schema from your running API. It's going to take the GraphQL query that we wrote in our users.query.ts file on the front end, it's gonna parse that out and it's gonna ensure that you're actually querying fields that are available from your GraphQL API. So it's gonna basically validate that our query to our API is going to be a valid query. If it isn't, it's gonna give you an error and it's gonna say you need to fix this before you can run your front end. Otherwise it's going to say, okay, that's great. Now I'm going to generate a set of types based off of your GraphQL schema that you can then use in your front end to represent the user and note types rather than using our manually defined types. That sounds confusing, that's all right. We're gonna walk through all the steps right now. If you look over here at the tasks, the very first one is to install the CodeGen libraries. So there's a tool called GraphQL CodeGen and I'll pop this link open here just so we can see it. This is what the tool looks like. You're basically gonna have a GraphQL schema which we've built on our API. You will set up a configuration file and then it'll spit out a big types file that looks something like this. We're not gonna really need to read this at all, we might pop it open and take a look, but that's basically what this does. If you want more info on what's actually going on under the hood, check out this link and all of these docs should help you out. But the very first thing we're gonna do is install those packages. I'm just gonna copy them from here, that way I don't have to type out the whole thing. So within the client directory, I'm gonna install all of those packages. And once those finish up, which could take a little bit, we can get going on configuring this and getting it running. Oh cool, that went faster than I thought. So we've installed them, now we can configure it. So to configure this, what we're gonna need to do is create a new file called code-gen.yml. It's a YAML file. And we're going to use some of GraphQL code-gen's configuration options to configure this library. So let's see what this says. Oh cool, I was just double checking, making sure that those were still valid and accurate and they are. So code-gen.yml, that's going to be the file where we're going to add all of our configurations. And the configurations are a little bit difficult to understand simply because it is a YAML file and without reading the documentation, you won't really know what to put here. So I do recommend you pop open the documentation when you're going through this part of the lesson, but for now, I'll just walk through and explain what's going on. So very first, we need to tell GraphQL code-gen where our schema is, and that's going to be brw-source.http localhost 4000 GraphQL. This is just our GraphQL server that we have running locally. We also need to tell it which documents in your front-end project to read so that it knows which queries it should validate against that GraphQL schema. And in our case, that is our source GraphQL. And I'm going to do star.ts. So what that's going to say is look in the source directory, go into this GraphQL folder, and any TypeScript file, validate any GraphQL that's in that file. So that's what that piece does. And then finally, we need to tell GraphQL code-gen what it needs to generate so that we can use, basically, we're telling it which types should be generated into our front-end project so that we can use them in our project. So to do that, we can use the generates option here. And we're gonna say that we want to generate a file into our source directory and GraphQL directory. And then the file should be called generated.ts. So it's gonna generate a new file called generated.ts within this GraphQL directory. And what it's gonna generate is what we configure here. So we're gonna do plugins. And then we can specify all of the different plugins that we want to use to actually generate this. And what those plugins are is what we installed in the previous part here where we have Types Document Node, TypeScript, and TypeScript Operations. These are the three different plugins that we're gonna need to get these types that are based off of our GraphQL schema. So to do this, we're going to, pop my notes open again, we're gonna need to do the TypeScript plugin so that it knows we're generating TypeScript types. We need the TypeScript Operations, this sets up a couple of different operations for us in TypeScript that we're gonna need. And Typed Document Node, this sets up a object for us when it gets generated, that's basically an abstract syntax tree representation of the queries that we're running, which is basically just a big JSON object that represents our query. And this is what we're gonna pass into Urql to run our query. So, that's all configured and we can now move on to the next step. And I'll say once again, the docs are right here. Definitely read through the docs, there's a bunch of different options that you can use with Urql. And yeah, so that'll help you quite a bit. Now that we have that configured though, we need a way to actually run GraphQL CodeGen. And to do that, we're gonna add a CodeGen script to our package.json.
18. Setting up Code Generation
To set up code generation, add a new script called Codegen to your package.json file. This script will run the GraphQL-CodeGen command, which validates your GraphQL API and generates the necessary types. Make sure your API is running before running the Codegen script.
And that's pretty simple, so we'll go through that right now. In package.json, you're just gonna go to your scripts section, and you're gonna add a new script called Codegen. And all this script needs to do is run the command CodeGen. So, it's gonna be GraphQL-CodeGen. And this is gonna run the process that actually reaches out to your GraphQL API, does all the validation, and creates the types for you. And now that we've actually set that up, let's go ahead and run it. We have our API running. When you go through this on your own, do make sure that your GraphQL API is up and running, otherwise this will fail, because it needs to actually have something to reach out to. But mine is, so I'll do npm run Codegen.
19. Adjusting Types and Running the Project
We parsed the configuration file and generated our outputs. The generated.ts file contains TypeScript types representing our GraphQL schema and specific types based on our individual query. We replaced our queries with the generated objects, updated the used query hook, and removed the manually written type. TypeScript now warns us when our query changes and we need to adjust our types. We adjusted our types based on the response of the query and defined the model in the types.ts file. We imported the git users query type from the generated file and got rid of the manually written type definitions. We adjusted the note and user types based on the response of the query. These types dynamically change as our queries and GraphQL API change. We ran the project and saw the not a number error because we're still rendering the removed component.
And there does seem to be a problem, so I must have typed something wrong. See, implicit map keys need to be on a single line. So I think probably typical Yaml stuff, I might have miss typed this. Yeah, so I need a colon here. Oh, I see your comment, I think Blazeedge, I don't know how to say her name, but thank you. Yes, that was exactly the problem. So there we go. So it parsed our configuration file and it generated our outputs. And what we're gonna see is a new file here called generated.ts. And this is the result of all those different plugins that we specified. So it's generated a bunch of TypeScript types representing our whole GraphQL schema. It generated specific types based on our individual query that we're running. So this is gonna be typed to the actual fields that we selected out of the overall models that we get here. And then finally, the Git users document, which is a document representation of the query that we want to run. So it's not important to read through all of these types and constants in detail, just know that they were generated by GraphQL code gen so that we can use them in our queries and in our TypeScript definitions. But now that we have those, we can actually replace our queries with the generated objects. So what that means is going back to app.tsx and replacing a couple of things. So if we go into app.tsx, what we're going to get is we need to remove some of this stuff. And the first thing that we can remove is this Git users query. We don't need that anymore because we're gonna use the generated one for us. So I'll actually import that. It's gonna be the Git users document. This is the, like I said again, the abstract syntax tree representation of the query that we're running in this user's query here. So that's what that is. Next, what we need to do is we need to actually update where we're using the used query hook here. So what this is gonna do instead is it's going to take in the Git user's document. And then, now that we've done that, what we can do is we can remove this. That's the cool thing about using GraphQL Code Gen with URQL here is that it's gonna know based off of this Git user's document what the return type is. So now if we look at our data object, we can see it's a Git user's query response type. And then finally, we're not using this manually written type anymore. So we can get rid of that here. And there we go. I think that's good. And the next step is to adjust types. But before we move on to that, I wanna look at why we need to adjust our types. What we're gonna see now is a squiggly line here with an error and that's TypeScript telling us that, hey, the property created at is missing in the type, which is the response of this use query. That's exactly what we wanted. Previously, we weren't getting that error message and instead we were getting funky behavior on our front end. Now, TypeScript's actually gonna warn us when our query changes that something's wrong and we need to adjust our types. So that leads us to the next piece here, adjusting our types. So you'll see type errors because of the created at field. That's exactly what we wanted. Now we can use the generated types based off of the actual query that we're running to define what the model should look like in our types.ts file. So over here, this is where we're gonna need to change things. Very first thing that we're gonna need to do is import. And we're importing a type and the type is called git users query. And we're importing that from our generated file. So this is a type that represents the response of the actual query that we wrote. So it's only gonna take into account these fields, it's not gonna care about any other fields that are available in the GraphQL schema. It's catered directly to this schema. I emphasize that so much because this is really cool. This is what's missing and this is what bridges the gap between your front end and your back end and the actual queries that you're writing. This is what allows you to get that fully true end-to-end type safe behavior. So back in types.ts, now that we've imported that, we can actually get rid of these manually written type definitions. And instead, what we want to do is we want to define these types based off of the response of your query. So that's gonna look like this. We're gonna say, a note should be a getUsersQuery, we're gonna drill down into the user's response. And in typescript, you can actually drill down into the arrays to get their types like to get the types of the objects in the array. So we're gonna drill down into the first user. And then we're gonna say, we wanna go into the user's notes. And we want the type note to be the same as the type of the first note in that array. Just basically copying the shape of what a note would be from the response of our GraphQL query, which is gonna be just a message basically. So back in types.ts, now we're gonna do the exact same thing for a user, except instead, it's just gonna be the user. So we've now adjusted our types. These types are going to dynamically change as our queries change, and as our GraphQL API changes, it's going to be validated against that. And you'll get errors if something that you're querying for doesn't even exist in your GraphQL API. So now that looks good, we can actually run our project, npm run dev. And we can pop it open. And what we're getting is we're still seeing the not a number here. And that's because within our component, we are still actually rendering it out even though we've removed it.
20. Recap and Conclusion
In this part, we completed the setup of the full stack application. We built the database, performed migrations, and seeded data. Then, we created a GraphQL API and ensured type safety between the API and the database. We built the GraphQL schema and queried the API to reflect changes in queries and the API. This setup provides a nice developer experience, reducing stress when making changes. Thank you all for joining, and if you have any questions, you can reach me on Twitter or email. Feel free to host this workshop yourself using the provided Notion doc link. Giving workshops is my favorite part of my job, and I've learned a lot from the participants. I encourage you to give workshops and consider using this one.
So if we go into our components, we're gonna actually see this problem here now, which is good. And this is exactly what we want. In TypeScript, seeing errors is often a good thing because you know that your problems are being caught. So I'll just remove all of that since it doesn't matter to us anymore. We've removed it from the query and we should see everything nice and working. So that's it. That's basically the end of our setup of the full stack application.
All right, everybody. So it's coming up pretty close to the time where we got to wrap up here. So I'm gonna go ahead and just recap what we've done and give a little bit of context into the type safety here. So as we went through this, we built out our database. So we modeled out our data, we performed a migration, which created our database for us. And then we actually seeded our database with our data. And from there, we built a GraphQL API and we set up type safety between our GraphQL API and our database so that we can be sure that as we query our database that we're getting data that actually exists in our database. From there, we built out our GraphQL schema, which used that underlying infrastructure of the GraphQL API, to ensure that the data we exposed from our GraphQL API actually exists in our database in a type safe way. And then these last two sections that we went through are where we actually queried our GraphQL API and made sure that as our queries and as our API changes, the types represented or sorry, the types in our front end that represent those models are reflecting the changes in those queries and API. So there's a lot of little pieces there, but once you get it set up, this developer experience is super nice because as you update queries, as you change queries, as your API changes and you're developing on your front end, you can be sure that the data you're reaching for actually exists and that the queries that you're running are returning the data that you need to hydrate your API. So all of these pieces work together to give you a nice developer experience and gives you a little bit less stress as you're coding. You don't have to worry as much when you're changing a query or changing your API. That's something might break. So that's the general gist of this all and I wanna thank everybody for joining me and for sticking along.
I see there's still 29 of you so a lot of you stuck around for the long haul and I really appreciate that. This was super fun for me. Giving workshops is like my favorite part of my job. So thank you all for joining and for interacting with me in the chats. Like I said at the very beginning, if you wanna reach me or ask any questions, these are all of my contacts here. You can reach me on Twitter, that's where I'm most active or you can email me. Or on the Prisma Slack, we're actually revamping it and we're gonna have a bunch of community events going on there so if you wanna join that, you can reach me there as well. And the final thing that I wanna say is that if you wanna host this workshop yourself, I've given you the notion doc link. Please feel free to steal it. Please copy it. If you like talk at meetups at all, I give you full permission to just completely rip off this whole workshop and give it on your own. It's a very fun experience. You learn a lot. I've given this workshop like five times and I learned new stuff every single time because of you guys. All the issues that you run across helped me to learn better how to give this workshop. So I definitely encourage you to at least try to give a workshop and if you do, maybe consider doing this one, but thank you all so much and I hope you have a great rest of your day.
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet? There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more. Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used. You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported. The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials. React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up). Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too. Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step. (Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
Have you ever thought about building something that doesn't require a lot of boilerplate with a tiny bundle size? In this workshop, Scott Spence will go from hello world to covering routing and using endpoints in SvelteKit. You'll set up a backend GraphQL API then use GraphQL queries with SvelteKit to display the GraphQL API data. You'll build a fast secure project that uses SvelteKit's features, then deploy it as a fully static site. This course is for the Svelte curious who haven't had extensive experience with SvelteKit and want a deeper understanding of how to use it in practical applications. Table of contents: - Kick-off and Svelte introduction - Initialise frontend project - Tour of the SvelteKit skeleton project - Configure backend project - Query Data with GraphQL - Fetching data to the frontend with GraphQL - Styling - Svelte directives - Routing in SvelteKit - Endpoints in SvelteKit - Deploying to Netlify - Navigation - Mutations in GraphCMS - Sending GraphQL Mutations via SvelteKit - Q & A
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock. In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
If you’re building a dashboard, analytics platform, or any web app where you need to give your users insight into their data, you need beautiful, custom, interactive data visualizations in your React app. But building visualizations hand with a low-level library like D3 can be a huge headache, involving lots of wheel-reinventing. In this talk, we’ll see how data viz development can get so much easier thanks to tools like Plot, a high-level dataviz library for quick & easy charting, and Observable, a reactive dataviz prototyping environment, both from the creator of D3. Through live coding examples we’ll explore how React refs let us delegate DOM manipulation for our data visualizations, and how Observable’s embedding functionality lets us easily repurpose community-built visualizations for our own data & use cases. By the end of this talk we’ll know how to get a beautiful, customized, interactive data visualization into our apps with a fraction of the time & effort!
React 18! Concurrent features! You might’ve already tried the new APIs like useTransition, or you might’ve just heard of them. But do you know how React 18 achieves the performance wins it brings with itself? In this talk, let’s peek under the hood of React 18’s performance features: - How React 18 lowers the time your page stays frozen (aka TBT) - What exactly happens in the main thread when you run useTransition() - What’s the catch with the improvements (there’s no free cake!), and why Vue.js and Preact straight refused to ship anything similar