1. 概述

GraphQL 是由 Facebook 开发的查询语言,旨在构建基于直观灵活语法的客户端应用,用于描述数据需求和交互。

传统 REST 调用存在两大痛点:

  • ❌ 客户端无法请求定制化数据(要么全拿,要么全不要)
  • ❌ 随着平台增长,需要维护大量接口,客户端常需跨多个接口请求数据

GraphQL 通过单一 URL 解决这些问题:客户端只需发送描述需求的查询字符串,即可获取精确数据。

2. GraphQL 核心术语

2.1 基础概念

术语 说明
Query 只读操作,类似 REST 的 GET
Mutation 读写操作,类似 REST 的 POST/PUT/DELETE
Resolver 解析器,连接操作与后端处理逻辑(类比 REST 的 MVC 控制器)
Type 定义响应数据结构,可包含关联其他 Type 的字段
Input 定义输入数据结构,类似 Type 但用于请求
Scalar 基础类型(String/Int/Boolean/Float 等)
Interface 接口,定义字段规范供其他对象继承
Schema 模式,管理所有可执行的查询和变更

2.2 模式加载方式

GraphQL 支持两种模式定义方式:

  1. 接口定义语言(IDL)
    简洁直观,官方推荐语法:

    type User {
        firstName: String
    }
    
  2. 编程语言定义
    以 Java 为例:

    GraphQLObjectType userType = newObject()
      .name("User")  
      .field(newFieldDefinition()
        .name("firstName")
        .type(GraphQLString))
      .build();
    

3. 接口定义语言详解

IDL(也称 SDL)是定义 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")
}

⚠️ 注意:! 表示非空字段,@relation 定义关联关系

4. GraphQL-java 实践

4.1 核心特性

4.2 依赖配置

Maven 依赖:

<!-- GraphQL 注解支持 -->
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-annotations</artifactId>
    <version>3.0.3</version>
</dependency>

<!-- HTTP 服务框架(示例用 Ratpack) -->
<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.4.6</version>
</dependency>

💡 提示:也可替换为 Vert.x/Spark/Spring Boot 等框架

4.3 完整实现案例

4.3.1 定义数据模型

@GraphQLName("user")
public class User {

    @GraphQLField
    private Long id;
 
    @GraphQLField
    private String name;
 
    @GraphQLField
    private String email;
  
    // 省略 getter/setter/构造方法
}

4.3.2 请求处理器

@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));
      });
}

4.3.3 查询操作

@GraphQLName("query")
public class UserQuery {

    @GraphQLField
    public static User retrieveUser(
     DataFetchingEnvironment env,
      @NotNull @GraphQLName("id") String id) {
        // 返回用户逻辑
    }
    
    @GraphQLField
    public static List<User> listUsers(DataFetchingEnvironment env) {
        // 返回用户列表逻辑
    }
}

4.3.4 变更操作

@GraphQLName("mutation")
public class UserMutation {
 
    @GraphQLField
    public static User createUser(
      DataFetchingEnvironment env,
      @NotNull @GraphQLName("name") String name,
      @NotNull @GraphQLName("email") String email) {
      // 创建用户逻辑
    }
}

4.4 请求与响应示例

创建用户

请求:

{
    "query": "mutation($name: String! $email: String!){
       createUser (name: $name email: $email) { id name email age } }",
    "parameters": {
        "name": "John",
        "email": "john.doe@example.com"
     }
}

响应:

{
    "data": {
        "createUser": {
            "id": 1,
            "name": "John",
            "email": "john.doe@example.com"
        }
    } 
}

查询用户(定制字段)

请求:

{
    "query": "query($id: String!){ retrieveUser (id: $id) {email} }",
    "parameters": {
        "id": 1
    }
}

响应:

{
    "data": {
        "retrieveUser": {
            "email": "john.doe@example.com"
        }
    }
}

✅ 关键优势:客户端可精确控制返回字段,避免冗余数据传输

5. 总结

GraphQL 作为 REST 的替代方案,显著简化了客户端/服务端交互复杂度:

  • 单一接口解决数据获取问题
  • 客户端按需获取数据
  • 强类型模式保障数据一致性

完整示例代码见 GitHub 仓库


原始标题:Introduction to GraphQL