GraphQL标准提供了多种主流编程语言的实现,本文介绍其中的Java实现graphql-java。所谓GraphQL标准的实现,就是一个GraphQL服务器的实现,通过一组软件组件,以支持请求的解析、验证和执行GraphQL查询。
graphql-java就是GraphQL标准的Java语言实现之一,当前最新版本是8.0。在Maven项目中配置如下:
com.graphql-java
graphql-java
8.0
本文主要介绍graphql-java的核心的GraphQL Schema定义。
1. GraphQL Schema
就类似于数据库服务器要响应用户对数据库的访问,首先要定义数据库Schema一样。GraphQL服务器要是响应用户对REST数据的查询请求,也要定义一个描述服务数据的结构,就是GraphQL Schema。
为了定义GraphQL Schema,graphql-java提供了如下两种定义的方式:
GraphQLObjectType fooType = newObject()
.name("Ci")
.field(newFieldDefinition()
.name("product_number")
.type(GraphQLString))
.build();
如代码所示,运行中创建了新类型Ci,并为其定义了属性product_number,该属性的类型为Scalar的GraphQLString。
创建src/main/resources/myschema.graphqls文件,定义类型Ci如下:
type Ci {
product_number: String
}
这里,为了示例,两种方式定义的GraphQL Schema完全等价。
实际上定义完整的GraphQL Schema还有具体的语法,请参考创建GraphQL Schema。
2. DataFetcher/TypeResolver
GraphQL Schema中的每个字段类型,都关联一个DataFetcher(在Node.js实现或Python实现中被称为resolver函数),用以为GraphQL Schema中的字段类型获取数据。数据源可以是数据库或REST服务,并将数据表示为Java POJO对象。DataFetcher对象可以直接读取Java POJO对象的属性,再使用Jackson或GSON转换为JSON格式的数据。
DataFetcher pnDataFetcher = new DataFetcher() {
@Override
public Object get(DataFetchingEnvironment environment) {
Map response = fetchPnFromDatabase(environment.getArgument("product_number"));
List errors = ((List)response.get("errors")).stream()
.map(MyMapGraphQLError::new)
.collect(Collectors.toList());
return new DataFetcherResult(response.get("data"), errors);
}
};
GraphQLObjectType objectType = newObject()
.name("Ci")
.field(newFieldDefinition()
.name("product_number")
.type(GraphQLString)
.dataFetcher(pnDataFetcher))
.build();
注意,这里的dataFetcher()方法的使用。
与DataFetcher获取数据的作用相似,TypeResolver提供了更强大的功能,能够根据获取的数据转换为对应的Schema中定义的字段类型,这对面向接口的编程有很大的意义。在一个获取数据的方法中,定义返回的类型是接口,而实际返回可能是实现该接口的任何对象,根据获取的数据转换为对应的对象,返回即可。
3. Runtime Wiring
创建完毕GraphQL Schema还只是开始,还要将其应用在GraphQL服务器上。这就类似于定义数据库结构的SDL脚本已经准备就绪,但是真正在数据库服务器上执行后才生效。
在GraphQL服务器上真正执行GraphQL Schema使其运行起来,是通过Runtime Wiring,这样才能够得到一个可执行的GraphQL Schema。典型代码如下:
File schemaFile = loadSchema("myschema.graphqls");
SchemaParser schemaParser = new SchemaParser();
TypeDefinitionRegistry typeRegistry = schemaParser.parse(schemaFile);
RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()....build();
SchemaGenerator schemaGenerator = new SchemaGenerator();
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
4. GraphQL服务
已经有了完整的可响应请求的GraphQL Schema,现在就运行起来吧。
GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();
5. Execution请求GraphQL服务
1) 请求参数
在请求应用中,可以直接将请求参数以字符串的形式传递给GraphQL服务,如下所示:
ExecutionResult executionResult = build.execute("query { hero { name } }");
也可以先构建ExecutionInput对象(适合复杂的请求参数),然后再传递给GraphQL服务,如下所示:
ExecutionInput executionInput = ExecutionInput.newExecutionInput().query("query { hero { name } }")
.build();
ExecutionResult executionResult = build.execute(executionInput);
2)处理结果
GraphQL服务的响应结果在data中,如果出错则在
executionResult.getData().toString();
List errors = executionResult.getErrors();
如果要将得到的响应数据序列化为JSON格式,为了完全兼容GraphQL标准,建议首先将响应结果进行规范化转换,然后再调用Jackson或GSON等JSON库进行JSON格式转换。
Map toSpecificationResult = executionResult.toSpecification();
doWithJson(toSpecificationResult);
这里只讨论了同步的请求,异步请求可以参考其文档。
参考链接:
https://github.com/graphql-java/graphql-java
http://graphql-java.readthedocs.io/en/latest/