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 -
cd 進入
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。
-
單擊生成。
-
下載生成的 ZIP 檔案,它是根據您的選擇配置的 GraphQL 應用程式的歸檔檔案。
| 如果您的 IDE 集成了 Spring Initializr,您可以從 IDE 中完成此過程。 |
| 您還可以從 GitHub 分叉該專案,並在您的 IDE 或其他編輯器中開啟它。 |
GraphQL 簡短介紹
GraphQL 是一種用於從伺服器檢索資料的查詢語言。它是 REST、SOAP 或 gRPC 的替代方案。在本教程中,我們將查詢線上商店後端中特定書籍的詳細資訊。
這是一個您可以傳送到 GraphQL 伺服器以檢索書籍詳細資訊的請求示例
此 GraphQL 請求表示
-
執行查詢以查詢 ID 為“book-1”的書籍
-
對於該書籍,返回 id、name、pageCount 和 author
-
對於作者,返回 firstName 和 lastName
響應是 JSON 格式。例如
{
"bookById": {
"id":"book-1",
"name":"Effective Java",
"pageCount":416,
"author": {
"firstName":"Joshua",
"lastName":"Bloch"
}
}
}
GraphQL 的一個重要特性是它定義了一種模式語言,並且它是靜態型別的。伺服器確切地知道請求可以查詢哪些型別的物件以及這些物件包含哪些欄位。此外,客戶端可以自省伺服器以獲取模式詳細資訊。
| 本教程中的“模式”一詞指的是“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 型別。
| 上面用於描述模式的領域特定語言稱為模式定義語言或 SDL。有關更多詳細資訊,請參閱 GraphQL 文件。 |
資料來源
GraphQL 的一個關鍵優勢是資料可以來自任何地方。資料可以來自資料庫、外部服務或靜態記憶體列表。
為了簡化教程,書籍和作者資料將來自其各自類中的靜態列表。
建立 Book 和 Author 資料來源
現在讓我們在主應用程式包中,緊鄰 GraphQlServerApplication 建立 Book 和 Author 類。使用以下內容作為它們的內容
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());
}
}
透過定義一個名為 bookById 並用 @QueryMapping 註解的方法,此控制器聲明瞭如何獲取 Query 型別下定義的 Book。查詢欄位由方法名稱確定,但也可以在註解本身上宣告。
Spring for GraphQL 使用 RuntimeWiring.Builder 將每個此類控制器方法註冊為 GraphQL Java graphql.schema.DataFetcher。DataFetcher 提供獲取查詢或任何模式欄位資料的邏輯。GraphQL 的 Spring Boot 啟動器具有自動配置,可自動執行此註冊。 |
在 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 工件中提供了 GraphQL 測試的輔助工具。我們已經將此工件作為 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.graphql.test.autoconfigure.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 路徑選擇響應的一部分,並斷言此位置的 JSON 與預期結果匹配。
恭喜!在本教程中,您構建了一個 GraphQL 服務,運行了您的第一個查詢,並編寫了您的第一個 GraphQL 測試!
延伸閱讀
示例原始碼
本指南與 GraphQL Java 團隊合作編寫。非常感謝 Donna Zhou、Brad Baker 和 Andreas Marek!本教程的原始碼可以在 GitHub 上找到。
文件
GraphQL Java 是為 Spring for GraphQL 提供支援的 GraphQL 引擎。閱讀 GraphQL Java 文件。
更多 Spring for GraphQL 示例
請參閱專用倉庫中的更多示例。
Stack Overflow 問題
您可以在 Stack Overflow 上使用 spring-graphql 標籤提出問題。
想寫新指南或為現有指南做貢獻嗎?請檢視我們的貢獻指南。
| 所有指南的程式碼均採用 ASLv2 許可,文字內容採用署名-禁止演繹知識共享許可。 |