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
HotChocolateaté que sua API de substituição (atualmente em andamento) seja concluída. Só podemos oferecer suporte às versões v13.5.xe 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 >
<!-- make sure to also include HotChocolate package -->
< PackageReference Include = " HotChocolate.AspNetCore " Version = " 13.6.0 " />
<!-- federation package -->
< 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 @extendsExternal aplicável em campos, consulte a documentação @externalKey aplicável em objetos, consulte a documentação @keyProvides aplicável em campos, consulte a documentação @providesRequires aplicável nos campos, consulte a documentação @requiresDiretivas da Federação v2 (inclui todas as diretivas v1)
ApolloTag aplicável no esquema, consulte a documentação @tagApolloAuthenticated (desde v2.5) aplicável em enum, campo, interface e objeto, documentação @authenticatedComposeDirective (desde v2.1) aplicável no esquema, consulte a documentação @composeDirectiveContact aplicável no esquema, consulte uso @contactInaccessible aplicável em todas as definições de tipo, consulte a documentação @inaccessibleInterfaceObject (desde v2.3) aplicável em objetos, consulte a documentação @interfaceObjectKeyInterface aplicável em interfaces, consulte a documentação da interface de entidade @keyLink aplicável no esquema, consulte a documentação @linkRequiresScopes (desde v2.5) aplicável em enum, campo, interface e objeto, documentação @requiresScopesShareable aplicável no esquema, consulte a documentação @shareableResoluçã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 barReferenceResolver 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 @extendsExternal aplicável em campos, consulte a documentação @externalKey(fieldset) aplicável em objetos, consulte a documentação @keyProvides(fieldset) aplicável em campos, consulte a documentação @providesRequires(fieldset) aplicável em campos, consulte a documentação @requiresDiretivas da Federação v2 (inclui todas as diretivas v1)
ApolloTag aplicável em todas as definições de tipo, consulte a documentação @tagApolloAuthenticated (desde v2.5) aplicável em enum, campo, interface e objeto, documentação @authenticatedComposeDirective(name) (desde v2.1) aplicável no esquema, consulte a documentação @composeDirectiveContact(name, url?, description?) aplicável no esquema, consulte uso @contactInaccessible aplicável em todas as definições de tipo, consulte a documentação @inaccessibleInterfaceObject (desde v2.3) aplicável em objetos, consulte a documentação @interfaceObjectKey(fieldset) aplicável em objetos, consulte a documentação @keyLink(url, [import]?) aplicável no esquema, consulte a documentação @linkNonResolvableKey(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 @requiresScopesShareable aplicável em campos e objetos, consulte a documentação @shareableResoluçã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 irá 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
; @interfaceObjectApollo 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 padroniza automaticamente para usar o nome do tipo 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
<ItemGroup>
<!-- make sure to also include HotChocolate package -->
<PackageReference Include="HotChocolate.AspNetCore" Version="13.6.0" />
<!-- federation package -->
- <PackageReference Include="HotChocolate.ApolloFederation" Version="$LatestVersion" />
+ <PackageReference Include="ApolloGraphQL.HotChocolate.Federation" Version="$LatestVersion" />
</ItemGroup>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 testVeja 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.