GraphQL API和Restful API类似,是一种查询数据的语言,相比于Restful API,GraphQL API在查询方面更加灵活,前后端代码编写也更加方便。本文将介绍如何构建GraphQL API应用程序,基于SpringBoot3.0。如何测试GraphQL API接口是否可用,以及前端如何调用GraphQL API接口(以Nuxt项目为例)。
#What is GraphQL
以下内容摘自
。GraphQL是一个开源的,面向API而创造出来的数据查询操作语言以及相应的运行环境。于2012年仍处于Facebook内部开发阶段,直到2015年才公开发布。 2018年11月7日,Facebook将GraphQL项目转移到新成立的GraphQL基金会(隶属于非营利性的Linux基金会)。
GraphQL相较于REST以及其他web service架构提供了一种更加高效、强大和灵活的开发web APIs的方式。它通过由客户端根据所需定义数据结构,同时由服务端负责返回相同数据结构的对应数据的方式避免了服务端大量冗余数据的返回,但与此同时也意味着这种方式不能有效利用起查询结果的web缓存。GraphQL这种查询语言所带来的灵活性和丰富性的同时也增加了复杂性,导致简单的APIs有可能并不适合这种方式。
#Why use GraphQL
- 由前端决定返回哪些数据。这对于后端需要复杂格式的JSON特别有用,前端可以自行决定需要哪些数据,不需要哪些数据,减少冗余数据同时也减少后端压力。
- 不仅可以查询,还可以用于更新。虽然名称为Graph Query Language,但是GraphQL同样可以用于创建、更新、删除操作。
#How to use
通过调用GraphQL接口,向后端发送这样的数据,注意这并不是JSON
这里传入参数为article id,用于查询一篇文章
query Article {
article(id: "2") {
id
title
content
author {
id
firstName
lastName
}
tags {
id
name
description
}
}
}
后端将根据请求中的数据结构返回对于应数据
{
"data": {
"article": {
"id": "2",
"title": "title2",
"content": "content2",
"author": {
"id": "author-2",
"firstName": "Douglas",
"lastName": "Adams"
},
"tags": [
{
"id": "2",
"name": "Python",
"description": "Python related articles"
},
{
"id": "3",
"name": "JavaScript",
"description": "JavaScript related articles"
}
]
}
}
}
也可以一次查询多条数据
query Article {
articles {
id
title
content
author {
id
firstName
lastName
}
tags {
id
name
description
}
}
}
Reponse
{
"data": {
"articles": [
{
"id": "1",
"title": "title1",
"content": "content1",
"author": {
"id": "author-1",
"firstName": "Joshua",
"lastName": "Bloch"
},
"tags": [
{
"id": "1",
"name": "Java",
"description": "Java related articles"
},
{
"id": "2",
"name": "Python",
"description": "Python related articles"
},
{
"id": "1",
"name": "Java",
"description": "Java related articles"
}
]
},
{
"id": "2",
"title": "title2",
"content": "content2",
"author": {
"id": "author-2",
"firstName": "Douglas",
"lastName": "Adams"
},
"tags": [
{
"id": "2",
"name": "Python",
"description": "Python related articles"
},
{
"id": "3",
"name": "JavaScript",
"description": "JavaScript related articles"
}
]
}
]
}
}
#Build SpringBoot Application
推荐IDEA先安装GraphQL的相关插件,
,该插件提供了对schema.graphqls
文件的语法高亮支持。##创建项目
pom.xml中需要加入以下依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
##定义实体类
实体类既可以和数据库字段对应,可以是自定义的VO。这里使用了record,也可以选择使用传统class
public record Article(String id, String title, String content, String authorId, String tagId) {}
public record Author (String id, String firstName, String lastName) {}
public record Tag(String id, String name, String description) {}
##查询数据
##定义schema.graphqls
schema.graphqls
文件将用于定义GraphQL模式、描述数据结构和数据之间的关系、以及客户端可以进行的操作。
文件将定义以下内容
- 类型
- 查询
- 关系
以下是一个具体的schema.graphqls
文件
# 查询的接口,客户端可以访问的内容
type Query {
# id表示查询的参数,Article为下面type中定义的返回的数据结构
article(id: String): Article
# 使用[Article]表示返回的是多条数据,以数组形式返回
articles: [Article]
tag(id: String): Tag
tags: [Tag]
author(id: String): Author
}
# 定义数据结构
type Author {
id: String
firstName: String
lastName: String
}
# tags是多条,用[Tag]表示
type Article {
id: String
title: String
content: String
author: Author
tags: [Tag]
}
type Tag {
id: String
name: String
description: String
}
##编写Controller
GraphQL同样需要通过Controller来访问。相比与MVC中使用@RequsetMapping
,在GraphQL中应该使用@QueryMapping
。并且该@QueryMapping
不需要定义请求路径,以下是一个具体示例
@QueryMapping
@QueryMapping
public Collection<Article> articles()
{
return queryService.listArticles();
}
此处方法名为articles()
,需要和schema.graphqls
文件中type Query
中定义的名称相同。
这样当前端发送
query Article { articles { id } }
时,会匹配到这一个接口。该方法返回type Article
中所包含的所有属性,之后GraphQL
会更具前端传入的参数,自动保留需要的数据。
例如在这个示例中,前端只请求了id和title,所以只有id和title会被返回,其他的数据会被隐藏/删除。
query Article {
articles {
id
title
}
}
查询的参数需要添加@Argument
注解,否则会报错
@QueryMapping
public Article article(@Argument String id) {
return queryService.getArticleById(id);
}
@SchemaMapping
@QueryMapping
和@SchemaMapping
是GraphQL最核心的两个注解。@QueryMapping
用于匹配请求地址,而@SchemaMapping
将用于构建数据关系。
以一个具体的例子来说
@QueryMapping
public Article article(@Argument String id) {
return queryService.getArticleById(id);
}
@SchemaMapping
public Collection<Tag> tags(Article article)
{
return queryService.getTagsByArticleId(article.id());
}
@SchemaMapping
public Author author(Article article)
{
return queryService.getAuthorByAuthorId(article.authorId());
}
tags()
方法将用于查询Article关联的Tag,所以该方法的参数为Article article
,返回数据为多条Tag。同理,author用于查询Article关联的Author,所以返回数据为Author类型。
##保存数据
定义schema.graphqls
type Mutation {
# !表示该参数为必须,:Article表示插入之后返回的数据类型
createArticle(article: ArticleInput!): Article
}
input ArticleInput {
title: String
content: String
authorId: String
tagId: String
}
Controller
@MutationMapping
public Article createArticle(@Argument("article") ArticleInput article) {
return createService.createArticle(article);
}
调用该接口
mutation {
createArticle(
article: {title: "Sample Title", content: "Sample Content", authorId: "author-3", tagId: "2"}
) {
id
title
content
author {
id
firstName
lastName
}
tags {
id
name
description
}
}
}
##删除/更新数据
删除数据同样需要在Mutation
中定义操作,:后面为操作完成之后返回的类型,这里不再详细描述
type Mutation {
deleteArticle(id: String!): Boolean
updateArticle(id: String!, article: ArticleInput!): Article
}
后端项目地址