Back to our favourite destination: the schema file!
Let's find the Listing type and add those three new fields along with descriptions.
listings.graphql
typeListing{
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 @connectdirective 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 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, and closedForBooking.
listings.graphql
typeQuery{
"A curated array of listings to feature on the homepage"
We can now add the new fields to our existing operation.
GetFeaturedListings operation
queryGetFeaturedListings{
featuredListings{
id
title
numOfBeds
costPerNight
closedForBooking
}
}
Run the operation 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
Hmm, looking closely at the closedForBookingfield 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:4000
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
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
Let'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 GraphQLcamelCase convention.
Let's update our selection with the closedForBookingfield mapped to the closedForBookings property.
"A curated array of listings to feature on the homepage"
featuredListings:[Listing!]!
@connect(
source:"listings"
http:{GET:"/featured-listings"}
selection:"""
id
title
numOfBeds
costPerNight
closedForBooking: closedForBookings
"""
)
}
"A particular intergalactic location available for booking"
typeListing{
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
}
Checking our work—part two!
Saving our changes again and hopping back over to Sandbox, let's re-run that GetFeaturedListingsoperation.
queryGetFeaturedListings{
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
{
"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
}
]
}
}
Task!
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!
Lesson tasks
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
typeQuery{
randomProduct:Product
@connect(
source:"ecomm"
http:{baseURL:"/random"}
selection:"""
# TODO
"""
)
}
typeProduct{
"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 GraphQLfields 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 GraphQLfield 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.
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.
An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
GraphQL
An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.
field
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
directive
A GraphQL annotation for a schema or operation that customizes request execution. Prefixed with @ and may include arguments. For example, the @lowerCase directive below can define logic to return the username field in lowercase:
typeUser{
username:String!@lowerCase
}
router
The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
operation
A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.
operation
A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.
field
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
field
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
GraphQL schema
A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.
field
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
alias
A mechanism for including multiple instances of a single field in a GraphQL operation. Aliases enable you to provide different argument values to each instance.
In the following example, admins and managers are both aliases of the users field:
queryAdminsAndManagers{
admins:users(role:"admin"){
id
firstname
lastname
}
managers:users(role:"manager"){
id
firstname
lastname
}
}
field
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
GraphQL
An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.
field
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
operation
A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.
fields
A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.
typeAuthor{
# id, firstName, and lastName are all fields of the Author type
id:Int!
firstName:String
lastName:String
}
Rover
Apollo's command-line interface for managing and maintaining graphs with GraphOS.
router
The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.