Overview
We're close to finishing up the data needed for the listing details page. Let's go ahead and add the amenities!
In this lesson, we will:
- Introduce the
Amenity
type to our schema - Build queries to display data for a listing and all of its amenities
Building the Amenity
type
As we learned in the lesson on SDL syntax, fields on GraphQL types don't have to return a basic scalar type—they can also return other object types!
For instance, we can give our Listing
type an amenities
field—but what does this field actually return?
type Listing {id: ID!title: String!description: String!numOfBeds: Int!costPerNight: Float!closedForBookings: Booleanamenities: # What type should this be?}
Putting our business glasses on, we can see how more detailed data about an amenity—such as its id, name, what category it belongs to—would come in handy. The better or more descriptive the amenities a listing has to offer, the more motivated users might feel to make a booking! Furthermore, several listings might have the same kind of amenities; unlike an id
or a title
, a listing's amenities might not be unique to the listing itself!
For these reasons, we need to think of an "amenity" as a standalone entity—in other words, we should make it its own GraphQL type called Amenity
.
This means that our Listing
type should be updated: we need its amenities
field to return a list of Amenity
types!
Update your Listing
type with the amenities
description and field highlighted below.
type Listing {id: ID!title: String!description: String!numOfBeds: Int!costPerNight: Float!closedForBookings: Boolean"The amenities available for this listing"amenities: [Amenity!]!}
Now, let's actually define what an Amenity
looks like. From our REST API responses, we know we can start with just a few properties: id
, name
, and category
. In the schema.graphql
file, add the new Amenity
type shown below:
type Amenity {id: ID!"The amenity category the amenity belongs to"category: String!"The amenity's name"name: String!}
And from the schema's perspective, our work is done!
After saving our changes, the codegen process should have run automatically. We can check types.ts
to make sure that our new Amenity
type has been added!
export type Amenity = {__typename?: "Amenity";/** The amenity category the amenity belongs to */category: Scalars["String"]["output"];id: Scalars["ID"]["output"];/** The amenity's name */name: Scalars["String"]["output"];};
Testing the Amenity
type
Jump back into the Explorer. We'll try running a query that calls for a listing's amenities
details.
query GetListing($listingId: ID!) {listing(id: $listingId) {titlenumOfBedsamenities {namecategory}}}
And make sure that in the Variables panel, our $listingId
variable is still set.
{ "listingId": "listing-1" }
And when we run the query... amenity data for our listing!
An alternate path
Now what about the featuredListings
path? It's another entry point to our schema that returns a list of Listing
types, for which we could then request the amenities
field. Let's try it out.
query GetFeaturedListings {featuredListings {idtitledescriptionamenities {idnamecategory}}}
Uh-oh!
An error appears in the Response panel rather than the data we want. But what's the problem?
"Cannot return null for non-nullable field Amenity.name."
We were able to return amenities
data for a single listing, but something's going wrong when we try to include it for each of our featured listings.
Key takeaways
- An object type's fields can return scalar types or other object types.
- When a field on an object type returns another object type, we can write complex queries that traverse from one object to another—no follow-up queries necessary!
Up next
Let's investigate the source of that error and fix it.
Share your questions and comments about this lesson
This course is currently in
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.