【问题标题】:Building a Relay GraphQL server with graphql-java使用 graphql-java 构建 Relay GraphQL 服务器
【发布时间】:2020-12-02 06:51:57
【问题描述】:

我们正在使用 graphql-java 构建一个 graphql 服务器,但在符合 Relay 规范时遇到了问题。根据 Relay 规范,所有节点都必须可以通过单个查询检索:node(id: ID)。 graphql-java 文档显示了对 Relay 节点接口的支持,但是实际实现这一点的文档相当稀少。我们遇到的确切问题是了解如何为节点查询生成通用查找? Relay Todo 示例:https://github.com/graphql-java/todomvc-relay-java 展示了一种极其简单的基于代码的方法,使用单个数据提取器,此处的示例永远不需要“读取”节点“类型”或将该请求委托给正确的数据提取器。

schema.graphqls:

type Query {
    node(id: ID!): Node
    user(id: ID!): User
    otherType(id:ID!): OtherType
}

interface Node {
   id: ID!
}

type User implements Node {
   id: ID!
   firstName: String
   lastName: String
}

type OtherType implements Node {
   id: ID!
   description: String
   stuff: String
}

我们目前正在使用 SDL 来生成我们的架构(如下所示)

@Bean
private GraphQLSchema schema() {
     Url url = Resources.getResource("schema.graphqls");
     String sdl = Resources.toString(url, Charsets.UTF_8);
     GraphQLSchema graphQLSchema = buildSchema(sdl);
     this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
     return graphQLSchema;
}
private GraphQLSchema buildSchema(String sdl) {
      TypeDefinitionRegistry typeRegistry - new SchemaParser().parse(sdl);
      RuntimeWiring runtimeWiring = buildWiring();
      SchemaGenerator schemaGenerator = new SchemaGenerator();
      return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
private RuntimeWiring buildWiring(){
    return RuntimeWiring.newRuntimeWiring()
    .type(newTypeWiring("Query")
          .dataFetcher("user", userDispatcher.getUserById()))
    .type(newTypeWiring("Query")
          .dataFetcher("otherType", otherTypeDispatcher.getOtherTypeById()))
    .type(newTypeWiring("Query")
          .dataFetcher("node", /*How do we implement this data fetcher? */))
    .build()
}

假设我们必须连接一个数据提取器,如上面最后一行所示。然而,这是文档开始变得模糊的地方。以上是否符合 Relay 规范?我们将如何实现一个单节点 dataFetcher,它返回对任何单个节点的引用,而不管底层类型(User、OtherType 等)如何?

【问题讨论】:

    标签: relay graphql-java relaymodern graphql-spring-boot


    【解决方案1】:

    你是对的,你需要实现一个节点数据获取器,它将从节点 id 返回一个节点。

    我将假设您使用的是底层 sql 数据库:节点可以是任何类型,这意味着您不能将数据库 id 用作节点 id(因为不同类型的两个节点可能具有相同的数据库 id )。您需要构建自己的节点 ID。

    构建此类 id 的一种简单方法是将对象类型和对象 id 连接起来。示例:“用户:4234”。

    作为中继 graphql 服务器规范指定:

    我们返回的 ID 是 base64 字符串。 ID 设计为不透明的 (唯一应该传递给节点上的 id 参数的是 在系统中的某个对象上查询 id 的不变结果),以及 对字符串进行 base64 处理是 GraphQL 中提醒查看者的有用约定 该字符串是一个不透明的标识符。

    我们需要将节点 ID 编码/解码为 base 64。

    以下代码尚未经过测试,但可以让您了解如何继续。

    DataFetcher dataFetcher = new DataFetcher() {
                
      @Override
      Object get(DataFetchingEnvironment environment) {
        Object argument = environment.getArgument("id");
        if(argument instanceof String) {
          String node = (String) argument;
          //TODO decode base 64 nodeId
          //TODO split nodeId into String `nodeType` and Long `id`
          //TODO retrieve your object of type `nodeType` and id `id`
          
        }
      }
    }
    

    【讨论】:

      【解决方案2】:

      模式中的 Node 接口需要一个 TypeResolver 和 Data Fetcher 实现。

      即使您提供数据获取器实现并按照 AllirionX 的建议进行连接,您仍然需要为 Node 接口提供解析器,以帮助 graphql-java 了解它应该解析哪个具体对象类型。

      .type(newTypeWiring("Query").dataFetcher("node", new NodeDataFetcher()))
      

      类似的东西-

      .type(newTypeWiring("Node").typeResolver(new TypeResolver() {
                      @Override
                      public GraphQLObjectType getType(TypeResolutionEnvironment env) {
                          return null;
                      }
                  }))
      

      【讨论】:

        猜你喜欢
        • 2016-02-10
        • 2016-07-26
        • 1970-01-01
        • 2020-09-30
        • 2020-11-19
        • 2017-08-08
        • 2018-02-12
        • 2015-11-27
        • 2016-07-03
        相关资源
        最近更新 更多