GraphQLとSpring Bootの使い方

GraphQLとSpring Bootの開始方法

1. 前書き

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

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

2. GraphQLとは何ですか?

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

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

GraphQL offers a solution to both of these problems。 これにより、クライアントは、単一の要求で子リソースをナビゲートするなど、必要なデータを正確に指定でき、単一の要求で複数のクエリを実行できます。

また、標準の必須アクションセットの代わりに名前付きクエリと突然変異を使用して、はるかに多くのRPC方式で動作します。 This works to put the control where it belongs, with the API developer specifying what is possible, and the API consumer what is desired.

たとえば、ブログでは次のクエリを許可できます。

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

このクエリは:

  • 最新の10個の投稿をリクエストする

  • 各投稿について、ID、タイトル、カテゴリをリクエストします

  • 投稿ごとに著者にリクエストし、ID、名前、サムネイルを返します

従来のREST APIでは、これには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入力不可のタイプであることを示しています。 これを持たないタイプは、サーバーからの応答でヌルになる可能性があります。 GraphQLサービスはこれらを正しく処理し、null許容型の子フィールドを安全に要求できるようにします。

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

これにより、クライアントはスキーマの変更を自動的に検出し、スキーマの動作に動的に適応するクライアントを許可できます。 信じられないほど便利な例の1つは、GraphiQLツールです(後述)。これにより、GraphQL APIとやり取りすることができます。

3. GraphQL Spring BootStarterの紹介

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a very short timeGraphQL Java Toolsライブラリと組み合わせると、サービスに必要なコードを記述するだけで済みます。

3.1. サービスの設定

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


    com.graphql-java
    graphql-spring-boot-starter
    5.0.2


    com.graphql-java
    graphql-java-tools
    5.2.4

Spring Bootはこれらを自動的に取得し、自動的に機能するように適切なハンドラーをセットアップします。

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

3.2. スキーマの作成

GraphQLツールライブラリは、GraphQLスキーマファイルを処理して正しい構造を構築し、特殊なBeanをこの構造にワイヤリングすることによって機能します。 The Spring Boot GraphQL starter automatically finds these schema files

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

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

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

The root query needs to have special beans defined in the Spring context to handle the various fields in this root query。 スキーマ定義とは異なり、ルートクエリフィールドに対して単一のSpring Beanのみが存在するという制限はありません。

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

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

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

  1. <フィールド>

  2. is –フィールドのタイプがBooleanの場合のみ

  3. get

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

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

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

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

Every complex type in the GraphQL server is represented by a Java bean –ルートクエリからロードされたか、構造内の他の場所からロードされたか。 同じJavaクラスは常に同じGraphQLタイプを表す必要がありますが、クラスの名前は必要ありません。

Fields inside the Java bean will directly map onto fields in the GraphQL response based on the name of the field

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

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

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

3.5. 複素数値のフィールドリゾルバ

場合によっては、フィールドの値をロードするのは簡単ではありません。 これには、データベース検索、複雑な計算などが含まれます。 GraphQL Tools has a concept of a field resolver that is used for this purpose.これらは、データBeanの代わりに値を提供できるSpringBeanです。

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

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

public class PostResolver implements GraphQLResolver {
    private AuthorDao authorDao;

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

これらのフィールドリゾルバがSpringコンテキストからロードされるという事実は重要です。 これにより、他のSpring管理Bean(DAOなど)と連携できます。

重要なのは、if the client does not request a field, then the GraphQL Server will never do the work to retrieve itです。 これは、クライアントが投稿を取得し、作成者を要求しない場合、上記のgetAuthor()メソッドが実行されることはなく、DAO呼び出しが行われることもないことを意味します。

3.6. ヌル可能値

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

これは、Javaコードでnull値を直接使用して処理できますが、同様に、Java 8の新しいOptional型をここでnull許容型に直接使用でき、システムは値を使用して正しい処理を行います。

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

3.7. 突然変異

これまで、私たちが行ってきたことはすべて、サーバーからデータを取得することでした。 GraphQLには、突然変異によってサーバーに保存されているデータを更新する機能もあります。

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

代わりに、Mutations should be used to inform the client that this will cause a change to the data being stored

ミューテーションは、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アプリとして存在し、hereから取得できます。

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


    com.graphql-java
    graphiql-spring-boot-starter
    5.0.2

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

5. 概要

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

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

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