GraphQL サービスの構築

Spring for GraphQL は、GraphQL Java (英語) 上に構築された Spring アプリケーションのサポートを提供します。

このガイドでは、Spring for GraphQL を使用して Java で GraphQL サービスを作成するプロセスについて説明します。

構築するもの

http://localhost:8080/graphql で GraphQL リクエストを受け入れるサービスを構築します。

必要なもの

本ガイドの完成までの流れ

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-graphql-server/complete のコードに対して結果を確認できます。

Spring Initializr から開始

必要に応じて、この事前入力済みの Spring Initializr リンクを使用して正しい設定を読み込むことができます。それ以外の場合は、Initializr を手動で設定してください。

プロジェクトを手動で初期化するには:

  1. IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。

  2. Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。

  3. 依存関係をクリックし、Spring for GraphQLSpring Web を選択します。

  4. 生成をクリックします。

  5. 結果の ZIP ファイルをダウンロードします。これは、選択した内容で構成された GraphQL アプリケーションのアーカイブです。

EclipseIntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。
GitHub (英語) からプロジェクトをフォークして、IDE または他のエディターで開くこともできます。

GraphQL のごく簡単な導入

GraphQL は、サーバーからデータを取得するためのクエリ言語です。これは、REST、SOAP、gRPC に代わるものです。このチュートリアルでは、オンラインストアバックエンドから特定の書籍の詳細を照会します。

これは、書籍の詳細を取得するために GraphQL サーバーに送信できるリクエストの例です。

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

この GraphQL リクエストは次のように述べています。

  • ID "book-1" の本のクエリを実行する

  • 本の場合は、ID、名前、pageCount、作成者を返します

  • 作成者の場合は 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 を使用してサーバーを作成する主な手順は次のとおりです。

  1. GraphQL スキーマを定義する

  2. クエリの実際のデータを取得するロジックを実装する

サンプルアプリは、特定の本の詳細を取得するための単純な API です。包括的な API を意図したものではありません。

スキーマ

前に準備した 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 スキーマにはトップレベルの Query 型があり、そのフィールドはアプリケーションによって公開されるクエリ操作です。ここで、スキーマは、特定の本の詳細を返す bookById と呼ばれる 1 つのクエリを定義します。

また、フィールド idnamepageCountauthor を持つ型 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());
    }
}

@QueryMapping でアノテーションが付けられた bookById という名前のメソッドを定義することにより、このコントローラーは Query 型で定義されているように Book を取得する方法を宣言します。クエリフィールドはメソッド名から決定されますが、アノテーション自体で宣言することもできます。

Spring for GraphQL は、そのような各コントローラーメソッドを GraphQL Java graphql.schema.DataFetcher として登録する RuntimeWiring.Builder を使用します。DataFetcher は、クエリまたは任意のスキーマフィールドのデータをフェッチするロジックを提供します。GraphQL の Spring Boot スターターには、この登録を自動化する自動構成があります。

GraphQL Java エンジンでは、DataFetchingEnvironment はフィールド固有の引数値のマップへのアクセスを提供します。@Argument アノテーションを使用して、引数をターゲットオブジェクトにバインドし、コントローラーメソッドに挿入します。デフォルトでは、メソッドパラメーター名は引数の検索に使用されますが、アノテーション自体で指定することもできます。

この bookById メソッドは、特定の Book を取得する方法を定義しますが、関連する Author の取得については考慮しません。リクエストが作成者情報をリクエストする場合、GraphQL Java はこのフィールドを取得する必要があります。

@SchemaMapping アノテーションは、ハンドラーメソッドを GraphQL スキーマのフィールドにマップし、それがそのフィールドの DataFetcher であることを宣言します。フィールド名はデフォルトでメソッド名になり、型名はデフォルトでメソッドに注入されたソース / 親オブジェクトの単純なクラス名になります。この例では、フィールドはデフォルトで author に設定され、型はデフォルトで Book に設定されています。

必要なコードはこれだけです。

最初のクエリを実行してみましょう。

最初のクエリを実行する

GraphiQL プレイグラウンドを有効にする

GraphiQL は、クエリの作成と実行などに役立つビジュアルインターフェースです。この構成を application.properties ファイルに追加して、GraphiQL を有効にします。

spring.graphql.graphiql.enabled=true

アプリケーションを起動する

Spring アプリケーションを開始します。http://localhost: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 Boot ドキュメントの Spring for GraphQL テストドキュメントおよび自動構成された Spring for GraphQL テストを参照してください。

数分前に GraphiQL プレイグラウンドでリクエストされたものと同じ 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 アノテーションは、1 つのコントローラーに焦点を当てたコントローラースライステストを作成する場合に役立ちます。@GraphQlTest は、トランスポートやサーバーが関与することなく、Spring for GraphQL インフラストラクチャを自動構成します。自動構成により、定型コードをスキップすることでテストをより迅速に作成できます。これは焦点を絞ったスライステストであるため、@Controller および RuntimeWiringConfigurer を含む限られた数の Bean のみがスキャンされます。スキャンされた Bean のリストについては、ドキュメントを参照してください。

GraphQlTester は、トランスポートに関係なく、GraphQL リクエストをテストするための共通のワークフローを宣言する契約です。私たちのテストでは、必要な変数を含む documentName でドキュメントを提供し、次にリクエストを execute で提供します。次に、JSON パスを含むレスポンスの一部を選択し、この場所の JSON が期待される結果と一致することをアサートします。

おめでとう! このチュートリアルでは、GraphQL サービスを構築し、最初のクエリを実行し、最初の GraphQL テストを作成しました。

参考文献

サンプルソースコード

このガイドは、GraphQL Java (英語) チームと共同で作成されました。Donna Zhou [GitHub] (英語) Brad Baker [GitHub] (英語) Andreas Marek [GitHub] (英語) に多大な感謝を! このチュートリアルのソースコードは GitHub (英語) にあります。

ドキュメント

Spring for GraphQL ドキュメントを参照してください。

GraphQL Java は、Spring for GraphQL を強化する GraphQL エンジンです。GraphQL Java ドキュメント (英語) を参照してください。

その他の Spring for GraphQL の例

1.0. ×ブランチ [GitHub] (英語) でより多くのサンプルを参照してください。これはまもなく別のリポジトリに移動 [GitHub] (英語) されます。

スタックオーバーフローに関する質問

spring-graphql でのスタックオーバーフロー (英語) タグに関する質問をすることができます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。

コードを入手する

プロジェクト