在本教程中,你将使用Spring for GraphQL在Java中创建一个GraphQL服务器。学习本教程之前,需要你具备一定的Spring和Java功底。虽然教程中简要的介绍了GraphQL,但本教程的重点是用Java开发GraphQL服务器。
内容来源:
https://spring.io/guides/gs/graphql-server/
https://www.graphql-java.com/tutorials/getting-started-with-spring-boot
GraphQL简介
GraphQL是一个从服务端获取数据的查询语言。它是REST、SOAP或gRPC的替代方案。假设我们想从在线商店后端查询某本书的详细信息。
使用GraphQL,你可以向服务器发送以下查询,以获取id为“book-1”的图书的详细信息:
query bookDetails {
bookById(id: "book-1") {
id
name
pageCount
author {
firstName
lastName
}
}
}
注意这不是JSON(尽管它与JSON特别相似),这个请求的意思是:
- 对id为“book-1”的图书执行查询;
- 检索结果需要返回:id、name、pageCount和author;
- auth返回firstName和lastName。
响应结果是标准的JSON:
{
"bookById": {
"id":"book-1",
"name":"Effective Java",
"pageCount":416,
"author": {
"firstName":"Joshua",
"lastName":"Bloch"
}
}
}
GraphQL的一个非常重要的属性是它是静态类型的:服务器清楚地知道你可以查询到的每一个对象的样子并且任何一个客户端都能"introspect"服务器并请求”schema“。schema 描述了可能的查询以及可以返回的字段。
schame 在这篇教程中指的是“GraphQL Schema”。
上述描述 schma 如下:
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
本次教程只关注如何通过在Java代码中通过以上schema实现 GrapQL 服务。
这里仅仅触及了GraphQL的表面。更多信息可以在GraphQL官方页面上找到。
GraphQL Java 概览
GraphQL Java 是GraphQL的Java实现版本。GraphQL Java中最重要的就是 GraphQL Java Engine ,它是其他一切的基础。
GraphQL Java Engine 仅仅是执行查询。它不处理任何HTTP或JSON相关的主题。对于这些方面,我们将使用Spring For GraphQL,它负责通过Spring Boot在HTTP上公开我们的API。
创建GraphQL Java服务的主要步骤如下:
- 定义GraphQL Schema.
- 决定如何获取查询的实际数据。
案例:查询图书详情
使用Spring for GraphQL 创建服务的核心步骤:
- 定义一个GraphQL schema;
- 实现一个查询数据逻辑。
我们的样例程序只是一个简单的查询指定数据详情的API,他不是一个全面的API。
创建一个Spring项目
使用 spring initializr 初始化项目,选择依赖见下图
Spring for GraphQL 添加了许多有用的特性,包括加载schema文件、初始化GraphQL Java以及使用控制器注释简化数据获取。
Schema
在Spring for GraphQL项目中 src/main/resources/graphql 目录下新建一个 schema.graphqls 文件,文件内容如下:
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
每个 GraphQL schema 都有一个顶级 Query 类型,字段是程序暴露出的查询操作,这里schema定义了一个 名叫bookById,返回指定图书详情的方法。
这个schema 同时定义了一个具有id,name,pageCount和author字段的Book类型和有firstName和lastName字段的Author类型。
这个schema 同时定义了一个Book类型和Author类型,Book类型有id,name,pageCount和author字段,Author类型有firstName和lastName字段。
上面用于描述模式的领域特定语言称为模式定义语言或SDL。有关更多详细信息,请参阅GraphQL文档。
数据来源
GraphQL的一个关键优势是数据可以从任何地方获取。数据可以来自数据库,一个外部服务,或者内存。
为了简化本教程,书籍和作者数据将来自它们各自类中的静态列表。
创建Book和Auth数据源
我们在启动类GraphQlServerApplication统计创建Book and Author 类:
package com.example.graphqlserver;
import java.util.Arrays;
import java.util.List;
public class Book {
private String id;
private String name;
private int pageCount;
private String authorId;
public Book(String id, String name, int pageCount, String authorId) {
this.id = id;
this.name = name;
this.pageCount = pageCount;
this.authorId = authorId;
}
private static List<Book> books = Arrays.asList(
new Book("book-1", "Harry Potter and the Philosopher's Stone", 223, "author-1"),
new Book("book-2", "Moby Dick", 635, "author-2"),
new Book("book-3", "Interview with the vampire", 371, "author-3")
);
public static Book getById(String id) {
return books.stream().filter(book -> book.getId().equals(id)).findFirst().orElse(null);
}
public String getId() {
return id;
}
public String getAuthorId() {
return authorId;
}
}
package com.example.graphqlserver;
import java.util.Arrays;
import java.util.List;
public class Author {
private String id;
private String firstName;
private String lastName;
public Author(String id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
private static List<Author> authors = Arrays.asList(
new Author("author-1", "Joanne", "Rowling"),
new Author("author-2", "Herman", "Melville"),
new Author("author-3", "Anne", "Rice")
);
public static Author getById(String id) {
return authors.stream().filter(author -> author.getId().equals(id)).findFirst().orElse(null);
}
public String getId() {
return id;
}
}
编写获取数据代码
Spring for GraphQL提供了一种基于注解的编程模型,用于声明处理程序方法,以获取特定GraphQL字段的数据。
创建BookController.java,代码如下:
package com.wheelmouse.graphql.controller;
import com.wheelmouse.graphql.entities.Author;
import com.wheelmouse.graphql.entities.Book;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
/**
* @author wheelmouse
* @date 2023-12-14 14:46
*/
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument String id) {
return Book.getById(id);
}
@SchemaMapping
public Author author(Book book) {
return Author.getById(book.authorId());
}
}
@QueryMapping 注解负责将java方法与 schema 中的 type Query 字段绑定,根据方法名寻找Query的字段。同时@QueryMapping 也可以在直接注释上声明时指定对应的Query字段。Spring for GraphQL 使用 RuntimeWiring.Builder 将处理程序方法注册为查询字段bookById的graphql.schema.DataFetcher。
在 GraphQL Java 中,DataFetchingEnvironment 提供了对特定字段参数值映射的访问。使用 @Argument 注解可将参数绑定到目标对象并注入到处理程序方法中。
默认情况下,方法参数名用于查找参数。参数名称可在注解中指定。
@SchemaMapping 注解将处理程序方法映射到 GraphQL schema 中的一个字段,并将其声明为该字段的 DataFetcher。字段名默认为方法名,类型名默认为注入方法的源/父对象的简单类名。在本例中,字段默认为 author,类型默认为 Book。类型和字段可在注解中指定。
有关更多信息,请参阅 Spring for GraphQL 注解控制器功能的文档。
这就是我们需要的所有代码!
让我们运行第一个查询吧。
执行我们的第一个查询
启动GraphiQL Playground
GraphiQL是一个用于编写和执行查询的的可视化界面,在application.properties 增加如下配置开启GraphiQL:
spring.graphql.graphiql.enabled=true
spring.graphql.graphiql.path=/graphiql
启动项目,登录http://localhost:8080/graphiql.
在控制台输入一下内容:
query bookDetails {
bookById(id: "book-1") {
id
name
pageCount
author {
id
firstName
lastName
}
}
}
点击执行,响应结果如下:
祝贺你,你已经构建了GraphQL服务并执行了第一个查询!在Spring for GraphQL的帮助下,您只需要几行代码就可以实现这一点。