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
}
]
}
}
Though the data did come back successfully, it looks like we have a problem. Let's dig into this by clicking on the error icon, which opens up Explorer's Connectors Debugger.
http://localhost:4000
Debugging mapping errors
The Connectors Debugger lists all the Connector calls happening under the hood, giving us insight into how our data is fetched, and what might have gone wrong.
We can see that our call to GET /featured-listings has one mapping problem, which we can click on to find out more.
http://localhost:4000
For this specific request, the error message says: Property .closedForBooking not found in object, and that's happening in the selection parameter in the Connector.
http://localhost:4000
We can click on the </> icon to open up the Connectors Mapping Playground, another useful tool for writing Connectors.
http://localhost:4000
It shows our API response on the left, our Connector selection mapping in the middle, and the results of that mapping on the right. You can also use the embedded version below:
http://localhost:4000
We can immediately see some squiggly lines indicating something's wrong, and when we hover over it, we get the same error message.
http://localhost:4000
Looking closely at the properties available in the response object, we've actually got a little typo in our selection. It's bookings plural, with an "s". In our schema and our selection, we've named it booking, singular, without the "s".
We could fix the naming to be the same as the response, but what if we wanted to go with our singular 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.
selection mapping
The process of mapping API response data to GraphQL schema fields and types when using Apollo Connectors. Selection mapping occurs within the @connect directive's selection field.
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.