1. Overview
1.概述
GraphQL is a query language, created by Facebook with the purpose of building client applications based on intuitive and flexible syntax, for describing their data requirements and interactions.
GraphQL是一种查询语言,由Facebook创建,目的是在直观和灵活的语法基础上建立客户端应用程序,用于描述其数据要求和互动。
One of the primary challenges with traditional REST calls is the inability of the client to request a customized (limited or expanded) set of data. In most cases, once the client requests information from the server, it either gets all or none of the fields.
传统REST调用的主要挑战之一是客户端无法请求定制(有限的或扩展的)数据集。在大多数情况下,一旦客户端向服务器请求信息,它要么得到所有字段,要么得不到。
Another difficulty is working and maintain multiple endpoints. As a platform grows, consequently the number will increase. Therefore, clients often need to ask for data from different endpoints.
另一个困难是工作和维护多个端点。随着一个平台的发展,随之而来的是数量的增加。因此,客户经常需要从不同的端点索取数据。
When building a GraphQL server, it is only necessary to have one URL for all data fetching and mutating. Thus, a client can request a set of data by sending a query string, describing what they want, to a server.
在构建GraphQL服务器时,只需要有一个URL,用于所有数据的获取和变异。因此,客户端可以通过向服务器发送一个查询字符串,描述他们想要的东西,来请求一组数据。
2. Basic GraphQL Nomenclature
2.基本的GraphQL命名法
Let’s have a look at GraphQL’s basic terminology.
让我们看一下GraphQL的基本术语。
- Query: is a read-only operation requested to a GraphQL server
- Mutation: is a read-write operation requested to a GraphQL server
- Resolver: In GraphQL, the Resolver is responsible for mapping the operation and the code running on the backend which is responsible for handle the request. It is analogous to MVC backend in a RESTFul application
- Type: A Type defines the shape of response data that can be returned from the GraphQL server, including fields that are edges to other Types
- Input: like a Type, but defines the shape of input data that is sent to a GraphQL server
- Scalar: is a primitive Type, such as a String, Int, Boolean, Float, etc
- Interface: An Interface will store the names of the fields and their arguments, so GraphQL objects can inherit from it, assuring the use of specific fields
- Schema: In GraphQL, the Schema manages queries and mutations, defining what is allowed to be executed in the GraphQL server
2.1. Schema Loading
2.1.模式加载
There are two ways of loading a schema into GraphQL server:
有两种方法可以将模式加载到GraphQL服务器。
- by using GraphQL’s Interface Definition Language (IDL)
- by using one of the supported programming languages
Let’s demonstrate an example using IDL:
让我们用IDL来演示一个例子。
type User {
firstName: String
}
Now, an example of schema definition using Java code:
现在,一个使用Java代码的模式定义的例子。
GraphQLObjectType userType = newObject()
.name("User")
.field(newFieldDefinition()
.name("firstName")
.type(GraphQLString))
.build();
3. Interface Definition Language
3.界面定义语言
Interface Definition Language (IDL) or Schema Definition Language (SDL) is the most concise way to specify a GraphQL Schema. The syntax is well-defined and will be adopted in the official GraphQL Specification.
接口定义语言(IDL)或模式定义语言(SDL)是指定GraphQL模式的最简洁的方式。该语法定义明确,将在官方GraphQL规范中采用。
For instance, let’s create a GraphQL schema for a User/Emails could be specified like this:
例如,让我们为用户/电子邮件创建一个GraphQL模式,可以像这样指定。
schema {
query: QueryType
}
enum Gender {
MALE
FEMALE
}
type User {
id: String!
firstName: String!
lastName: String!
createdAt: DateTime!
age: Int! @default(value: 0)
gender: [Gender]!
emails: [Email!]! @relation(name: "Emails")
}
type Email {
id: String!
email: String!
default: Int! @default(value: 0)
user: User @relation(name: "Emails")
}
4. GraphQL-java
4.GraphQL-java
GraphQL-java is an implementation based on the specification and the JavaScript reference implementation. Note that it requires at least Java 8 to run properly.
GraphQL-java是基于规范和JavaScript参考实现的一个实现。注意,它至少需要Java 8才能正常运行。
4.1. GraphQL-java Annotations
4.1.GraphQL-java注解
GraphQL also makes it possible to use Java annotations to generate its schema definition without all the boilerplate code created by the use of the traditional IDL approach.
GraphQL还可以使用Java注解来生成其模式定义,而无需使用传统的IDL方法所产生的所有模板代码。
4.2. Dependencies
4.2.依赖性
To create our example, let’s firstly start importing the required dependency which is relying on Graphql-java-annotations module:
为了创建我们的例子,首先让我们开始导入所需的依赖关系,该依赖关系依赖于Graphql-java-annotations模块。
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-annotations</artifactId>
<version>3.0.3</version>
</dependency>
We are also implementing an HTTP library to ease the setup in our application. We are going to use Ratpack (although it could be implemented as well with Vert.x, Spark, Dropwizard, Spring Boot, etc.).
我们还将实现一个HTTP库,以减轻我们应用程序中的设置。我们将使用Ratpack(尽管它也可以用Vert.x、Spark、Dropwizard、Spring Boot等实现)。
Let’s also import the Ratpack dependency:
让我们也导入Ratpack的依赖关系。
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-core</artifactId>
<version>1.4.6</version>
</dependency>
4.3. Implementation
4.3.实施
Let’s create our example: a simple API that provides a “CRUDL” (Create, Retrieve, Update, Delete, and List) for users. First, let’s create our User POJO:
让我们创建我们的例子:一个简单的API,为用户提供一个 “CRUDL”(创建、检索、更新、删除和列表)。首先,让我们创建我们的User POJO。
@GraphQLName("user")
public class User {
@GraphQLField
private Long id;
@GraphQLField
private String name;
@GraphQLField
private String email;
// getters, setters, constructors, and helper methods omitted
}
In this POJO we can see the @GraphQLName(“user”) annotation, as an indication that this class is mapped by GraphQL along with each field annotated with @GraphQLField.
在这个POJO中,我们可以看到@GraphQLName(“user”)注释,这表明这个类是由GraphQL映射的,同时每个字段都有@GraphQLField.注释。
Next, we’ll create the UserHandler class. This class inherits from the chosen HTTP connector library (in our case, Ratpack) a handler method, which will manage and invoke the GraphQL’s Resolver feature. Thus, redirecting the request (JSON payloads) to the proper query or mutation operation:
接下来,我们将创建UserHandler类。这个类从所选择的HTTP连接器库(在我们的例子中是Ratpack)继承了一个处理方法,它将管理和调用GraphQL的Resolver功能。因此,将请求(JSON有效载荷)重定向到适当的查询或变异操作。
@Override
public void handle(Context context) throws Exception {
context.parse(Map.class)
.then(payload -> {
Map<String, Object> parameters = (Map<String, Object>)
payload.get("parameters");
ExecutionResult executionResult = graphql
.execute(payload.get(SchemaUtils.QUERY)
.toString(), null, this, parameters);
Map<String, Object> result = new LinkedHashMap<>();
if (executionResult.getErrors().isEmpty()) {
result.put(SchemaUtils.DATA, executionResult.getData());
} else {
result.put(SchemaUtils.ERRORS, executionResult.getErrors());
LOGGER.warning("Errors: " + executionResult.getErrors());
}
context.render(json(result));
});
}
Now, the class that will support the query operations, i.e., UserQuery. As mentioned all methods that retrieve data from the server to the client are managed by this class:
现在,将支持查询操作的类,即UserQuery。如前所述,所有从服务器检索数据到客户端的方法都由该类管理。
@GraphQLName("query")
public class UserQuery {
@GraphQLField
public static User retrieveUser(
DataFetchingEnvironment env,
@NotNull @GraphQLName("id") String id) {
// return user
}
@GraphQLField
public static List<User> listUsers(DataFetchingEnvironment env) {
// return list of users
}
}
Similarly to UserQuery, now we create UserMutation, which will manage all the operations that intend to change some given data stored on the server side:
与UserQuery类似,现在我们创建UserMutation,它将管理所有打算改变存储在服务器端的一些给定数据的操作。
@GraphQLName("mutation")
public class UserMutation {
@GraphQLField
public static User createUser(
DataFetchingEnvironment env,
@NotNull @GraphQLName("name") String name,
@NotNull @GraphQLName("email") String email) {
//create user information
}
}
It is worth notice the annotations in both UserQuery and UserMutation classes: @GraphQLName(“query”) and @GraphQLName(“mutation”). Those annotations are used to define the query and mutation operations respectively.
值得注意的是UserQuery和UserMutation类中的注释。@GraphQLName(“query”)和@GraphQLName(“mutation”)。这些注解分别用于定义查询和突变操作。
With the GraphQL-java server able to run the query and mutation operations, we can use the following JSON payloads to test the request of the client against the server:
随着GraphQL-java服务器能够运行查询和变异操作,我们可以使用以下JSON有效载荷来测试客户端对服务器的请求。
- For the CREATE operation:
{
"query": "mutation($name: String! $email: String!){
createUser (name: $name email: $email) { id name email age } }",
"parameters": {
"name": "John",
"email": "john@email.com"
}
}
As the response from the server for this operation:
作为该操作的服务器的响应。
{
"data": {
"createUser": {
"id": 1,
"name": "John",
"email": "john@email.com"
}
}
}
- For the RETRIEVE operation:
{
"query": "query($id: String!){ retrieveUser (id: $id) {name email} }",
"parameters": {
"id": 1
}
}
As the response from the server for this operation:
作为该操作的服务器的响应。
{
"data": {
"retrieveUser": {
"name": "John",
"email": "john@email.com"
}
}
}
GraphQL provides features the client can customize the response. So, in the last RETRIEVE operation used as the example, instead of returning the name and email, we can, for instance, return only the email:
GraphQL提供了客户端可以自定义响应的功能。因此,在作为例子的最后一个RETRIEVE操作中,我们可以不返回姓名和电子邮件,而是,例如,只返回电子邮件。
{
"query": "query($id: String!){ retrieveUser (id: $id) {email} }",
"parameters": {
"id": 1
}
}
So, the returning information from the GraphQL server will only return the requested data:
因此,从GraphQL服务器返回的信息将只返回请求的数据。
{
"data": {
"retrieveUser": {
"email": "john@email.com"
}
}
}
5. Conclusion
5.结论
GraphQL is an easy and quite attractive way of minimizing complexity between client/server as an alternative approach to REST APIs.
GraphQL作为REST API的替代方法,是一种简单且颇具吸引力的方法,可以最大限度地降低客户/服务器之间的复杂性。
As always, the example is available at our GitHub repository.
一如既往,该示例可在我们的GitHub 仓库获得。