GraphQL+SpringBoot入门指南

本文介绍了GraphQL的基本原理,展示了如何在SpringBoot中配置和使用GraphQL,包括核心依赖、Schema定义、接口编写以及如何访问Playground。在生产环境中,文章讨论了如何定义父Schema、全局错误控制器以及GraphQL在微服务架构中的定位。最后,总结了GraphQL的优点如性能提升、灵活性高,同时也指出其学习曲线陡峭、服务器端负载增加等缺点,以及作者认为的优缺点。

一、什么是GraphQL

是一种用于查询数据的查询语言,其特点是支持在一次查询中获取多项数据,并允许多种数据组合。GraphQL 的基本原理是使用类型系统来定义数据的结构,并允许客户端使用查询语言来指定想要获取的数据。GraphQL 服务器根据查询语言来解析查询,并返回一个与查询语言结构相同的结果。

只通过一个暴露的端口即可访问后台所有的gql接口,一般都是:http://ip:port/graphql

说人话:可以通过自定义的查询语句筛选出想要的目标数据

举例:使用gql查询一个接口 (前提是已经定义好接口和graphql schema)

query test01 {
	getData{
		name
		age
		salary
		birthday
	}
}

响应结果:

{
  "data": {
    "getData": {
      "name": "张三",
      "age": 18,
      "salary": 3000,
      "birthday": "2023-12-21T09:27:16.153"
    }
  }
}

问题来了:假如我只想要name就行了,其他属性我暂不需要,这时候gql的特性就需要展示一下了!只需要把你想要的name属性写出来即可,左边为gql, 右边为执行结果

看到这里后,恭喜你已经掌握graphql的精髓了!

二、在SpringBoot中使用GraphQL

1. 核心依赖准备

 <!--Spring Boot 2.7+的版本支持graphql-->
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--graphql-->
<dependency>
      <groupId>org.springframework.graphql</groupId>
      <artifactId>spring-graphql</artifactId>
</dependency>

<!--这是一个可视化的graphql接口查询面板,很方便-->
<dependency>
      <groupId>com.graphql-java-kickstart</groupId>
      <artifactId>playground-spring-boot-starter</artifactId>
      <version>${gql.playground.version}</version>
</dependency>

2. 定义graphql Schema

# 定义接口返回值类型,一般命名风格:入参为input结尾,返回值payload结尾
type UserPayload {
    name: String
    age: Int,
    salary: Int,
    birthday: String
}
# 定义查询接口Schema
extend type Query {
    getData(ageInput: Int): UserPayload
}

3. 编写接口

官方的gql接口都是xxxResolver, 看着很不顺眼,这里就不展示官网写法了。Spring把它封装成注解的形式,大家可以把它看做Spring中的Controller即可

  @Controller
  public class TestController {
    @QueryMapping
    public Object getData(@Argument("ageInput") Integer age) {
        return User.builder().name("张三").age(age).salary(3000d).birthday(LocalDateTime.now(ZoneId.systemDefault())).build();
    }
}

解释说明:

<1> @QueryMapping

表示这是一个query类型的接口,只适用于查询操作,对应的有一个@MutationMapping, 适用于增删改操作。

<2> getData

需要和schema中定义的接口名称保持一致

<3> @Argument("ageInput")

表示这是一个gql参数,参数名称为ageInput,需要和schema中定义的参数保持一致

4. 访问playground

http://localhost:8888/playground

这样就咱们使用 graphql 完成了一个查询操作啦!

三、生产环境中使用GraphQL

1. 定义父schema

这样在其他文件就可以使用extend关键字来继续使用Query和Mutation了,否则schema是不允许定义多个Query和Mutation的,解耦的效果就达不到了

# 查询接口
type Query {
    # eg:
    # methodName(param: dataType): ResponseType
}

# 增删改接口
type Mutation {
    # eg:
    # methodName(param: dataType): ResponseType
}


# 在另外一个文件就可以这样写:查询接口
extend type Query {
    getData(ageInput: Int): UserPayload
}

2. 定义全局graphql错误控制器

@Slf4j
@Component
public class GraphqlExceptionHandler extends DataFetcherExceptionResolverAdapter {
    @Override
    protected GraphQLError resolveToSingleError(@NonNull Throwable throwable, @NonNull DataFetchingEnvironment env) {
        if (throwable instanceof FeignException) {
            return this.processFeignException(throwable, env);
        } else if (throwable instanceof Exception) {
            return this.processOtherException(throwable, env);
        } else {
            return this.processOtherException(throwable, env);
        }
    }

    private GraphQLError processOtherException(Throwable throwable, DataFetchingEnvironment env) {
        log.error("Service Exception, caused by: {}", throwable.getMessage());
        return GraphqlErrorBuilder.newError()
                .errorType(ErrorType.INTERNAL_ERROR)
                .message(throwable.getMessage())
                .path(env.getExecutionStepInfo().getPath())
                .location(env.getField().getSourceLocation())
                .build();
    }

}

这样发生graphql错误时它就会生效, 接口返回一般是这样的:

{
  "errors": [
    {
      "message": "User name has repeated,please change name!",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createUser"
      ],
      "extensions": {
        "classification": "BAD_REQUEST"
      }
    }
  ],
  "data": {
    "createUser": null
  }
}

3. 定义schema时的核心思想

以前:之前一个前端渲染一个页面需要请求多个REST接口获取数据,

现在:有了graphql后根据业务定义合适的Schema,就可以一个接口一次性返回所有数据,而且还可以根据前端的需要自定义想要的数据集,减少了后端服务请求的次数

4. gql在微服务架构定位

四、总结

1. 优点

1) **提高性能**

GraphQL 允许客户端一次查询获取多项数据,这可以减少与服务器的通信次数,从而提高性能。 *

2) **更灵活的数据查询**

GraphQL 查询语言允许客户端指定想要获取的数据的具体字段,这使数据查询更加灵活。

3) **简化前端开发**

GraphQL 使前端开发人员能够更轻松地构建数据驱动的应用程序,因为他们只需要关注如何查询数据,而无需关心数据的底层结构。

4) **减少数据传输量**

GraphQL 仅返回客户端请求的数据,这可以减少数据传输量,从而节省带宽和提高性能。

5) **增强安全性**

GraphQL 允许服务器端对数据访问进行细粒度的控制,这可以增强应用程序的安全性。

6) **更好的开发人员体验**

GraphQL 提供了统一的数据查询接口,这可以简化开发人员的工作,并提高开发效率。

2. 缺点

1) **学习曲线较陡**

GraphQL 的查询语言和类型系统都需要一定的学习时间,这可能会增加开发人员的入门难度。

2) **需要服务器端支持**

GraphQL 需要前后端同时支持,这可能会增加开发工作量。

3) **可能增加服务器端的负载**

如果 GraphQL 查询过于复杂或数据量过大,可能会增加服务器端的负载。

4) **工具支持有限**

与其他流行的数据查询语言相比,GraphQL 的工具支持还相对有限。 此外,GraphQL 并不是适用于所有场景。如果应用程序的数据模型简单,或者数据查询需求相对固定,那么使用 GraphQL 可能并不是最好的选择。

3. 个人认为优秀的地方

1)支持接口定义

和Java一样支持Interface的定义,但是注意接口入参是复杂对象类型时,必须使用input关键字定义

# 查询接口
extend type Query {
    getData(ageInput: AgeInput): UserPayload
}
input AgeInput {
    age: Int
}

type UserPayload  implements IBaseEntity{
    name: String
    age: Int,
    salary: Int,
    birthday: String

    creatorId: ID
    createTime: String
    modifyTime: String
}

2)Spring的支持, 而且支持多种语法糖

可以像定义REST Controller一样定义接口

@Controller
public class TestController {
    @QueryMapping
    public Object getData(@Argument("ageInput") Integer age) {
        return User.builder().name("张三").age(age).salary(3000d).birthday(LocalDateTime.now(ZoneId.systemDefault())).build();
    }
}

详见:使用 Spring Boot 创建 GraphQL API - spring 中文网

官网: GraphQL | A query language for your API

3)知名大平台都在使用

Facebook

Netflix

GitHub

Shopify

PayPal

Pinterest

4. 个人认为坑爹的地方

1)无法实现递归查询

当查询的结果是一个嵌套的结果时,比如树结构,gql查询无法使用递归,只能通过fragment关键字简化查询语句。还有一种解决办法就是后端先返回树结构的嵌套深度,前台再构造gql语句来进行查询,这样会额外增加一次请求而且有一定的开发工作量

2)增删改操作无法聚合

查询操作可以通过gql进行聚合,但是增删改只能一次请求一个操作和传统REST没啥区别

3)数据类型不完整

官方仅有Int, String, ID等属性类型,没有Long, Date等类型,有一些三方库提供额外的其他类型

4)文件操作复杂

使用gql进行文件上传下载很麻烦,而且官网没有详细说明

5) 需要重复定义实体

比如复杂对象类型作为入参时,需要定义Java对象, 同时也需要定义一个graphql schema obj

author: 一个有温度的Coder

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值