Apollo Federation Subgraph support for HotChocolate.
$ dotnet add package ApolloGraphQL.HotChocolate.FederationThis is the official Apollo Federation support library for Hot Chocolate with support for Federation 1 and Federation 2 subgraphs. For backwards compatibility, it was based on HotChocolate's original Fed 1 module with added support for Fed v2. We recommend as ongoing Federation support for HotChocolate ecosystem and using to kickstart new projects.
rover subgraph createApollo Federation is a powerful, open architecture that helps you create a unified supergraph that combines multiple GraphQL APIs.
ApolloGraphQL.HotChocolate.Federation provides Apollo Federation support for building subgraphs in the HotChocolate ecosystem. Individual subgraphs can be run independently of each other but can also specify
relationships to the other subgraphs by using Federated directives. See Apollo Federation documentation for details.
ApolloGraphQL.HotChocolate.Federation package is published to Nuget. Update your .csproj file with following package references
<ItemGroup>
<!-- make sure to also include HotChocolate package -->
<PackageReference Include="HotChocolate.AspNetCore" Version="13.5.1" />
<!-- federation package -->
<PackageReference Include="ApolloGraphQL.HotChocolate.Federation" Version="$LatestVersion" />
</ItemGroup>After installing the necessary packages, you need to register Apollo Federation with your GraphQL service.
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.AddApolloFederationV2()
// register your types and services
;
var app = builder.Build();
app.MapGraphQL();
app.Run();If you would like to opt-in to Federation v1 schema, you need to use
.AddApolloFederation()extension instead.
Refer to HotChocolate documentation for detailed information on how to create GraphQL schemas and configure your server.
Apollo Federation requires subgraphs to provide some additional metadata to make them supergraph aware. Entities are GraphQL objects that can be uniquely identified across
the supergraph by the specified @keys. Since entities can be extended by various subgraphs, we need an extra entry point to access the entities, i.e. subgraphs need to
implement reference resolvers for entities that they support.
See Apollo documentation for additional Federation details.
All federated directives are provided as attributes that can be applied directly on classes/fields/methods.
[Key("id")]
public class Product
{
public Product(string id, string name, string? description)
{
Id = id;
Name = name;
Description = description;
}
[ID]
public string Id { get; }
public string Name { get; }
public string? Description { get; }
// assumes ProductRepository with GetById method exists
// reference resolver method must be public static
[ReferenceResolver]
public static Product GetByIdAsync(
string id,
ProductRepository productRepository)
=> productRepository.GetById(id);
}This will generate following type
type Product @key(fields: "id") {
id: ID!
name: String!
description: String
}Federation v1 directives
Extends applicable on objects, see @extends documentationExternal applicable on fields, see @external documentationKey applicable on objects, see @key documentationProvides applicable on fields, see @provides documentationRequires applicable on fields, see @requires documentationFederation v2 directives (includes all of the v1 directives)
ApolloTag applicable on schema, see @tag documentationComposeDirective applicable on schema, see @composeDirective documentationContact applicable on schema, see @contact usageInaccessible applicable on all type definitions, see @inaccessible documentationInterfaceObject applicable on objects, see @interfaceObject documentationKeyInterface applicable on interfaces, see entity interface @key documentationLink applicable on schema, see @link documentationShareable applicable on schema, see @shareable documentationEntity resolution
Map applicable on entity resolver method paramaters, allows you to map complex argument to a simpler representation value, e.g. [Map("foo.bar")] string barReferenceResolver applicable on public static methods within an entity class to indicate entity resolverAlternatively, if you need more granular control, you can use code first approach and manually populate federation information on the underlying GraphQL type descriptor. All federated directives expose corresponding methods on the applicable descriptor.
public class Product
{
public Product(string id, string name, string? description)
{
Id = id;
Name = name;
Description = description;
}
[ID]
public string Id { get; }
public string Name { get; }
public string? Description { get; }
}
public class ProductType : ObjectType<Product>
{
protected override void Configure(IObjectTypeDescriptor<Product> descriptor)
{
descriptor
.Key("id")
.ResolveReferenceWith(t => GetProduct(default!, default!));
}
private static Product GetProduct(
string id,
ProductRepository productRepository)
=> productRepository.GetById(upc);
}This will generate following type
type Product @key(fields: "id") {
id: ID!
name: String!
description: String
}Federation v1 directives
ExtendsType applicable on objects, see @extends documentationExternal applicable on fields, see @external documentationKey(fieldset) applicable on objects, see @key documentationProvides(fieldset) applicable on fields, see @provides documentationRequires(fieldset) applicable on fields, see @requires documentationFederation v2 directives (includes all of the v1 directives)
ApolloTag applicable on all type definitions, see @tag documentationComposeDirective(name) applicable on schema, see @composeDirective documentationContact(name, url?, description?) applicable on schema, see @contact usageInaccessible applicable on all type definitions, see @inaccessible documentationInterfaceObject applicable on objects, see @interfaceObject documentationKey(fieldset, resolvable?) applicable on objects, see @key documentationLink(url, [import]?) applicable on schema, see @link documentationShareable applicable on fields and objects, see @shareable documentationEntity resolution
ResolveReferenceWith function to be able to resolve the entitiesSee HotChocolate documentation for details on the server support for command line interface.
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.AddApolloFederationV2()
// register your types and services
;
var app = builder.Build();
app.MapGraphQL();
app.RunWithGraphQLCommands();You can then generate your schema by running
dotnet run -- schema export --output schema.graphql@composedDirective usageBy default, Supergraph schema excludes all custom directives. The `@composeDirective`` is used to specify custom directives that should be preserved in the Supergraph schema.
ApolloGraphQL.HotChocolate.Federation provides common FederatedSchema class that automatically includes Apollo Federation v2 @link definition. When applying any custom
schema directives, you should extend this class and add required attributes/directives.
When applying @composedDirective you also need to @link it your specification. Your custom schema should then be passed to the AddApolloFederationV2 extension.
[ComposeDirective("@custom")]
[Link("https://myspecs.dev/myCustomDirective/v1.0", new string[] { "@custom" })]
public class CustomSchema : FederatedSchema
{
}
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.AddApolloFederationV2(new CustomSchema())
// register your types and services
;
var app = builder.Build();
app.MapGraphQL();
app.Run();@interfaceObject usageApollo Federation v2 supports entity interfaces, a powerful extension to the GraphQL interfaces that allows you to extend functionality of an interface across the supergraph without having to implement (or even be aware of) all its implementing types.
In a subgraph defininig the interface we need to apply @key
[InterfaceType]
[KeyInterface("id")]
public interface Product
{
[ID]
string Id { get; }
string Name { get; }
}
[Key("id")]
public class Book : Product
{
[ID]
public string Id { get; set; }
public string Name { get; set; }
public string Content { get; set; }
}We can then extend the interface in another subgraph by making it a type, applying @interfaceObject and same @key directive. This allows you add new fields to every
entity that implements your interface (e.g. adding Reviews field to all Product implementations).
[Key("id")]
[InterfaceObject]
public class Product
{
[ID]
public string Id { get; set; }
public List<string> Reviews { get; set; }
}You can use the @contact directive to add your team's contact information to a subgraph schema. This information is displayed in Studio, which helps other teams know who
to contact for assistance with the subgraph. See documentation for details.
We can apply [Contact] attribute on a custom schema. You then need to include @contact directive definition and pass your custom schema to the AddApolloFederationV2 extension.
[Contact("MyTeamName", "https://myteam.slack.com/archives/teams-chat-room-url", "send urgent issues to [#oncall](https://yourteam.slack.com/archives/oncall)")]
public class CustomSchema : FederatedSchema
{
}
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.AddType<ContactDirectiveType>();
.AddApolloFederationV2(new CustomSchema())
// register your types and services
;
var app = builder.Build();
app.MapGraphQL();
app.Run();undefinedMigrating from HotChocolate.Federation to ApolloGraphQL.HotChocolate.Federation is easy. Simply update your package import to point to a new module
<ItemGroup>
<!-- make sure to also include HotChocolate package -->
<PackageReference Include="HotChocolate.AspNetCore" Version="13.5.1" />
<!-- federation package -->
- <PackageReference Include="HotChocolate.ApolloFederation" Version="$LatestVersion" />
+ <PackageReference Include="ApolloGraphQL.HotChocolate.Federation" Version="$LatestVersion" />
</ItemGroup>and update namespace imports
- using HotChocolate.ApolloFederation;
+ using ApolloGraphQL.HotChocolate.Federation;While we tried to make migration process as seamless as possible, we had to make few tweaks to the library. Due to the dependency on some of the internal APIs, we had to make following breaking changes to the library:
[Key] is now applicable only on classes and you no longer can apply it on individual fields[ReferenceResolver] is now applicable only on public static methods within an entity, it is no longer applicable on classesIf you have a specific question about the library or code, please start a discussion in the Apollo community forums or start a conversation on our Discord server.
To get started, please fork the repo and checkout a new branch. You can then build the library locally by running
# install dependencies
dotnet restore
# build project
dotnet build
# run tests
dotnet testSee more info in CONTRIBUTING.md.
After you have your local branch set up, take a look at our open issues to see where you can contribute.
For more info on how to contact the team for security issues, see our Security Policy.
This library is licensed under The MIT License (MIT).