9. Connecting data using entities
7m

Overview

The foundation of our supergraph is complete. We've separated FlyBy's schema into location data and review data, and we've implemented subgraphs that only define the types they're concerned with.

Revisiting our schema agreement checklist, we still have three fields that we don't know how to implement yet:

  • Location.reviewsForLocation and Location.overallRating: These are both fields of the Location type, but we decided we want them to be owned by the reviews subgraph.
  • Review.location: This is a field on the Review type (which lives in the reviews subgraph), but the field has a return type of Location, which is defined in the locations subgraph.
The FlyBy schema diagram, with the three remaining fields in focus

To implement these fields, we need to add a new tool to our developer tool belts: entities!

In this lesson, we will:

  • Learn what an entity is and what it's used for
  • Learn how to define an entity
  • Learn how the router represents entities when it talks between subgraphs

What's an entity?

An entity is an object type with fields split between multiple subgraphs.

This means we can define a type that both of our subgraphs can contribute fields to and resolve independently.

Illustration of 2 subgraphs contributing fieleds to an entity.

In FlyBy, we want our reviews to include the location that they are written about. Our Location type needs to be used by both subgraphs, so we'll be turning the Location type into an entity.

A subgraph that defines an entity can do one or both of the following:

  1. Reference the entity
  2. Contribute fields to the entity

Reference the entity

Referencing an entity means using it as a return type for another field defined in the subgraph.

For example, in the reviews subgraph, we can add a location field to the Review type, which will reference the Location entity as its return type.

The Review type, with a location field that has a return value of a Location type

Contribute fields to the entity

Contributing fields to an entity means that one subgraph adds new fields to an entity that are specific to that subgraph's concerns.

For example, the Location entity will have fields for name, description, and photo, which will live in the locations subgraph. In other words, the locations subgraph contributes these fields to the Location entity.

And the reviews subgraph contributes two review-specific fields to the Location entity: reviewsForLocation and overallRating.

The Location entity, with fields divided between the locations and reviews subgraph

How to create an entity

To convert an object into an entity in the subgraph schema, we need to do two things:

  1. Define a primary key
  2. Define a reference resolver

Defining a primary key

An entity's primary key is the field (or fields) that can uniquely identify an instance of that entity within a subgraph. The router uses primary keys to collect data from across multiple subgraphs and associate it with a single entity instance.

For example, a location entity's primary key is its id. The router uses that id to collect data about a specific location instance, like a location with id "loc-1".

Illustration showing three entities with unique ids

In each of our subgraph schemas, we can define a primary key for an entity, by adding the @key directive after the type's name.

The @key directive needs a property called fields, which we'll set to the field we want to use as the entity's primary key.

Entity syntax
type EntityType @key(fields: "id")
Illustration showing the syntax for defining an entity. See code snippet above for an example.

Defining a reference resolver function

Each subgraph that contributes fields to an entity also needs to define a special resolver function for that entity called a reference resolver. The router uses reference resolvers to directly access the entity fields that each subgraph contributes.

Illustration of each subgraph contributing fields to an entity with an associated reference resolver.

Every reference resolver has the name: __resolveReference. We define each entity's reference resolver right alongside all the field resolvers for that type.

The __resolveReference function has a slightly different signature from other resolver functions. Instead of the usual four arguments, __resolveReference only takes three:

  • reference: The entity representation object that's passed in by the router. This tells the subgraph which instance of an entity is being requested. We'll cover what an entity representation is in the section below.
  • context: The object shared across all resolvers. This is the same as in normal resolvers.
  • info: Contains information about the operation's execution state, just like in a normal resolver. We won't use this argument much.
Illustration of each subgraph contributing fields to an entity with an associated reference resolver.

Let's focus on this first argument, reference, and learn more about entity representations.

What's an entity representation?

An entity representation is an object that the router uses to represent a specific instance of an entity. A representation always includes the typename for that entity and the @key field for the specific instance.

  • The __typename field: This field exists on all GraphQL types automatically. It always returns the name of its containing type, as a string. For example, Location.__typename returns "Location".
  • The @key field: The key-value pair that a subgraph can use to identify the instance of an entity. For example, if we defined the Location entity using the "id" field as a primary key, then our entity representation would include an "id" property with a value like "loc-2".

An entity representation for a location might look like this:

Example location entity representation
{
"__typename": "Location",
"id": "loc-2"
}

You can think of an entity representation as a passport that the router uses to refer to a particular object between subgraphs.

The typename field is like a passport's country of origin. It says which entity the object belongs to. And the @key field is like a passport's ID number, uniquely identifying this instance of that entity.

The representation object is like a passport

Practice

Where should an entity's __resolveReference function be defined?

Check your understanding!

An entity's primary key is used to identify a unique instance of that entity within a
 
. To define a primary key, apply the
 
directive and pass in the name of the primary key field as the value of the
 
property. An entity can have
 
primary key.

Drag items from the box to the blanks above

router
@key
@primary
keys
fields
reference resolver
subgraph
only one
more than one
@unique

Key takeaways

  • An entity is a type that can resolve its fields across multiple subgraphs.
  • To create an entity, we can use the @key directive to specify which field(s) can uniquely identify an object of that type.
  • We can use entities in two ways:
    • As a return type for a field (referencing an entity).
    • Defining fields for an entity from multiple subgraphs (contributing to an entity).
  • Any subgraph that contributes fields to an entity needs to define a reference resolver function for that entity. This __resolveReference resolver is called whenever the router needs to access fields of the entity from within another subgraph.
  • An entity representation is an object that the router uses to represent a specific instance of an entity. It includes the entity's type and its key field(s).

Up next

We've covered a lot about entities! In the next lesson, we'll jump back into the code for our subgraphs and define our first entity.

Previous
Next