Overview
Airlock's featured listings need a little more than the id
and title
.
In this lesson, we will:
- Add new fields to the schema
- Use the Connectors Debugger to examine mapping errors
- Learn how to rename properties that don't match the schema
Adding new fields
Let's add three more fields:
- the number of beds available
- the cost per night
- whether or not the listing is currently closed
These are properties that are already available from our endpoint: https://airlock-listings.demo-api.apollo.dev/featured-listings
[{"id": "listing-1","title": "Cave campsite in snowy MoundiiX","numOfBeds": 2,"costPerNight": 120,"closedForBookings": false,"amenities": [{"id": "am-2"},{"id": "am-10"},{"id": "am-11"}// more amenities]}// more featured listings]
Updating the schema
Back to our favourite destination: the schema file!
Let's find the
Listing
type and add those three new fields along with descriptions.listings.graphqltype Listing {id: ID!"The listing's title"title: String!"The number of beds available"numOfBeds: Int"The cost per night"costPerNight: Float"Indicates whether listing is closed for bookings (on hiatus)"closedForBooking: Boolean}When we save the file,
rover dev
will restart automatically, always watching for new changes. Uh-oh, we've got errors. These look familiar! The error also gives a helpful hint: "must have a@connect
directive or appear in@connect(selection:)
".Rover errorserror[E029]: Encountered 3 build errors while trying to build a supergraph.Caused by:CONNECTORS_UNRESOLVED_FIELD: [listings] No connector resolves field `Listing.numOfBeds`.It must have a `@connect` directive or appear in `@connect(selection:)`.CONNECTORS_UNRESOLVED_FIELD: [listings] No connector resolves field `Listing.costPerNight`.It must have a `@connect` directive or appear in `@connect(selection:)`.CONNECTORS_UNRESOLVED_FIELD: [listings] No connector resolves field `Listing.closedForBooking`.It must have a `@connect` directive or appear in `@connect(selection:)`.The router needs to know where these new fields are coming from. Do we need to make another network call to retrieve a listing's number of beds? Or is that data available in the response from our connector? We already know it's the latter!
In the connector, let's add:
numOfBeds
,costPerNight
, andclosedForBooking
.listings.graphqltype Query {"A curated array of listings to feature on the homepage"featuredListings: [Listing!]!@connect(source: "v1"http: { GET: "/featured-listings" }selection: """idtitlenumOfBedscostPerNightclosedForBooking""")}
Checking our work
Let's save our changes, happy to see those errors go away, and jump back over to Sandbox.
We can now add the new fields to our existing operation.
query GetFeaturedListings {featuredListings {idtitlenumOfBedscostPerNightclosedForBooking}}
Run the operation and we get more data back!
{"data": {"featuredListings": [{"id": "listing-1","title": "Cave campsite in snowy MoundiiX","costPerNight": 120,"numOfBeds": 2,"closedForBooking": null},{"id": "listing-2","title": "Cozy yurt in Mraza","costPerNight": 592,"numOfBeds": 1,"closedForBooking": null},{"id": "listing-3","title": "Repurposed mid century aircraft in Kessail","costPerNight": 313,"numOfBeds": 5,"closedForBooking": null}]}}
Hmm, looking closely at the closedForBooking
field though... we seem to be getting null
for all the listings. null
is valid since the field is nullable, but the JSON response was returning true
and false
values... so let's dig into this!
Debugging mapping errors
To find out more about what's happening with each network call under the hood and debug any connector errors, we can use Explorer's Connectors Debugger.
We can find it in the dropdown when we click the arrow beside Response. (Note: If you don't see it, try refreshing the page!)
http://localhost:4000We can see that our call to
GET /featured-listings
has one mapping error. Let's click on it to find out more.http://localhost:4000This opens the Mapping tab for the specific request we made, which shows more details like the error message:
"Property .closedForBooking not found in object"
.http://localhost:4000Let's investigate by clicking over to the Response body tab.
http://localhost:4000
And it's true, we've actually got a little typo in our selection. It's bookings plural, with an "s". We could fix this in our schema, but what if we wanted to go with our version instead?
Mapping JSON properties to GraphQL fields
When a response object property doesn't quite match what we want to name it in our GraphQL schema, we'll need to take one extra step to map it.
First we define how we named the field in our schema, followed by a colon (:
), then the name of the property from the JSON response. It's like providing an alias for the value, a new name!
selection: """fieldName: jsonName"""
This is helpful for situations where we want to choose a different, usually clearer name for a field, or if the REST API snake_case
convention might not match GraphQL camelCase
convention.
- Let's update our selection with the
closedForBooking
field mapped to theclosedForBookings
property.
selection: """idtitlenumOfBedscostPerNightclosedForBooking: closedForBookings"""
Checking our work—part two!
Saving our changes again and hopping back over to Sandbox, let's re-run that GetFeaturedListings
operation.
query GetFeaturedListings {featuredListings {idtitlenumOfBedscostPerNightclosedForBooking}}
No errors this time! Checking the Response, we can see true
and false
values for closedForBooking
. Much better!
{"data": {"featuredListings": [{"id": "listing-1","title": "Cave campsite in snowy MoundiiX","costPerNight": 120,"numOfBeds": 2,"closedForBooking": false},{"id": "listing-2","title": "Cozy yurt in Mraza","costPerNight": 592,"numOfBeds": 1,"closedForBooking": true},{"id": "listing-3","title": "Repurposed mid century aircraft in Kessail","costPerNight": 313,"numOfBeds": 5,"closedForBooking": false}]}}
The pattern for building connectors
That's our first connector all done! From here on out, we can repeat this pattern for any new connectors we want to build. Let's recap those steps:
- Start by adding to our schema: the types and fields that represent our API.
- Identify which data source, or REST API endpoint we need to retrieve that data.
- Using
@connect
, we build a connector to configure the request details and the response mapping.
Rover takes care of running the router, and we can use Sandbox Explorer to send and debug our calls!
Practice
selection
that is NOT defined in the schema? (For example, try adding latitude
to the selection
.)Use the JSON response and schema below to answer the next question.
{"id": "product-xyz""description": "A high-quality replica of a space helmet used by astronauts."}
type Query {randomProduct: Product@connect(source: "ecomm"http: { baseURL: "/random" }selection: """# TODO""")}type Product {"The at-a-glance description for a product"tagline: String!}
selection
mappings would result in no mapping errors?Key takeaways
- To troubleshoot why our GraphQL fields are not populating the right values from the REST API, we can use the Connectors Debugger. This tool lets us take a deeper look at each network call to see where any errors might have occurred.
- When there's a name mismatch between our GraphQL field name and JSON property, we can use the
selection
parameter to map them manually. To do this, we use the syntaxfieldName: jsonName
.
Up next
Let's continue to put this process into practice throughout the course as we sprinkle in some new concepts along the way.
Share your questions and comments about this lesson
Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.