HotChocolate.ApolloFederation
agora oferece suporte total ao Federation v2. Para simplificar as integrações no ecossistema HC, decidimos descontinuar este pacote em favor do suporte a um único pacote integrado com todos os recursos.Aviso Devido a uma alteração significativa na API pública, não podemos oferecer suporte a versões mais recentes do
HotChocolate
até que sua API de substituição (atualmente em andamento) seja concluída. Só podemos oferecer suporte às versões v13.5.x
e v13.6.x
Apollo Federation é uma arquitetura aberta e poderosa que ajuda a criar um supergráfico unificado que combina várias APIs GraphQL. ApolloGraphQL.HotChocolate.Federation
fornece suporte da Apollo Federation para a construção de subgráficos no ecossistema HotChocolate
. Subgráficos individuais podem ser executados independentemente uns dos outros, mas também podem especificar relacionamentos com outros subgráficos usando diretivas federadas. Consulte a documentação da Federação Apollo para obter detalhes.
O pacote ApolloGraphQL.HotChocolate.Federation
é publicado no Nuget. Atualize seu arquivo .csproj
com as seguintes referências de pacote
< ItemGroup > < PackageReference Include = " HotChocolate.AspNetCore " Version = " 13.6.0 " /> < PackageReference Include = " ApolloGraphQL.HotChocolate.Federation " Version = " $LatestVersion " /> ItemGroup >
Depois de instalar os pacotes necessários, você precisa registrar a Apollo Federation em seu serviço GraphQL.
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( )
// register your types and services
;
var app = builder . Build ( ) ;
app . MapGraphQL ( ) ;
app . Run ( ) ;
Se desejar aceitar o esquema Federation v1, você precisará usar a extensão
.AddApolloFederation()
.
Consulte a documentação HotChocolate
para obter informações detalhadas sobre como criar esquemas GraphQL e configurar seu servidor.
A Apollo Federation exige que os subgráficos forneçam alguns metadados adicionais para torná-los conscientes dos supergrafos. Entidades são objetos GraphQL que podem ser identificados exclusivamente no supergráfico pelos @key
s especificados. Como as entidades podem ser estendidas por vários subgráficos, precisamos de um ponto de entrada extra para acessar as entidades, ou seja, os subgráficos precisam implementar resolvedores de referência para as entidades que eles suportam.
Consulte a documentação da Apollo para obter detalhes adicionais da Federação.
Todas as diretivas federadas são fornecidas como atributos que podem ser aplicados diretamente em classes/campos/métodos.
[ 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 ) ;
}
Isso irá gerar o seguinte tipo
type Product @key ( fields : " id " ) {
id : ID !
name : String !
description : String
}
Diretivas da Federação v1
Extends
aplicável em objetos, consulte a documentação @extends
External
aplicável em campos, consulte a documentação @external
Key
aplicável em objetos, consulte a documentação @key
Provides
aplicável em campos, consulte a documentação @provides
Requires
aplicável nos campos, consulte a documentação @requires
Diretivas da Federação v2 (inclui todas as diretivas v1)
ApolloTag
aplicável no esquema, consulte a documentação @tag
ApolloAuthenticated
(desde v2.5) aplicável em enum, campo, interface e objeto, documentação @authenticated
ComposeDirective
(desde v2.1) aplicável no esquema, consulte a documentação @composeDirective
Contact
aplicável no esquema, consulte uso @contact
Inaccessible
aplicável em todas as definições de tipo, consulte a documentação @inaccessible
InterfaceObject
(desde v2.3) aplicável em objetos, consulte a documentação @interfaceObject
KeyInterface
aplicável em interfaces, consulte a documentação da interface de entidade @key
Link
aplicável no esquema, consulte a documentação @link
RequiresScopes
(desde v2.5) aplicável em enum, campo, interface e objeto, documentação @requiresScopes
Shareable
aplicável no esquema, consulte a documentação @shareable
Resolução de entidade
Map
aplicável em parâmetros do método resolvedor de entidade, permite mapear argumentos complexos para um valor de representação mais simples, por exemplo, [Map("foo.bar")] string bar
ReferenceResolver
aplicável em métodos estáticos públicos dentro de uma classe de entidade para indicar o resolvedor de entidadeComo alternativa, se precisar de um controle mais granular, você pode usar a abordagem code first e preencher manualmente as informações da federação no descritor de tipo GraphQL subjacente. Todas as diretivas federadas expõem métodos correspondentes no descritor aplicável.
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 ) ;
}
Isso irá gerar o seguinte tipo
type Product @key ( fields : " id " ) {
id : ID !
name : String !
description : String
}
Diretivas da Federação v1
ExtendsType
aplicável em objetos, consulte a documentação @extends
External
aplicável em campos, consulte a documentação @external
Key(fieldset)
aplicável em objetos, consulte a documentação @key
Provides(fieldset)
aplicável em campos, consulte a documentação @provides
Requires(fieldset)
aplicável em campos, consulte a documentação @requires
Diretivas da Federação v2 (inclui todas as diretivas v1)
ApolloTag
aplicável em todas as definições de tipo, consulte a documentação @tag
ApolloAuthenticated
(desde v2.5) aplicável em enum, campo, interface e objeto, documentação @authenticated
ComposeDirective(name)
(desde v2.1) aplicável no esquema, consulte a documentação @composeDirective
Contact(name, url?, description?)
aplicável no esquema, consulte uso @contact
Inaccessible
aplicável em todas as definições de tipo, consulte a documentação @inaccessible
InterfaceObject
(desde v2.3) aplicável em objetos, consulte a documentação @interfaceObject
Key(fieldset)
aplicável em objetos, consulte a documentação @key
Link(url, [import]?)
aplicável no esquema, consulte a documentação @link
NonResolvableKey(fieldset)
aplicável em objetos, consulte a documentação @key
não resolvívelRequiresScopes(scopes)
(desde v2.5) aplicável em enum, campo, interface e objeto, documentação @requiresScopes
Shareable
aplicável em campos e objetos, consulte a documentação @shareable
Resolução de entidade
ResolveReferenceWith
para poder resolver as entidades Consulte a documentação do HotChocolate para obter detalhes sobre o suporte do servidor para interface de linha de comando. Para gerar o esquema no momento da construção, você precisa adicionar dependência adicional no pacote HotChocolate.AspNetCore.CommandLine
e configurar seu servidor para permitir RunWithGraphQLCommands
.
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( )
// register your types and services
;
var app = builder . Build ( ) ;
app . MapGraphQL ( ) ;
app . RunWithGraphQLCommands ( args ) ;
Você pode então gerar seu esquema executando
dotnet run -- schema export --output schema.graphql
Por padrão, ApolloGraphQL.HotChocolate.Federation
gerará esquema usando a versão mais recente da Federação suportada. Se desejar optar por usar versões mais antigas, você pode especificar a versão ao configurar a extensão AddApolloFederationV2
.
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( FederationVersion . FEDERATION_23 )
// register your types and services
;
Como alternativa, você também pode fornecer FederatedSchema
personalizado direcionado a uma versão específica da Federação
public class CustomSchema : FederatedSchema
{
public CustomSchema ( ) : base ( FederationVersion . FEDERATION_23 ) {
}
}
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( new CustomSchema ( ) )
// register your types and services
;
Se desejar personalizar seu esquema aplicando algumas diretivas, você também pode fornecer FederatedSchema
personalizado que pode ser anotado com atributos que estendem SchemaTypeDescriptorAttribute
[ AttributeUsage ( AttributeTargets . Class | AttributeTargets . Struct , Inherited = true , AllowMultiple = true ) ]
public sealed class CustomAttribute : SchemaTypeDescriptorAttribute
{
public override void OnConfigure ( IDescriptorContext context , ISchemaTypeDescriptor descriptor , Type type )
{
// configure your directive here
}
}
[ Custom ]
public class CustomSchema : FederatedSchema
{
public CustomSchema ( ) : base ( FederationVersion . FEDERATION_23 ) {
}
}
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( new CustomSchema ( ) )
// register your types and services
;
Alternativamente, você também pode especificar uma ação de configuração de esquema personalizada ao construir subgráfico federado
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( schemaConfiguration : s =>
{
// apply your directive here
} )
// register your types and services
;
@key
não resolvível Seus subgráficos podem usar uma entidade como tipo de retorno de campo sem contribuir com nenhum campo para essa entidade. Como ainda precisamos de uma definição de tipo para gerar um esquema válido, podemos definir um objeto stub com [NonResolvableKeyAttribute]
.
public class Review {
public Review ( Product product , int score )
{
Product = product ;
Score = score
}
public Product Product { get ; }
public int Score { get ; }
}
[ NonResolvableKey ( " id " ) ]
public class Product {
public Product ( string id )
{
Id = id ;
}
public string Id { get ; }
}
@composedDirective
Por padrão, o esquema Supergraph exclui todas as diretivas personalizadas. O @composeDirective
é usado para especificar diretivas personalizadas que devem ser preservadas no esquema Supergraph.
ApolloGraphQL.HotChocolate.Federation
fornece uma classe FederatedSchema
comum que aplica automaticamente a definição @link
do Apollo Federation v2. Ao aplicar qualquer diretiva de esquema customizada, você deve estender esta classe e adicionar atributos/diretivas necessários.
Ao aplicar @composedDirective
você também precisa @link
sua especificação. Seu esquema customizado deve então ser passado para a extensão AddApolloFederationV2
.
[ 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
;
Como alternativa, você pode aplicar @composedDirective
aplicando-o diretamente em um esquema de destino usando a ação de configuração
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( schemaConfiguration : s =>
{
s . Link ( " https://myspecs.dev/myCustomDirective/v1.0 " , new string [ ] { " @custom " } ) ;
s . ComposeDirective ( " @custom " ) ;
} )
// register your types and services
;
@interfaceObject
Apollo Federation v2 suporta interfaces de entidade , uma extensão poderosa para as interfaces GraphQL que permite estender a funcionalidade de uma interface através do supergrafo sem ter que implementar (ou mesmo estar ciente de) todos os seus tipos de implementação.
Em um subgrafo que define a interface precisamos aplicar @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 ; }
}
Podemos então estender a interface em outro subgrafo tornando-a um tipo, aplicando @interfaceObject
e a mesma diretiva @key
. Isso permite que você adicione novos campos a cada entidade que implementa sua interface (por exemplo, adicionando o campo Reviews
a todas as implementações Product
).
[ Key ( " id " ) ]
[ InterfaceObject ]
public class Product
{
[ ID ]
public string Id { get ; set ; }
public List < string > Reviews { get ; set ; }
}
A diretiva @requiresScopes
é usada para indicar que o elemento de destino é acessível apenas aos usuários autenticados do supergrafo com os escopos JWT apropriados. Consulte o artigo do Apollo Router para obter detalhes adicionais.
public class Query
{
[ RequiresScopes ( scopes : new string [ ] { " scope1, scope2 " , " scope3 " } ) ]
[ RequiresScopes ( scopes : new string [ ] { " scope4 " } ) ]
public Product ? GetProduct ( [ ID ] string id , Data repository )
=> repository . Products . FirstOrDefault ( t => t . Id . Equals ( id ) ) ;
}
Isso irá gerar o seguinte esquema
type Query {
product ( id : ID ! ): Product @requiresScopes ( scopes : [ [ " scope1, scope2 " , " scope3 " ], [ " scope4 " ] ])
}
Você pode usar a diretiva @contact
para adicionar as informações de contato da sua equipe a um esquema de subgráfico. Essas informações são exibidas no Studio, o que ajuda outras equipes a saber quem contatar para obter assistência com o subgráfico. Consulte a documentação para obter detalhes.
Precisamos aplicar o atributo [Contact]
em um esquema. Você pode aplicar o atributo [Contact]
em um esquema customizado e passar seu esquema customizado para a extensão AddApolloFederationV2
.
[ 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
;
ou aplique a diretiva @contact
diretamente em um esquema, fornecendo uma ação de configuração de esquema personalizada
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Services
. AddGraphQLServer ( )
. AddApolloFederationV2 ( schemaConfiguration : s =>
{
s . Contact ( " MyTeamName " , " https://myteam.slack.com/archives/teams-chat-room-url " , " send urgent issues to [#oncall](https://yourteam.slack.com/archives/oncall) " ) ;
} )
// register your types and services
;
ApolloGraphQL.HotChocolate.Federation
usa automaticamente como padrão o nome do tipo de Query
. Ao usar tipos de operação Query
raiz personalizados, você deve configurar explicitamente o esquema com esses valores personalizados.
public class CustomQuery
{
public Foo ? GetFoo ( [ ID ] string id , Data repository )
=> repository . Foos . FirstOrDefault ( t => t . Id . Equals ( id ) ) ;
}
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Services
. AddGraphQLServer ( )
. ModifyOptions ( opts => opts . QueryTypeName = " CustomQuery " )
. AddApolloFederationV2 ( )
. AddQueryType < CustomQuery > ( )
// register your other types and services
;
var app = builder . Build ( ) ;
app . MapGraphQL ( ) ;
app . Run ( ) ;
Migrar de HotChocolate.Federation
para ApolloGraphQL.HotChocolate.Federation
é fácil. Basta atualizar a importação do seu pacote para apontar para um novo módulo
- +
e atualizar importações de namespace
- using HotChocolate.ApolloFederation;
+ using ApolloGraphQL.HotChocolate.Federation;
Embora tentássemos tornar o processo de migração o mais simples possível, tivemos que fazer alguns ajustes na biblioteca. Devido à dependência de algumas APIs internas, tivemos que fazer as seguintes alterações importantes na biblioteca:
[Key]
agora é aplicável apenas em aulas e você não pode mais aplicá-la em campos individuais[ReferenceResolver]
agora é aplicável apenas em métodos estáticos públicos dentro de uma entidade , não é mais aplicável em classes [EntityResolver]
s podem mapear automaticamente a representação da entidade para os valores @key
/ @requires
suportados. Os campos escalares @key
são mapeados automaticamente e podemos usar o atributo [Map]
para mapear automaticamente valores escalares de conjuntos de seleção complexos.
Atualmente não oferecemos suporte ao mapeamento automático de valores de Lista e Objeto.
Como solução alternativa, você precisa analisar manualmente o objeto de representação em sua implementação.
[ ReferenceResolver ]
public static Foo GetByFooBar (
[ LocalState ] ObjectValueNode data
Data repository )
{
// TODO implement logic here by manually reading values from local state data
}
@link
limitadoAtualmente oferecemos suporte apenas à importação de elementos dos subgráficos referenciados.
Atualmente, o namespace e a renomeação de elementos não são suportados. Consulte o problema para obter detalhes.
Se você tiver uma pergunta específica sobre a biblioteca ou código, inicie uma discussão nos fóruns da comunidade Apollo ou inicie uma conversa em nosso servidor Discord.
Para começar, faça um fork do repositório e verifique um novo branch. Você pode então construir a biblioteca localmente executando
# install dependencies
dotnet restore
# build project
dotnet build
# run tests
dotnet test
Veja mais informações em CONTRIBUTING.md.
Depois de configurar sua filial local, dê uma olhada em nossos problemas em aberto para ver onde você pode contribuir.
Para obter mais informações sobre como entrar em contato com a equipe para questões de segurança, consulte nossa Política de Segurança.
Esta biblioteca está licenciada sob a licença MIT (MIT).