6. Debugging connectors
4m

Overview

Airlock's featured listings need a little more than the id and title.

In this lesson, we will:

  • Add new 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

Airlock homepage marked up

Let's add three more :

  • 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

REST API response
[
{
"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!

  1. Let's find the Listing type and add those three new along with descriptions.

    listings.graphql
    type 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
    }
  2. 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 or appear in @connect(selection:)".

    Rover errors
    error[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 needs to know where these new 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!

  3. In the connector, let's add: numOfBeds, costPerNight, and closedForBooking.

    listings.graphql
    type Query {
    "A curated array of listings to feature on the homepage"
    featuredListings: [Listing!]!
    @connect(
    source: "v1"
    http: { GET: "/featured-listings" }
    selection: """
    id
    title
    numOfBeds
    costPerNight
    closedForBooking
    """
    )
    }

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 to our existing .

GetFeaturedListings operation
query GetFeaturedListings {
featuredListings {
id
title
numOfBeds
costPerNight
closedForBooking
}
}

Run the and we get more data back!

Response
{
"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
}
]
}
}
http://localhost:4000

Response null

Hmm, looking closely at the closedForBooking though... we seem to be getting null for all the listings. null is valid since the 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.

  1. 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:4000

    Connectors Debugger

  2. We can see that our call to GET /featured-listings has one mapping error. Let's click on it to find out more.

    http://localhost:4000

    Connectors Debugger

  3. This 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:4000

    Connectors Debugger

  4. Let's investigate by clicking over to the Response body tab.

    http://localhost:4000

    Connectors Debugger

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 , we'll need to take one extra step to map it.

First we define how we named the in our schema, followed by a colon (:), then the name of the property from the JSON response. It's like providing an 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 , or if the REST API snake_case convention might not match camelCase convention.

  1. Let's update our selection with the closedForBooking mapped to the closedForBookings property.
listings.graphql
selection: """
id
title
numOfBeds
costPerNight
closedForBooking: closedForBookings
"""

Checking our work—part two!

Saving our changes again and hopping back over to Sandbox, let's re-run that GetFeaturedListings .

query GetFeaturedListings {
featuredListings {
id
title
numOfBeds
costPerNight
closedForBooking
}
}

No errors this time! Checking the Response, we can see true and false values for closedForBooking. Much better!

http://localhost:4000

Response correct

Response
{
"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:

  1. Start by adding to our schema: the types and that represent our API.
  2. Identify which data source, or REST API endpoint we need to retrieve that data.
  3. Using @connect, we build a connector to configure the request details and the response mapping.

takes care of running the , and we can use Sandbox Explorer to send and debug our calls!

Practice

What happens when you include a field in the selection that is NOT defined in the schema? (For example, try adding latitude to the selection.)
Which tab in the Connectors Debugger panel shows you errors if there are discrepancies in your selection and your schema?

Use the JSON response and schema below to answer the next question.

REST API response
{
"id": "product-xyz"
"description": "A high-quality replica of a space helmet used by astronauts."
}
Schema
type Query {
randomProduct: Product
@connect(
source: "ecomm"
http: { baseURL: "/random" }
selection: """
# TODO
"""
)
}
type Product {
"The at-a-glance description for a product"
tagline: String!
}
Given the JSON response and the schema above, which of the following selection mappings would result in no mapping errors?

Key takeaways

  • To troubleshoot why our 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 name and JSON property, we can use the selection parameter to map them manually. To do this, we use the syntax fieldName: 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.

Previous

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.