How to Modify GraphQL Type Definitions to Support TypeScript

In this lesson, we'll modify the User, Listing, and Booking GraphQL type definitions that are relevant to the user information we want to query from the database.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two

With our UserQuery field established, let's set up the user type definitions and resolve our function in a way that we expect to resolve and query the user information that we want. First, we'll look to have a User Object type in our GraphQL schema that represents the fields we expect can be queried for a certain user. We'll review our existing TypeScript definition of the user interface we've created when we set up the TypeScript types for our database documents. Our GraphQL schema definition of the user object type will be very similar to what we have here. We'll want to query the ID, name, avatar, contact, and the listings and book ings a user is to have. A few of the differences, however, is in the user object type for our GraphQL schema will exist in ID field, not an underscore ID field. There won't be a token field. Token is a field in the document that is used for login session information which we learned in lesson 5.3. We'll never need to return this field in our User GraphQL endpoints. We'll have a has a wallet field instead of wallet ID because wallet ID will be the actual wallet ID from Stripe when the user actually authenticates with Stripe. We'll see this in a later lesson, but we don't want to pass this sensitive information to the client. The client will only need to know if a user has a wallet or not, with which we 'll be able to know whether a user is signed in with a Stripe or not. The client won't need to know the actual Stripe ID of a certain user. Bookings will return an array of booking objects, listings will return an array of listing objects, but these two particular fields will be paginated, with which we'll see shortly. Let's create this user object type in our type definitions file. We'll create a user type above our existing viewer type. We'll say it is to have an ID field of type GraphQL ID, name, avatar and contact fields will exist to be of type strings. Has wallet will be a boolean, income will be an integer, bookings is to return a booking object type and the listings field will return a listings object type. Let's address a few interesting things we did here and what we'll need to do. First of all, we've stated all fields within the user object type are to be none null fields except for the income and bookings information. When we query for a user from the client, we'll expect to receive values for most of these fields except for income and bookings. This is because income and bookings are the two protected fields where we'll only allow these fields to be resolved to their intended values when a user is querying their own user information. We consider the bookings a user is to make to be sensitive information. We wouldn't want another person to query for a certain user, for example myself and know the bookings I've made and this is the same for income. Now they could be null values because the way we intend to handle it is to check the viewer ID making the request and to see if the viewer ID matches the user ID being queried. If they do, we'll return the intended values, if not, we'll return null values. Another thing we mentioned is that bookings and listings are to be paginated fields. Pagination is the process of dividing a large set of data into smaller discrete chunks or pages. We're going to address how we handle pagination when we build our resolver functions, but the client is going to be able to pass two arguments to these two fields to determine the content and the number of documents that are to be paginated. They'll pass a limit field and a page field of both to be defined integer types . Limit will dictate the amount or limit of data that is to be queried for a certain page and page will reference the chunk or page of data being queried. So for example, a limit of 10 and a page of 1 will return the first 10 documents in a collection. Page of 2 with a limit of 10 will return the next 10 documents and so on. We'll get a better understanding of this when we build our resolver functions. Since bookings and listings are custom object types, we'll need to create what they are. We've said that we want the bookings and the listings field in our user object type to return lists, but we haven't defined a GraphQL list. This is because the bookings and listings objects are to contain two fields each. The bookings object type will have a total field to reference the total amount of objects that can be queried with which our client will be able to use and it will contain a result field which is to be the GraphQL list that is to be returned for a certain page . We'll say that it is to be a defined list of booking object types. The listings object type will be very similar. It'll have a total integer field and a result field which is to be a GraphQL list of listing object types which is returned for a certain page. Now we'll need to create the booking and listing GraphQL object types. Let's first take a look at the listing interface that resembles a listing document in the type definitions file we've set up. We'll have our GraphQL object type look very similar to this with a few exceptions. We'll have an ID field instead of underscore ID. Our client will not need the country and admin fields from our document. Books will return an array of booking objects which will be paginated. Bookings here refer to the bookings made for a certain listing from many different users. Bookings index is defined as a key value pair in our TypeScript definitions however GraphQL doesn't have a type for this. Instead what we'll do is simply stringify a booking index object in our res olver function and have it returned as a string when queried. Let's create this listing object type in our GraphQL schema. The ID will be of type ID, title, description and image will be of type string. The host field would be of the user object type. In our TypeScript definitions file host is a reference to the ID of the particular user but in our field we'll have it actually resolved to the particular object. Type will reference an enum we'll create called listing type. Address and city would be strings. Bookings will be a paginated field that will return the bookings data object we 've already specified. Bookings index will be a string and price and number of guests will be integers . We'll create the enum listing type in our GraphQL schema to reference the two different listing type properties, apartment and house and as good practice we'll have our enum values here in capital letters. We'll create the booking type that references a booking object within the list of bookings. If we take a look at the interface that represents the shape of a booking document, the booking object type will create to be very similar to this except for having an ID field instead of underscore ID. The listing field will resolve to a listing object and tenant in our schema as well will resolve to the user object. So let's go ahead and create this schema object type. We'll say that IDs of type GraphQL ID, the type of the listing field will be the listing object type we've created, the type of the tenant field will be the user object type we've created and check in and check out would be strings. Finally, we can update our query user field as well. The user field will take an ID input of type ID and return a user object from this ID when resolved. And this is where we'll stop for this lesson. Here's a quick summary of what we've just done. We've said the user query entry point field will accept an ID argument and return a user object when resolved. Notice how we haven't used an input object type here for the argument. We like to use input object types for arguments in our mutation fields. For queries that expect a few number of arguments we prefer to declare them as is. This user object will contain a series of fields. The bookings and listings fields will return the bookings and listings object types respectfully that we've created as well. These object types return a total field and a GraphQL list field. The GraphQL lists being returned are either a list of booking or listing object types respectfully. Booking and listing refer to the single object or in other words the single document we query from the database and these are the fields we expect to be queried. Now as a note, not all the fields we defined here are going to be used when we actually build the user page. A lot of the fields we've defined are going to be used when we build the other pages in our UI. For example, a lot of the fields defined in the listing object type will be utilized when we actually build out the listing page for a single listing. We'll address the fields here again when we actually get to that point. In the next lesson we'll continue what we've done by building out the resolver functions within our user resolvers map. [ Silence ]