Graphql 联合在扩展关系上返回 null

Posted

技术标签:

【中文标题】Graphql 联合在扩展关系上返回 null【英文标题】:Graphql federation returning null on extended relation 【发布时间】:2020-09-09 14:34:25 【问题描述】:

我正在尝试设置一个原型,以便在多个 java 微服务中使用 graphql,这需要我将多个 graphql 模式合并为一个。

我正在使用 2 个 java 服务和带有 ApolloGateway 的 ApolloServer;它在操场上显示了以下架构:

type Client 
  id: ID!
  name: String
  linkeduser: User


type Query 
  user(id: ID!): User
  users: [User]
  client(id: ID!): Client
  clients: [Client]


type User 
  id: ID!
  name: String

运行简单查询时:

query client 
  client(id: 1) 
    id
    name
    linkeduser 
      id
      name
    
  

我希望返回的是一个带有linkeduser 的客户端;调试时客户端服务被查询,用户服务被查询,但响应是:


  "data": 
    "client": 
      "id": "1",
      "name": "Bob",
      "linkeduser": null
    
  

如何在我的客户端中获得链接的用户响应?

我尝试返回用户列表、带有链接用户列表的新客户端对象、单个用户。 https://github.com/apollographql/federation-jvm 的例子是这段代码的基础,虽然我还没有看到它工作。

代码:

服务 1:客户端


@WebServlet(loadOnStartup = 1, urlPatterns = "/graphql")
public class GraphQLService extends GraphQLHttpServlet 
    @Override
    protected GraphQLConfiguration getConfiguration() 
        return GraphQLConfiguration.with(getGraphQLSchema()).build();
    

    private static GraphQLSchema getGraphQLSchema() 
        InputStream inputStream = client.GraphQLService.class
            .getClassLoader().getResourceAsStream("schema.graphqls");
        TypeDefinitionRegistry parse = new SchemaParser().parse(inputStream);
        RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
            .type("Query", builder -> builder.defaultDataFetcher(GraphQLService::getClient))
            .build();
        return com.apollographql.federation.graphqljava.Federation.transform(parse, runtimeWiring)
            .fetchEntities(env -> env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
                .stream()
                .map(values -> 
                    if ("Client".equals(values.get("__typename"))) 
                        final Object id = values.get("id");
                        if (id instanceof String) 
                            return getSingleClient((String) id);
                        
                    
                    return null;
                )
                .collect(Collectors.toList()))
            .resolveEntityType(env -> 
                final Object src = env.getObject();
                if (src instanceof Client) 
                    return env.getSchema().getObjectType("Client");
                
                return null;
            ).build();
    

    private static Object getClient(DataFetchingEnvironment environment) 
        switch (environment.getFieldDefinition().getName()) 
            case "client":
                return getSingleClient(environment.getArgument("id"));
            case "clients":
                return getAllClients();
            default:
                return null;
        
    

    //... extra code with simple getters

使用此架构:

extend type Query 
    client(id: ID!): Client
    clients: [Client]


type Client @key(fields: "id")
    id: ID!
    name: String

服务 2:用户


@WebServlet(loadOnStartup = 1, urlPatterns = "/graphql")
public class GraphQLService extends GraphQLHttpServlet 

    @Override
    protected GraphQLConfiguration getConfiguration() 
        return GraphQLConfiguration.with(getGraphQLSchema()).build();
    

    private static GraphQLSchema getGraphQLSchema() 
        InputStream inputStream = user.GraphQLService.class
            .getClassLoader().getResourceAsStream("schema.graphqls");
        TypeDefinitionRegistry parse = new SchemaParser().parse(inputStream);
        RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
            .type("Query", builder -> builder.defaultDataFetcher(GraphQLService::getUser))
            .build();
        return com.apollographql.federation.graphqljava.Federation.transform(parse, runtimeWiring)
            .fetchEntities(env -> env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
                .stream()
                .map(values -> 
                    if ("Client".equals(values.get("__typename"))) 
                        final Object id = values.get("id");
                        if (id instanceof String) 
                            return getSingleUser((String) id);
                        
                    
                    return null;
                )
                .collect(Collectors.toList()))
            .resolveEntityType(env -> 
                final Object src = env.getObject();
                if (src instanceof User) 
                    return env.getSchema().getObjectType("User");
                
                return null;
            )
            .build();
    

    private static Object getUser(DataFetchingEnvironment environment) 
        switch (environment.getFieldDefinition().getName()) 
            case "user":
                return getSingleUser(environment.getArgument("id"));
            case "users":
                return getAllUsers();
            default:
                return null;
        
    
    //... extra code with simple getters

使用此架构:

type Query @extends
    user (id: ID!): User
    users: [User]


type User @key(fields: "id") 
    id: ID!
    name: String


type Client @key(fields: "id") @extends
    id: ID! @external
    linkeduser : User

POM.xml 中的版本

<graphql.version>14.0</graphql.version>
<graphql-tools.version>5.2.4</graphql-tools.version>
<graphql-servlet.version>9.0.1</graphql-servlet.version>
<graphql-federation-support.version>0.4.0</graphql-federation-support.version>

【问题讨论】:

【参考方案1】:

在用户服务中,您需要返回一个客户端类型的 pojo,并带有一个用于链接用户的 getter(仅需要存在扩展字段)

if ("Client".equals(values.get("__typename"))) 
    final Object id = values.get("id");
    if (id instanceof String) 
        return new Client((String) id, getSingleUser((String) id));
    

resolveTypeEntity 也需要解析到所述客户端

【讨论】:

以上是关于Graphql 联合在扩展关系上返回 null的主要内容,如果未能解决你的问题,请参考以下文章

联合内的 GraphQL 联合

无法在 GraphQL 中返回关系数据

为啥 Apollo/GraphQL 返回一个不可扩展的对象?

Graphql Apollo Federation - 基于实现服务解析计算域

GraphQL 解析器为具有关系的对象返回啥?

GraphQL 查询结果中未返回的扩展