This article was first published on my blog, original address vio.vin/article/use-puppeteer
GraphQL API is similar to Restful API; it is a query language for data. Compared to Restful API, GraphQL API is more flexible in querying and makes it easier to write front-end and back-end code. This article will introduce how to build a GraphQL API application based on SpringBoot 3.0, how to test whether the GraphQL API interface is available, and how the front end can call the GraphQL API interface (using a Nuxt project as an example).
What is GraphQL#
The following content is excerpted from GraphQL Wiki.
GraphQL is an open-source data query language and runtime environment created for APIs. It was still in the internal development stage at Facebook in 2012 and was publicly released in 2015. On November 7, 2018, Facebook transferred the GraphQL project to the newly established GraphQL Foundation (affiliated with the non-profit Linux Foundation).
Compared to REST and other web service architectures, GraphQL provides a more efficient, powerful, and flexible way to develop web APIs. It avoids returning a large amount of redundant data from the server by allowing the client to define the data structure it needs, while the server is responsible for returning the corresponding data in the same structure. However, this also means that this approach cannot effectively utilize web caching of query results. The flexibility and richness brought by GraphQL as a query language also increase complexity, making simple APIs potentially unsuitable for this approach.
Why use GraphQL#
- The front end decides which data to return. This is particularly useful for back ends that require complex JSON formats, as the front end can decide which data is needed and which is not, reducing redundant data and alleviating pressure on the back end.
- It can not only query but also be used for updates. Although it is called Graph Query Language, GraphQL can also be used for create, update, and delete operations.
How to use#
By calling the GraphQL interface, send such data to the back end; note that this is not JSON.
Here, the parameter passed is the article id, used to query an article.
query Article {
article(id: "2") {
id
title
content
author {
id
firstName
lastName
}
tags {
id
name
description
}
}
}
The back end will return the corresponding data based on the data structure in the request.
{
"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"
}
]
}
}
}
You can also query multiple pieces of data at once.
query Article {
articles {
id
title
content
author {
id
firstName
lastName
}
tags {
id
name
description
}
}
}
Response
{
"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#
It is recommended to first install the relevant GraphQL plugins in IDEA, GraphQL, which provides syntax highlighting support for the schema.graphqls
file.
Create Project#
The following dependencies need to be added to the 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>
Define Entity Classes#
Entity classes can correspond to database fields or be custom VOs. Here, records are used, but traditional classes can also be chosen.
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) {}
Query Data#
Define schema.graphqls
#
The schema.graphqls
file will be used to define the GraphQL schema, describe the data structure and relationships between data, and the operations that the client can perform.
The file will define the following content:
- Types
- Queries
- Relationships
Here is a specific schema.graphqls
file
# The interface for queries, content accessible to the client
type Query {
# id represents the query parameter, Article is the data structure defined in the type below that is returned
article(id: String): Article
# Using [Article] indicates that multiple pieces of data are returned as an array
articles: [Article]
tag(id: String): Tag
tags: [Tag]
author(id: String): Author
}
# Define data structures
type Author {
id: String
firstName: String
lastName: String
}
# tags are multiple, represented by [Tag]
type Article {
id: String
title: String
content: String
author: Author
tags: [Tag]
}
type Tag {
id: String
name: String
description: String
}
Write Controller#
GraphQL also needs to be accessed through a Controller. Compared to using @RequestMapping
in MVC, in GraphQL, @QueryMapping
should be used. Moreover, this @QueryMapping
does not require defining a request path. Here is a specific example.
@QueryMapping#
@QueryMapping
public Collection<Article> articles()
{
return queryService.listArticles();
}
The method name here is articles()
, which needs to match the name defined in type Query
in the schema.graphqls
file.
Thus, when the front end sends
query Article { articles { id } }
, it will match this interface. This method returns all properties contained in type Article
, and then GraphQL
will automatically retain the required data based on the parameters passed in by the front end.
For example, in this case, the front end only requested id and title, so only id and title will be returned, while other data will be hidden/deleted.
query Article {
articles {
id
title
}
}
The query parameters need to be annotated with @Argument
, otherwise an error will occur.
@QueryMapping
public Article article(@Argument String id) {
return queryService.getArticleById(id);
}
@SchemaMapping#
@QueryMapping
and @SchemaMapping
are the two core annotations of GraphQL. @QueryMapping
is used to match the request address, while @SchemaMapping
is used to build data relationships.
For a specific example:
@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());
}
The tags()
method will be used to query the Tags associated with the Article, so the parameter for this method is Article article
, and the return data is multiple Tags. Similarly, author
is used to query the Author associated with the Article, so the return data is of Author type.
Save Data#
Define schema.graphqls
#
type Mutation {
# ! indicates that this parameter is required, :Article indicates the data type returned after insertion
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);
}
Call this interface#
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
}
}
}
Delete/Update Data#
Deleting data also requires defining operations in Mutation
, with the type returned after the operation specified after the colon; this will not be described in detail here.
type Mutation {
deleteArticle(id: String!): Boolean
updateArticle(id: String!, article: ArticleInput!): Article
}
The back-end project address https://github.com/lnbiuc/graphql-start