Odyssey

Federation with .NET (C#) & Hot Chocolate
beta

Intro to federationManaged federation with GraphOSA Hot Chocolate subgraphPublishing to the registryRouter configuration & insightsAdding a subgraphLocal development with rover devEntities and the query planContributing fields to an entityUsing the @requires and @external directivesSchema checksPublishing schema changes
9. Contributing fields to an entity
3m

Overview

We're finally ready to add the recommendedPlaylists field to a Recipe!

In this lesson, we will:

  • Learn how multiple subgraphs can contribute fields to an entity

Defining an entity

The Recipe type is already marked as an entity in the recipes subgraph. We can confirm this by taking a look at the Schema page in Studio, and selecting Objects.

https://studio.apollographql.com

Schema - Objects page

Beside the Recipe type is an E label, denoting that it's an entity type. Clicking into the type, we can further see the key fields for this entity: id.

This is great! The recipes subgraph has already opened us up to contribute fields to this entity. Let's go ahead and do that, jumping back to our project in the code editor.

Let's create a new class in the Types folder called Recipe.

Types/Recipe.cs
namespace Odyssey.MusicMatcher;
public class Recipe
{
}

Remember, to define an entity, we need two things: a primary key ([Key]) and a reference resolver ([ReferenceResolver]).

Defining a primary key

  1. First, let's import the HotChocolate.ApolloFederation package at the top of the file.

    Types/Recipe.cs
    using HotChocolate.ApolloFederation;
  2. Inside the Recipe class, we'll add the Id as a property of the class, using short-hand syntax for auto-implemented properties. Note that we're using uppercase Id here to follow C# convention, but behind the scenes, Hot Chocolate will convert it to lowercase when generating the GraphQL schema. This property returns a string type.

    Types/Recipe.cs
    public string Id { get; }
  3. We'll also annotate this property with the [ID] attribute. Though the Id returns a string type, we want to map it to a GraphQL scalar type for ID, not String.

    Types/Recipe.cs
    [ID]
    public string Id { get; }
  4. Finally, we'll tag the property with the Key attribute, which specifies it as the primary key.

    Types/Recipe.cs
    [ID]
    [Key]
    public string Id { get; }

Defining the reference resolver function

Along with the key, we need a reference resolver: a function that returns a particular instance of an entity.

  1. Let's define a new function, which will be public static. We'll call it GetRecipeById. The name of the reference resolver doesn't matter, but we should be descriptive about what it's doing. This function will return a Recipe type (the entity type!).

    Types/Recipe.cs
    public static Recipe GetRecipeById()
    {
    }
  2. To mark this as the reference resolver for the entity, we'll use the [ReferenceResolver] attribute.

    Types/Recipe.cs
    [ReferenceResolver]
    public static Recipe GetRecipeById()
  3. For the parameters of this function, we have access to the key field(s) for the entity. In this case, that's the id property.

    Types/Recipe.cs
    public static Recipe GetRecipeById(
    string id
    )
  4. In the body of the function, we'll return an instance of the Recipe class, using the id to instantiate it.

    Types/Recipe.cs
    return new Recipe(id);
  5. Finally, we'll write the constructor for the Recipe class, which takes in a string id and assigns it to the Id property.

    Types/Recipe.cs
    public Recipe(string id)
    {
    Id = id;
    }

Contributing to an entity

Awesome, we have the stub of the entity filled out: the bare minimum needed to define an entity in a new subgraph. Now let's add the new field to make our dream query come true: recommended playlists for a recipe.

  1. Inside the body of the Recipe class, add a new resolver function called RecommendedPlaylists. For now, we'll work with hard-coded data. We'll replace it with a call to our data source in the next lesson.

    The RecommendedPlaylists method will return a list of Playlist objects.

    Types/Recipe.cs
    public List<Playlist> RecommendedPlaylists()
    {
    return new List<Playlist>
    {
    new Playlist("1", "GraphQL Groovin'"),
    new Playlist("2", "Graph Explorer Jams"),
    new Playlist("3", "Interpretive GraphQL Dance")
    };
    }
  2. Let's also add a description for this field.

    Types/Recipe.cs
    [GraphQLDescription(
    "A list of recommended playlists for this particular recipe. Returns 1 to 3 playlists."
    )]
using HotChocolate.ApolloFederation.Resolvers;
using HotChocolate.ApolloFederation.Types;
namespace Odyssey.MusicMatcher;
public class Recipe
{
[ID]
[Key]
public string Id { get; }
[ReferenceResolver]
public static Recipe GetRecipeById(string id)
{
return new Recipe(id);
}
[GraphQLDescription(
"A list of recommended playlists for this particular recipe. Returns 1 to 3 playlists."
)]
public List<Playlist> RecommendedPlaylists()
{
return new List<Playlist>
{
new Playlist("1", "GraphQL Groovin'"),
new Playlist("2", "Graph Explorer Jams"),
new Playlist("3", "Interpretive GraphQL Dance")
};
}
public Recipe(string id)
{
Id = id;
}
}

Registering the entity

One last thing: we'll need to register this type in our GraphQL server. Open up Program.cs and find the line where we initialize the GraphQL server. To group all our similar method calls, we'll add the next line right after registering the other types (such as Query and Mutation): AddType<Recipe>().

Program.cs
builder
.Services
.AddGraphQLServer()
.AddApolloFederation()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<Recipe>()
.RegisterService<SpotifyService>();

Alright, let's save our changes and restart the server.

The rover dev process will pick up the changes and take care of composing those changes with the recipes subgraph. If all goes well, we're ready to query this new schema!

Querying in Explorer

Jump back over to http://localhost:4000, where our local rover dev router is running.

Let's start building a new operation, a pared down version of our dream query. We'll ask for a specific recipe's name and the names of its recommended playlists.

query GetRecipeWithPlaylists {
randomRecipe {
name
recommendedPlaylists {
id
name
}
}
}
http://localhost:4000

Local router, query response

Woohoo–we're getting data back–playlists to sing along to while cooking up our recipe, nice!

Let's take a peek at the query plan. Click on the dropdown arrow beside Response and select Query Plan.

The chart view shows an initial fetch to the recipes subgraph, followed by another fetch to the soundtracks subgraph before finally merging (or flattening) the recipe type. Click Show plan as text.

http://localhost:4000

Local router, query plan

Query plan
QueryPlan {
Sequence {
Fetch(service: "recipes") {
{
recipe(id: "rec3j49yFpY2uRNM1") {
__typename
id
name
}
}
},
Flatten(path: "recipe") {
Fetch(service: "soundtracks") {
{
... on Recipe {
__typename
id
}
} =>
{
... on Recipe {
recommendedPlaylists {
id
name
}
}
}
},
},
},
}

We get a little more detail here. First, the router sends an operation to the recipes subgraph for the recipe's __typename, id and name. We didn't include __typename or id in our original operation, but the router needs them for its next step in the query plan to build the entity representation of a Recipe!

The router sends that entity representation to the soundtracks subgraph, which uses the information to identify that instance of a Recipe and contribute further fields to it: specifically, the recommendedPlaylists field.

Awesome! Our recipes and soundtracks services are now collaborating on the same Recipe entity. Each subgraph contributes its own fields, and the router packages up the response for us.

Practice

True or false: In Hot Chocolate, the reference resolver function must be named ReferenceResolver.

Key takeaways

  • In Hot Chocolate, we use the [Key] attribute to define the key field for an entity and the [ReferenceResolver] attribute to define the reference resolver function.
  • In Hot Chocolate, entities need to be registered to the GraphQL server.

Up next

Our musical recommendations are still pretty weak. In fact, they're not much like recommendations at all—they're hardcoded playlist objects! We haven't actually used any recipe-specific data to determine the playlists we recommend to our potential chef users. In the next lesson, we'll dive deeper into federation-specific directives and fortify our soundtrack suggestions.

Previous
Next

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.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              subgraphs

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              entity type

              An object type in a federated graph that's defined as an entity.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              reference resolver

              A type of resolver function responsible for resolving references to an entity within a federated GraphQL schema. Also known as an entity resolver.

              GraphQL schema

              A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.

              attribute

              Key-value pairs that add contextual metadata to telemetry. The router supports a built-in set of standard attributes from OpenTelemetry semantic conventions, and custom selectors to extract data from the router request lifecycle.

              GraphQL

              An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.

              scalar

              A "base" type that resolves to a single value. GraphQL includes the following scalar types by default: Int, Float, String, Boolean, and ID.

              attribute

              Key-value pairs that add contextual metadata to telemetry. The router supports a built-in set of standard attributes from OpenTelemetry semantic conventions, and custom selectors to extract data from the router request lifecycle.

              reference resolver

              A type of resolver function responsible for resolving references to an entity within a federated GraphQL schema. Also known as an entity resolver.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              reference resolver

              A type of resolver function responsible for resolving references to an entity within a federated GraphQL schema. Also known as an entity resolver.

              entity type

              An object type in a federated graph that's defined as an entity.

              reference resolver

              A type of resolver function responsible for resolving references to an entity within a federated GraphQL schema. Also known as an entity resolver.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              attribute

              Key-value pairs that add contextual metadata to telemetry. The router supports a built-in set of standard attributes from OpenTelemetry semantic conventions, and custom selectors to extract data from the router request lifecycle.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              GraphQL server

              A server that contains a GraphQL schema and can resolve client-requested operations that are executed against that schema.

              GraphQL server

              A server that contains a GraphQL schema and can resolve client-requested operations that are executed against that schema.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              router

              The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.

              operation

              A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.

              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              query plan

              A strategy for executing an incoming operation efficiently. A query plan divides a single incoming operation into one or more operations that are each resolvable by a single subgraph.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              router

              The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.

              operation

              A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              operation

              A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.

              router

              The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.

              query plan

              A strategy for executing an incoming operation efficiently. A query plan divides a single incoming operation into one or more operations that are each resolvable by a single subgraph.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              router

              The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.

              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              subgraph

              A service in a federated GraphQL architecture. Acts as a module for a supergraph. Includes both GraphQL services and REST services integrated via Apollo Connectors.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              router

              The single access point for a federated GraphQL architecture. It receives incoming operations and intelligently routes them across component services before returning a unified response.

              attribute

              Key-value pairs that add contextual metadata to telemetry. The router supports a built-in set of standard attributes from OpenTelemetry semantic conventions, and custom selectors to extract data from the router request lifecycle.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              entity

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              attribute

              Key-value pairs that add contextual metadata to telemetry. The router supports a built-in set of standard attributes from OpenTelemetry semantic conventions, and custom selectors to extract data from the router request lifecycle.

              reference resolver

              A type of resolver function responsible for resolving references to an entity within a federated GraphQL schema. Also known as an entity resolver.

              entities

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              GraphQL server

              A server that contains a GraphQL schema and can resolve client-requested operations that are executed against that schema.

              directives

              A GraphQL annotation for a schema or operation that customizes request execution. Prefixed with @ and may include arguments. For example, the @lowerCase directive below can define logic to return the username field in lowercase:

              type User {
              username: String! @lowerCase
              }

              NEW COURSE ALERT

              Introducing Apollo Connectors

              Connectors are the new and easy way to get started with GraphQL, using existing REST APIs.

              Say goodbye to GraphQL servers and resolvers—now, everything happens in the schema!

              Take the course