7. Connecting to object fields
2m

Overview

There's an important aspect of we haven't yet covered in our journey through the connectors world: the relationships between types! Let's set our sights on bringing a listing's amenities into the equation.

In this lesson, we will:

  • Learn how to use connectors on an 's s
  • Learn how to access an 's to use inside a connector

A listing's amenities

In the listing details page, we're still missing a big chunk of data about a listing's amenities.

Airlock listing amenities

This data will be provided by the listings/:id/amenities endpoint from our Listings REST API. We can examine the response from that endpoint here: https://rt-airlock-services-listing.herokuapp.com/listings/listing-9/amenities

Adding to the schema

To implement this in the schema, we'll first need to add a new called Amenity. We'll give it three :

  • id of type ID!
  • category of type String!
  • name of type String!
  1. Open up the listings.graphql schema file and add the Amenity type.

  2. Add the three new along with descriptions.

    listings.graphql
    type Amenity {
    id: ID!
    "The amenity category the amenity belongs to"
    category: String!
    "The amenity's name"
    name: String!
    }
  3. Next, we'll add the amenities to the Listing type, which will return a list of Amenity types.

    listings.graphql
    type Listing {
    # ...other Listing fields
    "The amenities available for this listing"
    amenities: [Amenity!]!
    }
  4. When we save our changes, rover dev will restart automatically. And of course, we get errors! But don't worry, they should look pretty familiar...

    error[E029]: Encountered 4 build errors while trying to build a supergraph.
    Caused by:
    CONNECTORS_UNRESOLVED_FIELD: No connector resolves field `Listing.amenities`. It must have a `@connect` directive or appear in `@connect(selection:)`.
    CONNECTORS_UNRESOLVED_FIELD: No connector resolves field `Amenity.id`. It must have a `@connect` directive or appear in `@connect(selection:)`.
    CONNECTORS_UNRESOLVED_FIELD: No connector resolves field `Amenity.category`. It must have a `@connect` directive or appear in `@connect(selection:)`.
    CONNECTORS_UNRESOLVED_FIELD: No connector resolves field `Amenity.name`. It must have a `@connect` directive or appear in `@connect(selection:)`.

It's connector time!

Adding a connector on an object type's fields

This time, we're not adding a connector to a root type (like Query). Instead, we'll add it to a on an object type. In this case, the Listing.amenities !

  1. In the schema, let's jump back to where we added the amenities on the Listing type. We'll add the @connect .

    listings.graphql
    type Listing {
    # ...other Listing fields
    amenities: [Amenity!]!
    @connect(
    source: ""
    http: { GET: "" }
    selection: """
    """
    )
    }
  2. We can define the source the same as before: v1.

    listings.graphql
    source: "v1"
  3. Next up, http.GET. We have an endpoint to retrieving a listing's amenities details, given a listing ID, so let's start with that!

    listings.graphql
    http: { GET: "/listings/{???}/amenities" }

Now all we need is the listing's id to complete the endpoint path—but unlike the connector we defined for the listing(id: ID!) in the last lesson, we won't get the listing's id on the 's arguments.

Instead, we'll need to access the id on the actual Listing type that we're resolving amenities for! Let's look at how we do this.

type Listing {
id: ID! # ⬅️ Here's the value we actually need!
"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)"
closed: Boolean
"The amenities available for this listing"
amenities: [Amenity!]!
@connect(
source: "v1"
http: { GET: "/listings/{???}/amenities" } # ⬅️ Here's where we need it to end up!
)
}

Accessing an object type's fields

We can access an object's values using the $this .

Example schema
type SpaceCat {
id: ID!
name: String!
code: String!
}

For example, in the schema above, we can access the 's values in a connector using $this.id, $this.name and $this.code.

Just like before, we can wrap it in curly brackets { } to interpolate it into the HTTP path in our connector.

Let's give it a spin!

  1. Jumping back into the listings.graphql file where we left off with our amenities connector.

    listings.graphql
    amenities: [Amenity!]!
    @connect(
    source: "v1"
    http: { GET: "/listings/{???}/amenities" }
    selection: """
    """
    )
  2. We can replace the ??? with $this.id referring to the Listing type's id .

    listings.graphql
    type Listing {
    id: ID!
    # ... other Listing fields
    amenities: [Amenity!]!
    @connect(
    source: "v1"
    http: { GET: "/listings/{$this.id}/amenities" }
    selection: """
    """
    )
  3. Amazing! Last thing—we have to fill in our selection mapping. We can visit the endpoint to examine the response and determine that the mapping is one-to-one: the properties of the response are named the same way as the we've defined in the .

    listings.graphql
    selection: """
    id
    name
    category
    """

We're good to go!

Check your work

The exciting part! Let's jump back to http://localhost:4000 where the local is running.

We'll build an to retrieve a specific listing, its id, title and a list of amenities. For each amenity, we want the id, name and category.

query GetListingAmenities($listingId: ID!) {
listing(id: $listingId) {
id
title
amenities {
id
name
category
}
}
}

Don't forget to define a listingId in the Variables section:

{
"listingId": "listing-9"
}

Let's run the ... awesome! We've got the same listing from before, but this time, with amenity data!

Practice

Answer the next question using the schema below:

Example schema - bookings subgraph
type Booking {
id: ID!
checkInDate: String!
checkOutDate: String!
status: BookingStatus!
hostReview: Review
@connect(
source: "v1"
http: { GET: "/review?bookingId={???}" }
selection: """
id
text
rating
"""
)
}
Which of the following should replace ??? in the schema?
Which of the following are valid locations to apply the @connect directive? (Select all that apply)

Key takeaways

  • When a of one returns another , we can apply the @connect to define the specific method to retrieve that data from our source.
  • When a particular 's connector requires the value of a different (on the same ), we can access it within the connector using $this.

Up next

In the next lesson, we'll dive into the and see how we can optimize our schema to make one less network call.

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. 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.