13. Contributing to an entity
5m

Overview

In the previous lesson, we just saw how subgraphs can reference an entity as a 's return type. Now, let's take a look at how subgraphs can contribute s to an entity.

In this lesson, we will:

  • Learn how multiple subgraphs can contribute fields to an entity
  • Update the Location entity in our reviews subgraph schema by contributing the reviewsForLocation and overallRating fields
The FlyBy schema diagram, with pencil icons next to the Location.reviews and Location.overallRating fields

✏️ Contributing fields

Remembering our FlyBy UI, we know it needs to fetch each location's overallRating, along with a list of its reviewsForLocation:

A query containing fields that will be populated by our two subgraphs, locations and reviews

By following the separation of concerns principle, it makes sense that any data about ratings or reviews is populated by the reviews subgraph, so let's head on over there and make those additions!

  1. Open up the subgraph-reviews/reviews.graphql file.

  2. Find the Location entity definition in the . We previously defined this as a stub of the Location type.

    subgraph-reviews/reviews.graphql
    type Location @key(fields: "id", resolvable: false) {
    id: ID!
    }

    By default, a subgraph should only contribute s that aren't defined by other subgraphs, with the exception of the primary key . This means that because the locations subgraph defines name, description, and photo as s for the Location type, we won't - and shouldn't - define those s here in the reviews subgraph!

    Note: You can override the default behavior explained above to allow multiple subgraphs to resolve the same field by applying either the @shareable or @provides . This is an optional performance optimization that can instruct the on how to plan the execution of a query across as few subgraphs as possible.

    Because we now want the reviews subgraph to contribute new s to the Location definition, the first thing we need to do is remove the resolvable: false property from the @key . This will enable our reviews subgraph to define and resolve its own Location s.

  3. Remove the resolvable: false property from the Location type's @key .

    subgraph-reviews/reviews.graphql
    type Location @key(fields: "id") {
    id: ID!
    }
Task!

✏️ Adding new fields to the Location entity

Now we're ready to add the two new s.

  • the overallRating field, which returns a Float
  • the reviewsForLocation field, which returns a non-null list of Review objects.

We'll also add descriptions to these s so we can quickly see what they represent.

subgraph-reviews/reviews.graphql
type Location @key(fields: "id") {
id: ID!
"The calculated overall rating based on all reviews"
overallRating: Float
"All submitted reviews about this location"
reviewsForLocation: [Review]!
}

✏️ Adding resolvers

Each of these s needs a function to return data, so let's take care of that next.

  1. Open the resolvers.js file in the subgraph-reviews directory.

  2. Add a Location entry to the resolvers map. We'll also add two empty functions for the overallRating and reviewsForLocation s.

    subgraph-reviews/resolvers.js
    const resolvers = {
    Query: {
    // ...
    },
    Location: {
    overallRating: () => {},
    reviewsForLocation: () => {},
    },
    Review: {
    // ...
    },
    Mutation: {
    // ...
    },
    };
  3. We'll start with the overallRating . First, we'll destructure the parent (a Location object) to get the id . We'll also destructure the contextValue to pull out our dataSources.

    subgraph-reviews/resolvers.js
    overallRating: ({id}, _, {dataSources}) => {
    // TODO
    },
  4. Inside the function, we'll return the results of calling our dataSources object, its ReviewsAPI, and its getOverallRatingForLocation method. Then, pass in the id of the location that we're querying.

    subgraph-reviews/resolvers.js
    overallRating: ({id}, _, {dataSources}) => {
    return dataSources.reviewsAPI.getOverallRatingForLocation(id);
    },

    Note: You can check out how the getOverallRatingForLocation method works by peeking inside the subgraph-reviews/datasources/ReviewsApi.js file.

  5. Next, we'll set up the function for the reviewsForLocation and follow the same structure as before. This time, we'll use the getReviewsForLocation method of the ReviewsAPI to fetch all reviews for a location based on its id.

    subgraph-reviews/resolvers.js
    reviewsForLocation: ({id}, _, {dataSources}) => {
    return dataSources.reviewsAPI.getReviewsForLocation(id);
    },

Wonderful! Our s receive a location's id and can return the right data for that location.

Adding a __resolveReference function

Earlier, we learned that each subgraph that contributes s to an entity needs to define a reference for that entity.

We already defined the reference in the locations subgraph, but the reviews subgraph also needs some way of knowing which particular location object it's resolving s for.

Here's the good news: because we're using Apollo Server, defining the reference function explicitly in the reviews subgraph is not a requirement. Apollo Server defines a default reference for any entities we don't define one for.

This diagram shows how the __resolveReference function works by default with a query for a particular Location.

The reviews subgraph resolve reference function called for a particular queried Location
  1. A queried location is resolved in the locations subgraph based on its id argument.
  2. When the server reaches the reviewsForLocation field, the router knows that this is the responsibility of the reviews subgraph. The __resolveReference function receives the queried Location object that the locations subgraph returned.
  3. The reviewsForLocation resolver receives the referenced Location object as its parent argument, which it can then destructure and use to resolve data.

Even with this feature working for us under the hood, we'll walk through the steps to add this function ourselves in the optional section below, and review what happens when our associates data across subgraphs.

So are we ready to put our to the test? Not so fast! Remember we made changes! These changes need to be published to the , or we'll run into the same problem we faced in the last lesson.

So we'll run the rover subgraph publish command, passing it the values for our reviews subgraph.

rover subgraph publish <APOLLO_GRAPH_REF> \
--name reviews \
--schema ./subgraph-reviews/reviews.graphql

Querying data across subgraphs

With a successful publish, let's return to Studio and refresh the . We can see that our list of subs now includes overallRating and reviewsForLocation!

studio.apollographql.com/graph/flyby/explorer?variant=current
The Location type with reviews fields

Let's include these s in a new query to our . We'll use the query the client needs for the location details page.

query GetLocationDetails($locationId: ID!) {
location(id: $locationId) {
id
name
description
photo
overallRating
reviewsForLocation {
id
comment
rating
}
}
}

In the Variables panel:

{ "locationId": "loc-1" }

Look at this sweet sweet data!

The client is going to be thrilled that they're getting information about a location from both subgraphs without having to do any work to put it all together themselves!

Even better, we didn't have to restart our . This is thanks to our 's connection to Apollo Uplink. Our newly published subgraph triggered Apollo Studio to compose a new . Our then polled the Uplink and fetched the new . Best of all, the started to use the new immediately, enabling us to query the new s right away!

With that, we can finally check off the last two s of our agreement!

The FlyBy schema diagram, with all the fields checked off!

Practice

Check your understanding!
In a federated graph, we should define our subgraphs based on 
 
instead of 
 
. Types containing fields that can be resolved across multiple subgraphs are called 
 
. These types always have a 
 
 that enables different subgraphs to associate data with the same object. To keep its supergraph schema up to date, our 
 
 can poll the 
 
.

Drag items from this box to the blanks above

  • assignments

  • router

  • key field

  • types

  • create

  • concerns

  • delete

  • endpoints

  • entities

Code Challenge!

You're working on a federated graph that manages book information. The authors subgraph defines an Author entity with a primary key field id of non-nullable type ID. You want to use the Author entity in the books subgraph. Define the Author entity below, and add a new field, books, which returns a non-nullable list of non-nullable type Book.

Key takeaways

  • A subgraph that contributes fields to an entity should define the following:
    • The entity, using the @key directive and its primary key fields, as well as the new fields the subgraph defines
    • A __resolveReference function to know which particular entity instance a subgraph is resolving fields for. This can be taken care of by default by Apollo Server.
  • A federated architecture helps organize and illustrate the relationships between types across our graph in a way that an app developer (or multiple teams of developers!) would want to consume the data.
  • When both subgraphs use the same primary key to associate data for a type, the router coordinates data from both sources and bundles it up in a single response.

Up next

Congratulations, you've finished implementing all the s in the FlyBy graph!

In the next and final lesson, we'll put the backend and frontend together and finally see FlyBy working in a browser!

Previous
Next