1. 序章

GraphQL は、Facebookの比較的新しい概念であり、REST forWebAPIの代替として請求されます。

このチュートリアルでは、Spring Bootを使用してGraphQLサーバーをセットアップし、既存のアプリケーションに追加したり、新しいアプリケーションで使用したりできるようにする方法を学習します。

2. GraphQL とは何ですか?

従来のRESTAPIは、サーバーが管理するリソースの概念で機能します。 これらのリソースは、さまざまなHTTP動詞に従って、いくつかの標準的な方法で操作できます。 これは、APIがリソースの概念に適合している限り非常にうまく機能しますが、それから逸脱する必要がある場合はすぐに崩壊します。

これは、ブログの投稿やコメントのリクエストなど、クライアントが同時に複数のリソースからのデータを必要とする場合にも問題になります。 通常、これは、クライアントに複数の要求を行わせるか、サーバーに常に必要とは限らない追加のデータを提供させて、応答サイズを大きくすることで解決されます。

GraphQLは、これらの問題の両方に対する解決策を提供します。 これにより、クライアントは、単一のリクエストで子リソースをナビゲートするなど、必要なデータを正確に指定でき、単一のリクエストで複数のクエリを実行できます。

また、標準の必須のアクションセットの代わりに、名前付きクエリとミューテーションを使用して、はるかにRPCの方法で機能します。 これは、コントロールを所属する場所に配置するために機能します。API開発者は可能なことを指定し、APIコンシューマーは必要なものを指定します。

たとえば、ブログで次のクエリが許可される場合があります。

query {
    recentPosts(count: 10, offset: 0) {
        id
        title
        category
        author {
            id
            name
            thumbnail
        }
    }
}

このクエリは次のようになります。

  • 最新の10件の投稿をリクエストする
  • 投稿ごとに、ID、タイトル、カテゴリをリクエストします
  • 投稿ごとに、作成者をリクエストし、ID、名前、サムネイルを返します

従来のRESTAPIでは、これには11のリクエスト(投稿用に1つ、作成者用に10つ)が必要であるか、投稿の詳細に作成者の詳細を含める必要があります。

2.1. GraphQLスキーマ

GraphQLサーバーは、APIを記述するスキーマを公開します。 このスキームは、タイプ定義で構成されています。 各タイプには1つ以上のフィールドがあり、それぞれが0個以上の引数を取り、特定のタイプを返します。

グラフは、これらのフィールドが互いにネストされている方法から導き出されます。 グラフは非周期的である必要はなく、サイクルは完全に許容可能ですが、方向付けられていることに注意してください。 つまり、クライアントは1つのフィールドからその子に到達できますが、スキーマでこれが明示的に定義されていない限り、自動的に親に戻ることはできません。

ブログのGraphQLスキーマの例には、投稿、投稿の作成者、およびブログの最新の投稿を取得するためのルートクエリを説明する次の定義が含まれている場合があります。

type Post {
    id: ID!
    title: String!
    text: String!
    category: String
    author: Author!
}

type Author {
    id: ID!
    name: String!
    thumbnail: String
    posts: [Post]!
}

# The Root Query for the application
type Query {
    recentPosts(count: Int, offset: Int): [Post]!
}

# The Root Mutation for the application
type Mutation {
    writePost(title: String!, text: String!, category: String) : Post!
}

「!」 一部の名前の末尾にあるのは、それがnull許容型ではないことを示しています。 これがないタイプは、サーバーからの応答でnullになる可能性があります。 GraphQLサービスはこれらを正しく処理し、null許容型の子フィールドを安全に要求できるようにします。

GraphQLサービスは、標準のフィールドセットを使用してスキーマ自体も公開し、クライアントが事前にスキーマ定義を照会できるようにします。

これにより、クライアントはスキーマが変更されたことを自動的に検出でき、スキーマの動作方法に動的に適応できるようになります。 この非常に便利な例の1つは、GraphiQLツールです。これにより、任意のGraphQLAPIと対話できます。

3. GraphQL SpringBootStarterの紹介

Spring Boot GraphQL Starterは、GraphQLサーバーを非常に短時間で実行するための素晴らしい方法を提供します GraphQL Java Tools ライブラリと組み合わせると、サービスに必要なコードを記述するだけで済みます。

3.1. サービスの設定

これが機能するために必要なのは、正しい依存関係だけです。

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>5.0.2</version>
</dependency>
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>5.2.4</version>
</dependency>

Spring Bootはこれらを自動的に取得し、動作する適切なハンドラーを設定します。

デフォルトでは、これにより、アプリケーションの /graphql エンドポイントでGraphQLサービスが公開され、GraphQLペイロードを含むPOSTリクエストが受け入れられます。 必要に応じて、application.propertiesファイルでこのエンドポイントをカスタマイズできます。

3.2. スキーマの作成

GraphQL Toolsライブラリは、GraphQLスキーマファイルを処理して正しい構造を構築し、この構造に特別なBeanをワイヤリングすることで機能します。 SpringBootGraphQLスターターはこれらのスキーマファイルを自動的に検出します

これらのファイルは拡張子「。graphqls、」で保存する必要があり、クラスパスのどこにでも存在できます。 これらのファイルは必要な数だけ持つことができるので、必要に応じてスキームをモジュールに分割できます。

1つの要件は、ルートクエリが1つだけで、ルートミューテーションが1つまででなければならないことです。 スキームの他の部分とは異なり、これをファイル間で分割することはできません。 これは、Java実装ではなく、GraphQLスキーマ定義自体の制限です。

3.3. ルートクエリリゾルバ

このルートクエリのさまざまなフィールドを処理するには、ルートクエリにSpringコンテキストで定義された特別なBeanが必要です。 スキーマ定義とは異なり、ルートクエリフィールドにはSpringbeanが1つしかないという制限はありません。

唯一の要件は、Beanが GraphQLQueryResolver、を実装し、スキームのルートクエリのすべてのフィールドに同じ名前のこれらのクラスの1つにメソッドがあることです。

public class Query implements GraphQLQueryResolver {
    private PostDao postDao;
    public List<Post> getRecentPosts(int count, int offset) {
        return postsDao.getRecentPosts(count, offset);
    }
}

メソッドの名前は、次のいずれかである必要があります。

  1. –フィールドがタイプの場合のみブール値
  2. 得る

メソッドには、GraphQLスキーマの任意のパラメーターに対応するパラメーターが必要であり、オプションでtypeDataFetchingEnvironment。の最終パラメーターを受け取ることができます。

これから説明するように、メソッドはGraphQLスキームの型に対して正しい戻り型も返す必要があります。 String、Int、List、などの単純な型を同等のJava型で使用でき、システムはそれらを自動的にマップします。

上記は、メソッド getRecentPosts、を定義します。このメソッドは、前に定義したスキーマのreservedPostsフィールドに対するGraphQLクエリを処理するために使用します。

3.4. Beanを使用したタイプの表現

GraphQLサーバーのすべての複合型は、Java Beanによって表されます。ルートクエリからロードされたか、構造内の他の場所からロードされたかは関係ありません。 同じJavaクラスは常に同じGraphQLタイプを表す必要がありますが、クラスの名前は必須ではありません。

Java Bean内のフィールドは、フィールドの名前に基づいてGraphQL応答のフィールドに直接マップされます。

public class Post {
    private String id;
    private String title;
    private String category;
    private String authorId;
}

GraphQLスキーマにマップされないJavabeanのフィールドまたはメソッドは無視されますが、問題は発生しません。 これは、フィールドリゾルバが機能するために重要です。

たとえば、ここでフィールド authorId は、前に定義したスキーマのいずれにも対応していませんが、次のステップで使用できます。

3.5. 複雑な値のフィールドリゾルバー

場合によっては、フィールドの値をロードするのは簡単ではありません。 これには、データベースの検索、複雑な計算などが含まれる場合があります。 GraphQL Toolsには、この目的で使用されるフィールドリゾルバーの概念があります。これらは、データbeanの代わりに値を提供できるSpringbeanです。

フィールドリゾルバーは、データBeanと同じ名前で、接尾辞 Resolver が付いた、Spring Context内の任意のBeanであり、GraphQLResolverインターフェイスを実装します。 フィールドリゾルバーBeanのメソッドは、データBeanの場合と同じルールをすべて実行しますが、データBean自体を最初のパラメーターとして提供します。

フィールドリゾルバーとデータBeanの両方に同じGraphQLフィールドのメソッドがある場合、フィールドリゾルバーが優先されます。

public class PostResolver implements GraphQLResolver<Post> {
    private AuthorDao authorDao;

    public Author getAuthor(Post post) {
        return authorDao.getAuthorById(post.getAuthorId());
    }
}

これらのフィールドリゾルバーがSpringコンテキストからロードすることが重要です。 これにより、他のSpringマネージドBeanを使用できるようになります。 DAO。

重要なのは、クライアントがフィールドを要求しない場合、GraphQLサーバーはフィールドを取得するための作業を行わない。 これは、クライアントが投稿を取得して作成者を要求しない場合、上記の getAuthor()メソッドは実行されず、DAO呼び出しは行われないことを意味します。

3.6. ヌル可能値

GraphQLスキーマには、一部の型はnull許容であり、他の型はnull可能ではないという概念があります。

これは、Javaコードでnull値を直接使用して処理します。 逆に、Java 8の新しいオプション型をnull許容型に直接使用すると、システムは値を使用して正しい処理を実行します。

これは、Javaコードがメソッド定義のGraphQLスキーマと明らかに同じであることを意味するため、非常に便利です。

3.7. 突然変異

これまでのところ、サーバーからデータを取得することだけを行ってきました。 GraphQLには、ミューテーションによってサーバーに保存されているデータを更新する機能もあります。

コードの観点からは、クエリがサーバー上のデータを変更できない理由はありません。 引数を受け入れ、新しいデータを保存し、それらの変更を返すクエリリゾルバーを簡単に作成できます。 これを行うと、APIクライアントに驚くべき副作用が発生し、悪い習慣と見なされます。

代わりに、 Mutationsを使用して、保存されているデータに変更が生じることをクライアントに通知する必要があります

ミューテーションは、 GraphQLQueryResolverの代わりにGraphQLMutationResolver、を実装するクラスを使用してJavaコードで定義されます。

それ以外の場合は、クエリの場合と同じルールがすべて適用されます。 次に、Mutationフィールドからの戻り値は、Queryフィールドからの戻り値とまったく同じように扱われ、ネストされた値も取得できるようになります。

public class Mutation implements GraphQLMutationResolver {
    private PostDao postDao;

    public Post writePost(String title, String text, String category) {
        return postDao.savePost(title, text, category);
    }
}

4. GraphiQLの紹介

GraphQLには、GraphiQLと呼ばれるコンパニオンツールもあります。 これは、任意のGraphQLサーバーと通信し、それに対してクエリとミューテーションを実行できるUIです。 ダウンロード可能なバージョンはElectronアプリとして存在し、こちらから取得できます。

GraphiQL Spring Bootスターターの依存関係を追加することで、WebベースバージョンのGraphiQLをアプリケーションに自動的に含めることもできます。

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphiql-spring-boot-starter</artifactId>
    <version>5.0.2</version>
</dependency>

これは、 /graphql;のデフォルトエンドポイントでGraphQLAPIをホストしている場合にのみ機能します。そうでない場合は、スタンドアロンアプリケーションが必要になります。

5. 概要

GraphQLは非常にエキサイティングな新しいテクノロジーであり、WebAPIの開発方法に革命をもたらす可能性があります。

Spring BootGraphQLStarterとGraphQLJavaToolsライブラリの組み合わせにより、このテクノロジーを新規または既存のSpringBootアプリケーションに非常に簡単に追加できます。

コードスニペットは、GitHubにあります。