Overview
In the previous lesson, we just saw how subgraphs can reference an entity as a field's return type. Now, let's take a look at how subgraphs can contribute fields to an entity.
In this lesson, we will:
- Learn how multiple subgraphs can contribute fields to an entity
- Update the
Locationentity in ourreviewssubgraph schema by contributing thereviewsForLocationandoverallRatingfields
✏️ Contributing fields
Remembering our FlyBy UI, we know it needs to fetch each location's overallRating, along with a list of its reviewsForLocation:
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!
Open up the
subgraph-reviews/reviews.graphqlfile.Find the
Locationentity definition in the schema. We previously defined this as a stub of theLocationtype.subgraph-reviews/reviews.graphqltype Location @key(fields: "id", resolvable: false) {id: ID!}By default, a subgraph should only contribute fields that aren't defined by other subgraphs, with the exception of the primary key field. This means that because the
locationssubgraph definesname,description, andphotoas fields for theLocationtype, we won't—and shouldn't—define those fields here in thereviewssubgraph!Note: You can override the default behavior explained above to allow multiple subgraphs to resolve the same field by applying either the
@shareableor@providesdirective. This is an optional performance optimization that can instruct the router on how to plan the execution of a query across as few subgraphs as possible.Because we now want the
reviewssubgraph to contribute new fields to theLocationdefinition, the first thing we need to do is remove theresolvable: falseproperty from the@keydirective. This will enable ourreviewssubgraph to define and resolve its ownLocationfields.Remove the
resolvable: falseproperty from theLocationtype's@keydirective.subgraph-reviews/reviews.graphqltype Location @key(fields: "id") {id: ID!}
✏️ Adding new fields to the Location entity
Now we're ready to add the two new fields.
- the
overallRatingfield, which returns aFloat - the
reviewsForLocationfield, which returns a non-null list ofReviewobjects.
We'll also add descriptions to these fields so we can quickly see what they represent.
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 fields needs a resolver function to return data, so let's take care of that next.
Open the
resolvers.jsfile in thesubgraph-reviewsdirectory.Add a
Locationentry to theresolversmap. We'll also add two empty resolver functions for theoverallRatingandreviewsForLocationfields.subgraph-reviews/resolvers.jsconst resolvers = {Query: {// ...},Location: {overallRating: () => {},reviewsForLocation: () => {},},Review: {// ...},Mutation: {// ...},};We'll start with the
overallRatingresolver. First, we'll destructure theparentargument (aLocationobject) to get theidfield. We'll also destructure thecontextValueargument to pull out ourdataSources.subgraph-reviews/resolvers.jsoverallRating: ({id}, _, {dataSources}) => {// TODO},Inside the function, we'll return the results of calling our
dataSourcesobject, itsReviewsAPI, and itsgetOverallRatingForLocationmethod. Then, pass in theidof the location that we're querying.subgraph-reviews/resolvers.jsoverallRating: ({id}, _, {dataSources}) => {return dataSources.reviewsAPI.getOverallRatingForLocation(id);},Note: You can check out how the
getOverallRatingForLocationmethod works by peeking inside thesubgraph-reviews/datasources/ReviewsApi.jsfile.Next, we'll set up the resolver function for the
reviewsForLocationfield and follow the same structure as before. This time, we'll use thegetReviewsForLocationmethod of theReviewsAPIto fetch all reviews for a location based on its id.subgraph-reviews/resolvers.jsreviewsForLocation: ({id}, _, {dataSources}) => {return dataSources.reviewsAPI.getReviewsForLocation(id);},
Wonderful! Our resolvers 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 fields to an entity needs to define a reference resolver for that entity.
We already defined the reference resolver in the locations subgraph, but the reviews subgraph also needs some way of knowing which particular location object it's resolving fields for.
Here's the good news: because we're using Apollo Server, defining the reference resolver function explicitly in the reviews subgraph is not a requirement. Apollo Server defines a default reference resolver 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.
- A queried location is resolved in the
locationssubgraph based on itsidargument. - When the server reaches the
reviewsForLocationfield, the router knows that this is the responsibility of thereviewssubgraph. The__resolveReferencefunction receives the queriedLocationobject that thelocationssubgraph returned. - The
reviewsForLocationresolver receives the referencedLocationobject as itsparentargument, 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 router associates data across subgraphs.
So are we ready to put our supergraph to the test? Not so fast! Remember we made schema changes! These changes need to be published to the registry, 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 Explorer. We can see that our list of subfields now includes overallRating and reviewsForLocation!

Let's include these fields in a new query to our router. We'll use the query the client needs for the location details page.
query GetLocationDetails($locationId: ID!) {location(id: $locationId) {idnamedescriptionphotooverallRatingreviewsForLocation {idcommentrating}}}
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 router. This is thanks to our router's connection to Apollo Uplink. Our newly published subgraph triggered GraphOS to compose a new supergraph schema. Our router then polled the Uplink and fetched the new supergraph schema. Best of all, the router started to use the new supergraph schema immediately, enabling us to query the new fields right away!
With that, we can finally check off the last two fields of our schema agreement!
Practice
Drag items from this box to the blanks above
assignments
create
concerns
key field
entities
router
types
search
delete
Uplink
endpoints
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
@keydirective and its primary key fields, as well as the new fields the subgraph defines - A
__resolveReferencefunction to know which particular entity instance a subgraph is resolving fields for. This can be taken care of by default by Apollo Server.
- The entity, using the
- 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 fields 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!
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.