使用 graphql-java 构建 Relay GraphQL 服务器
Posted
技术标签:
【中文标题】使用 graphql-java 构建 Relay GraphQL 服务器【英文标题】:Building a Relay GraphQL server with graphql-java 【发布时间】: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 等)如何?
【问题讨论】:
【参考方案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;
))
【讨论】:
以上是关于使用 graphql-java 构建 Relay GraphQL 服务器的主要内容,如果未能解决你的问题,请参考以下文章
graphql-java:如何在以编程方式生成 graphql 模式时添加自定义标量类型?