建立 GraphQL 服務

Spring for GraphQL 提供基於 GraphQL Java 建構的 Spring 應用程式的支援。

本指南將引導您使用 Spring for GraphQL 在 Java 中建立 GraphQL 服務的流程。

您將建立的內容

您將建立一個服務,該服務將在 https://127.0.0.1:8080/graphql 接受 GraphQL 請求。

您需要的東西

如何完成本指南

與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,或者您可以跳過您已經熟悉的基礎設定步驟。 無論哪種方式,您最終都會得到可運作的程式碼。

從頭開始,請前往 從 Spring Initializr 開始

跳過基礎,請執行以下操作

完成後,您可以將您的結果與 gs-graphql-server/complete 中的程式碼進行比較。

從 Spring Initializr 開始

如果您願意,可以使用這個預先填寫的 Spring Initializr 連結來載入正確的設定。 否則,請繼續手動設定 Initializr。

手動初始化專案

  1. 導覽至 https://start.spring.io。 此服務會提取應用程式所需的所有依賴項,並為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想使用的語言。 本指南假設您選擇了 Java。

  3. 點擊Dependencies 並選擇 Spring for GraphQLSpring Web

  4. 點擊 Generate

  5. 下載產生的 ZIP 檔案,該檔案是使用您的選擇配置的 GraphQL 應用程式的封存檔。

如果您的 IDE 具有 Spring Initializr 整合,您可以從 IDE 完成此過程。
您也可以從 GitHub fork 專案,並在您的 IDE 或其他編輯器中開啟它。

GraphQL 的簡短介紹

GraphQL 是一種用於從伺服器檢索資料的查詢語言。 它是 REST、SOAP 或 gRPC 的替代方案。 在本教程中,我們將從線上商店後端查詢特定書籍的詳細資訊。

這是一個您可以發送到 GraphQL 伺服器以檢索書籍詳細資訊的請求範例

query bookDetails {
  bookById(id: "book-1") {
    id
    name
    pageCount
    author {
      firstName
      lastName
    }
  }
}

此 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 的一個重要功能是它定義了一種 schema 語言,並且它是靜態類型的。 伺服器確切地知道請求可以查詢哪些類型的物件以及這些物件包含哪些欄位。 此外,客戶端可以內省伺服器以請求 schema 詳細資訊。

本教程中的 schema 一詞指的是 "GraphQL Schema",它與其他 schema(例如 "JSON Schema" 或 "Database Schema")無關。

上述查詢的 schema 為

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 實作 GraphQL 伺服器。

我們幾乎沒有觸及 GraphQL 可能實現的功能的表面。 更多資訊可以在 官方 GraphQL 頁面上找到。

我們的範例 API:取得書籍詳細資訊

以下是使用 Spring for GraphQL 建立伺服器的主要步驟

  1. 定義 GraphQL schema

  2. 實作邏輯以提取查詢的實際資料

我們的範例應用程式將是一個簡單的 API,用於取得特定書籍的詳細資訊。 它不打算成為一個全面的 API。

Schema

在您先前準備好的 Spring for GraphQL 應用程式中,新增一個名為 schema.graphqls 的新檔案到 src/main/resources/graphql 資料夾,並包含以下內容

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 的查詢,該查詢傳回特定書籍的詳細資訊。

它還定義了具有欄位 idnamepageCountauthor 的類型 Book,以及具有欄位 firstNamelastName 的類型 Author

上面用於描述 schema 的特定領域語言稱為 Schema Definition Language 或 SDL。 有關更多詳細資訊,請參閱 GraphQL 文件

資料來源

GraphQL 的一個關鍵優勢是資料可以來自任何地方。 資料可以來自資料庫、外部服務或靜態記憶體內列表。

為了簡化本教程,書籍和作者資料將來自各自類別中的靜態列表。

建立 Book 和 Author 資料來源

現在讓我們在主應用程式套件中,緊鄰 GraphQlServerApplication 旁邊建立 BookAuthor 類別。 使用以下內容作為其內容

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 欄位的資料。

在主應用程式套件中,緊鄰 BookAuthor 旁邊將以下內容新增到 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.DataFetcherDataFetcher 提供了提取查詢或任何 schema 欄位的資料的邏輯。 GraphQL 的 Spring Boot 啟動器具有自動配置功能,可以自動執行此註冊。

在 GraphQL Java 引擎中,DataFetchingEnvironment 提供了對欄位特定參數值對應的存取權。 使用 @Argument 註解將參數綁定到目標物件並注入到控制器方法中。 預設情況下,方法參數名稱用於查找參數,但也可以在註解本身上指定。

bookById 方法定義瞭如何取得特定的 Book,但不負責提取相關的 Author。 如果請求要求作者資訊,GraphQL Java 將需要提取此欄位。

@SchemaMapping 註解將處理程序方法對應到 GraphQL schema 中的欄位,並宣告它為該欄位的 DataFetcher。 欄位名稱預設為方法名稱,類型名稱預設為注入到方法中的來源/父物件的簡單類別名稱。 在此範例中,欄位預設為 author,類型預設為 Book

有關更多資訊,請參閱 Spring for GraphQL 註解控制器功能的文檔

這就是我們需要的所有程式碼!

讓我們執行我們的第一個查詢。

執行我們的第一個查詢

啟用 GraphiQL Playground

GraphiQL 是一個有用的視覺介面,用於編寫和執行查詢等等。 透過將此配置新增到 application.properties 檔案來啟用 GraphiQL。

spring.graphql.graphiql.enabled=true

啟動應用程式

啟動您的 Spring 應用程式。 導覽至 https://127.0.0.1:8080/graphiql

執行查詢

輸入查詢,然後點擊視窗頂部的播放按鈕。

query bookDetails {
  bookById(id: "book-1") {
    id
    name
    pageCount
    author {
      id
      firstName
      lastName
    }
  }
}

您應該會看到像這樣的回應。

GraphQL response

恭喜,您已經建立了一個 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.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,包括 @ControllerRuntimeWiringConfigurer。 有關掃描的 bean 列表,請參閱文件

GraphQlTester 是一個合約,它宣告了測試 GraphQL 請求的通用工作流程,與傳輸方式無關。 在我們的測試中,我們提供一個具有 documentName 的文件,以及所需的變數,然後 execute 執行請求。 然後,我們使用其 JSON 路徑選擇回應的一部分,並斷言此位置的 JSON 與預期結果相符。

恭喜! 在本教學中,您建立了一個 GraphQL 服務、執行了您的第一個查詢,並編寫了您的第一個 GraphQL 測試!

延伸閱讀

範例原始碼

本指南與 GraphQL Java 團隊合作編寫。 非常感謝 Donna ZhouBrad BakerAndreas Marek! 本教學的原始碼可以在 GitHub 上找到。

文件

GraphQL Java 是支援 Spring for GraphQL 的 GraphQL 引擎。 閱讀 GraphQL Java 文件

更多 Spring for GraphQL 範例

請參閱 1.0.x 分支中的更多範例,這些範例很快將 移動到 單獨的儲存庫中。

Stack Overflow 問題

您可以在 Stack Overflow 上使用 spring-graphql 標籤提出問題。

想要編寫新的指南或貢獻現有的指南嗎? 請查看我們的 貢獻指南

所有指南均以 ASLv2 授權發布程式碼,並以 Attribution, NoDerivatives creative commons 授權發布寫作。

取得程式碼