7. Introducing entities
15m

Overview

It's time to witness the true strength of federation; we're about to join the pieces and make our queries more powerful and dynamic than ever before.

In this lesson, we will:

  • Learn what an type is, what it's used for, and how to define it
  • Convert the Listing type into an

Reviews and listings

Remember our dream ?

query GetListingAndReviews {
listing(id: "listing-1") {
title
description
numOfBeds
amenities {
name
category
}
overallRating
reviews {
id
text
}
}
}

As this shows, we want reviews and overallRating to be on the Listing type.

The big problem with that? As we've previously discussed, the reviews has no idea about the types in the listings —much less that there's a Listing type it should be adding a to! Let's fix that—with entities.

What's an entity?

An entity is an with split between multiple .It's the fundamental building block of a federated graph architecture, used to connect data between subgraphs while still adhering to the separation of concerns principle.

When working with an , each can do one or both of the following:

  • Contribute different to the
  • Reference an , which means using it as a return type for another defined in the

Contributing vs. referencing

To differentiate between that contribute to an , and those that reference an , think of it this way: a that contributes is actually adding new data capabilities from its own domain to the type.

This is in contrast to a that merely references the ; it's essentially just "mentioning" the existence of the entity, and providing it as the return type for another field.

In federation, we use entities to create cohesive types that aren't confined to just one or another; instead, they can span our entire API!

In our example, we want to supplement our Listing type with reviews-related data. That makes the Listing type a perfect candidate to become an ; it needs to have that are defined in both of our . This lets us build a more powerful representation of what a "listing" is in our API, and the different data that makes it up.

To create an , a needs to provide two things: a primary key and a reference resolver.

Defining a primary key

An 's primary key is the (or fields) that can uniquely identify an instance of that within a . Just like in a database, this is the information that lets us tell one thing from another. When we talk about listings, we use their id to identify which listing we're talking about.

The uses primary keys to collect data from across multiple and associate it with a single instance. It's how we know that each subgraph is talking about—and providing data for—the same object!

Illustration showing three entities with unique ids

We use the @key , along with a property called fields to set the we want to use as the 's primary key.

Entity syntax
type EntityType @key(fields: "id") {
# ...
}

Reference resolvers and entity representations

Because an 's can be divided across , each subgraph that provides needs a way to identify the instance the is gathering data for. When it's able to identify this instance, the can provide additional data. Identifying a particular instance of an takes the form of a method called a reference resolver.

This method receives a basic object called an representation. An entity representation is an object that the uses to identify a specific instance of an . A representation includes the typename for that and the @key for the specific instance.

  • The __typename field: This exists on all types automatically. It always returns the name of its containing type, as a string. For example, Listing.__typename returns "Listing".

  • The @key field: The key-value pair that a can use to identify the instance of an . For example, if we defined the Listing using the "id" as a primary key, then our entity representation would include an "id" property with a value like "listing-3".

An representation passed to each resolving for a particular Listing might look like this:

Example Listing entity representation
{
"__typename": "Listing",
"id": "listing-3"
}

You can think of the representation as the minimum basic information the needs to associate data from multiple , and ensure that each subgraph is talking about the same object.

As we'll see shortly, the representation for a particular listing will give our reviews all the information it needs (nothing more, nothing less!) to contribute the relevant reviews data.

Creating the Listing entity

Bringing this all back to our dream ! We want to make the Listing type an so that the reviews can contribute two to it: overallRating and reviews.

Let's get to it!

In the listings subgraph

With your rover dev process still running, open up the listings and navigate to schema.graphql. Scroll down to find the Listing type.

Here we'll apply the @key , passing in a fields property set to "id".

(listings) schema.graphql
type Listing @key(fields: "id") {
id: ID!
# ... other Listing fields
}

When we save our changes, rover dev will automatically pick up on the new schema and restart the process. Make sure it composes successfully before moving on to the reviews .

In the reviews subgraph

Open up your code editor to the reviews , and navigate to the src/schema.graphql file. We'll start by adding a basic stub of the Listing type: this consists of the typename, the @key , and the assigned as its primary key, id.

(reviews) schema.graphql
type Listing @key(fields: "id") {
id: ID!
}

Notice that we don't include any other from the Listing as defined in the listings . We can keep our reviews clean and concerned only with the data it has the responsibility of providing.

Let's add that reviews to the Listing type. We'll give it a return type of [Review!]!. We'll also define the overallRating, which returns a nullable Float type.

(reviews) schema.graphql
type Listing @key(fields: "id") {
id: ID!
"The submitted reviews for this listing"
reviews: [Review!]!
"The overall calculated rating for a listing"
overallRating: Float
}

Testing our schema changes

Our rover dev process should still be running, so let's jump back to http://localhost:4002.

Now we'll see that the reviews is available under the Listing type. Our small schema changes have made it possible to construct our dream !

http://localhost:4002

Sandbox with our dream query syntax written into the Operation panel

query GetListingAndReviews {
listing(id: "listing-1") {
title
description
numOfBeds
amenities {
name
category
}
overallRating
reviews {
id
text
}
}
}

Though it's lovely to look at, our can't actually return data yet. While the listings can easily resolve its listing (id, title, description, numOfBeds, and amenities), we still haven't given the reviews any idea of what to do when the requests a particular listing's reviews or overallRating.

In fact, if we run the , we'll see just a bunch of gnarly errors.

Practice

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 this box to the blanks above

  • @key

  • fields

  • @primary

  • more than one

  • keys

  • only one

  • subgraph

  • reference resolver

  • router

  • @unique

Code Challenge!

Make Planet an entity type, using id as its key field.

Loading...
Loading progress

Key takeaways

  • An is a type that can resolve its across multiple .
  • To create an , we can use the @key to specify which (s) can uniquely identify an object of that type.
  • We can use entities in two ways:
    • As a return type for a (referencing an ).
    • Defining for an from multiple (contributing to an entity).

Up next

In the next lesson, we'll equip our reviews with logic to actually return data.

Previous

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.