query bookDetails {
bookById(id: "book-1") {
id
name
pageCount
author {
firstName
lastName
}
}
}
構建 GraphQL 服務
Spring for GraphQL 為基於 GraphQL Java 構建的 Spring 應用提供支援。
本指南將引導您瞭解如何使用 Spring for GraphQL 在 Java 中建立 GraphQL 服務。
您將構建什麼
您將構建一個服務,該服務將在 https://:8080/graphql
接收 GraphQL 請求。
您需要什麼
-
大約 15 分鐘
-
一個喜歡的文字編輯器或 IDE
-
Java 17 或更高版本
-
您也可以將程式碼直接匯入到您的 IDE 中
如何完成本指南
與大多數 Spring 入門指南一樣,您可以從頭開始完成每個步驟,也可以跳過您已熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。
要從頭開始,請轉到從 Spring Initializr 開始。
要跳過基礎部分,請執行以下操作
-
下載並解壓本指南的原始碼庫,或使用 Git 克隆:
git clone https://github.com/spring-guides/gs-graphql-server.git
-
進入目錄
gs-graphql-server/initial
-
跳到GraphQL 極簡介紹。
完成後,您可以對照 gs-graphql-server/complete
中的程式碼檢查您的結果。
從 Spring Initializr 開始
如果您願意,可以使用此預設 Spring Initializr 連結來載入正確的設定。否則,請繼續手動設定 Initializr。
手動初始化專案
-
導航到 https://start.spring.io。此服務會自動引入應用程式所需的所有依賴項,併為您完成大部分設定。
-
選擇 Gradle 或 Maven,以及您想要使用的語言。本指南假設您選擇 Java。
-
點選 Dependencies(依賴),然後選擇 Spring for GraphQL 和 Spring Web。
-
點選 Generate(生成)。
-
下載生成的 ZIP 檔案,這是一個根據您的選擇配置好的 GraphQL 應用程式歸檔。
如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成此過程。 |
您也可以從 GitHub Fork 專案,並在您的 IDE 或其他編輯器中開啟它。 |
GraphQL 極簡介紹
GraphQL 是一種從伺服器檢索資料的查詢語言。它是 REST、SOAP 或 gRPC 的替代方案。在本教程中,我們將從線上商店後端查詢特定圖書的詳細資訊。
這是您可以傳送到 GraphQL 伺服器以檢索圖書詳細資訊的請求示例
此 GraphQL 請求表示
-
查詢 ID 為 "book-1" 的圖書
-
對於該圖書,返回 ID、名稱、頁數和作者
-
對於作者,返回 firstName(名)和 lastName(姓)
響應為 JSON 格式。例如
{
"bookById": {
"id":"book-1",
"name":"Effective Java",
"pageCount":416,
"author": {
"firstName":"Joshua",
"lastName":"Bloch"
}
}
}
GraphQL 的一個重要特性是它定義了一種模式語言,並且是靜態型別的。伺服器清楚地知道請求可以查詢哪些型別的物件以及這些物件包含哪些欄位。此外,客戶端可以自省(introspect)伺服器以獲取模式詳情。
本教程中的“模式”(schema)一詞指的是“GraphQL 模式”,它與“JSON 模式”或“資料庫模式”等其他模式無關。 |
上述查詢的模式為
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 中使用此模式實現 GraphQL 伺服器。
我們只是觸及了 GraphQL 可能性的皮毛。更多資訊可以在GraphQL 官方頁面找到。
我們的示例 API:獲取圖書詳細資訊
使用 Spring for GraphQL 建立伺服器的主要步驟如下
-
定義 GraphQL 模式
-
實現獲取查詢實際資料的邏輯
我們的示例應用程式將是一個簡單的 API,用於獲取特定圖書的詳細資訊。它並非旨在成為一個全面的 API。
模式
在您之前準備好的 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 模式都有一個頂級的 Query
型別,其下的欄位是應用程式公開的查詢操作。這裡模式定義了一個名為 bookById
的查詢,用於返回特定圖書的詳細資訊。
它還定義了包含 id
、name
、pageCount
和 author
欄位的 Book
型別,以及包含 firstName
和 lastName
欄位的 Author
型別。
上述用於描述模式的領域特定語言稱為 Schema Definition Language(模式定義語言)或 SDL。更多詳細資訊,請參閱GraphQL 文件。 |
資料來源
GraphQL 的一個關鍵優勢在於資料可以來自任何地方。資料可以來自資料庫、外部服務,或者靜態的記憶體列表。
為了簡化教程,圖書和作者資料將來自於它們各自類中的靜態列表。
建立圖書和作者資料來源
現在我們在主應用程式包中建立 Book
和 Author
類,緊挨著 GraphQlServerApplication
。使用以下內容作為它們的內容
package com.example.graphqlserver;
import java.util.Arrays;
import java.util.List;
public record Book (String id, String name, int pageCount, String authorId) {
private static List<Book> books = Arrays.asList(
new Book("book-1", "Effective Java", 416, "author-1"),
new Book("book-2", "Hitchhiker's Guide to the Galaxy", 208, "author-2"),
new Book("book-3", "Down Under", 436, "author-3")
);
public static Book getById(String id) {
return books.stream()
.filter(book -> book.id().equals(id))
.findFirst()
.orElse(null);
}
}
package com.example.graphqlserver;
import java.util.Arrays;
import java.util.List;
public record Author (String id, String firstName, String lastName) {
private static List<Author> authors = Arrays.asList(
new Author("author-1", "Joshua", "Bloch"),
new Author("author-2", "Douglas", "Adams"),
new Author("author-3", "Bill", "Bryson")
);
public static Author getById(String id) {
return authors.stream()
.filter(author -> author.id().equals(id))
.findFirst()
.orElse(null);
}
}
新增程式碼以獲取資料
Spring for GraphQL 提供了一種基於註解的程式設計模型。透過使用控制器註解的方法,我們可以宣告如何獲取特定 GraphQL 欄位的資料。
在主應用程式包中,緊鄰 Book
和 Author
,向 BookController.java
新增以下內容
package com.example.graphqlserver;
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;
@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
註解的名為 bookById
的方法,此控制器聲明瞭如何獲取 Query 型別下定義的 Book
。查詢欄位由方法名確定,但也可以在註解本身上宣告。
Spring for GraphQL 使用 RuntimeWiring.Builder 將每個這樣的控制器方法註冊為 GraphQL Java 的 graphql.schema.DataFetcher 。DataFetcher 提供了獲取查詢或任何模式欄位資料的邏輯。GraphQL 的 Spring Boot starter 具有自動配置,可以自動完成此註冊。 |
在 GraphQL Java 引擎中,DataFetchingEnvironment
提供對欄位特定引數值對映的訪問。使用 @Argument
註解可以將引數繫結到目標物件並注入到控制器方法中。預設情況下,方法引數名用於查詢引數,但也可以在註解本身上指定。
這個 bookById
方法定義瞭如何獲取特定的 Book
,但它不負責獲取相關的 Author
。如果請求需要作者資訊,GraphQL Java 將需要獲取此欄位。
@SchemaMapping
註解將處理方法對映到 GraphQL 模式中的一個欄位,並宣告它為該欄位的 DataFetcher
。欄位名預設為方法名,型別名預設為注入到方法中的源/父物件的簡單類名。在此示例中,欄位預設為 author
,型別預設為 Book
。
更多資訊,請參閱Spring for GraphQL 註解控制器功能的文件。
這就是我們所需的全部程式碼!
讓我們執行我們的第一個查詢。
執行我們的第一個查詢
啟用 GraphiQL Playground
GraphiQL 是一個用於編寫和執行查詢等的有用視覺化介面。透過將此配置新增到 application.properties
檔案來啟用 GraphiQL。
spring.graphql.graphiql.enabled=true
啟動應用程式
啟動您的 Spring 應用程式。導航到 https://:8080/graphiql。
執行查詢
輸入查詢,然後點選視窗頂部的執行按鈕。
query bookDetails {
bookById(id: "book-1") {
id
name
pageCount
author {
id
firstName
lastName
}
}
}
您應該會看到如下響應。

恭喜您,您已經構建了一個 GraphQL 服務並執行了您的第一個查詢!藉助 Spring for GraphQL,您僅用少量程式碼就完成了這項工作。
測試
Spring for GraphQL 在 spring-graphql-test
artifact 中提供了 GraphQL 測試輔助工具。我們已將此 artifact 作為 Spring Initializr 生成專案的一部分包含在內。
全面測試 GraphQL 服務需要具有不同範圍的測試。在本教程中,我們將編寫一個 @GraphQlTest
切片測試,它專注於單個控制器。還有其他輔助工具可以幫助進行完整的端到端整合測試和專注於伺服器端的測試。有關完整詳情,請參閱Spring for GraphQL 測試文件以及 Spring Boot 文件中的自動配置的 Spring for GraphQL 測試。
讓我們編寫一個控制器切片測試,該測試驗證剛才在 GraphiQL playground 中請求的相同 bookDetails
查詢。
將以下內容新增到測試檔案 BookControllerTests.java
中。將此檔案儲存在 src/test/java/com/example/graphqlserver/
資料夾內的位置。
package com.example.graphqlserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
import org.springframework.graphql.test.tester.GraphQlTester;
@GraphQlTest(BookController.class)
public class BookControllerTests {
@Autowired
private GraphQlTester graphQlTester;
@Test
void shouldGetFirstBook() {
this.graphQlTester
.documentName("bookDetails")
.variable("id", "book-1")
.execute()
.path("bookById")
.matchesJson("""
{
"id": "book-1",
"name": "Effective Java",
"pageCount": 416,
"author": {
"firstName": "Joshua",
"lastName": "Bloch"
}
}
""");
}
}
此測試引用了一個類似於我們在 GraphiQL Playground 中使用的 GraphQL 查詢。它使用 $id
引數化以使其可重用。將此查詢新增到位於 src/test/resources/graphql-test
的 bookDetails.graphql
檔案中。
query bookDetails($id: ID) {
bookById(id: $id) {
id
name
pageCount
author {
id
firstName
lastName
}
}
}
執行測試並驗證結果是否與在 GraphiQL Playground 中手動請求的 GraphQL 查詢結果一致。
@GraphQlTest
註解對於編寫控制器切片測試很有用,它專注於單個控制器。@GraphQlTest
會自動配置 Spring for GraphQL 基礎設施,不涉及任何傳輸或伺服器。自動配置透過跳過樣板程式碼使我們能夠更快地編寫測試。由於這是一個重點切片測試,只會掃描有限數量的 bean,包括 @Controller
和 RuntimeWiringConfigurer
。有關掃描 bean 的列表,請參閱文件。
GraphQlTester
是一個契約,它聲明瞭測試 GraphQL 請求的通用工作流程,與傳輸無關。在我們的測試中,我們提供了一個帶有 documentName
和所需變數的文件,然後 execute
(執行)請求。然後,我們使用 JSON Path 選擇響應的一部分,並斷言此位置的 JSON 與預期結果匹配。
恭喜!在本教程中,您構建了一個 GraphQL 服務,運行了您的第一個查詢,並編寫了您的第一個 GraphQL 測試!
進一步閱讀
示例原始碼
本指南是與 GraphQL Java 團隊合作編寫的。非常感謝 Donna Zhou、Brad Baker 和 Andreas Marek!本教程的原始碼可在 GitHub 上找到。