1前書き


GraphQL

はFacebookからの比較的新しい概念であり、Web APIのRESTの代わりとして請求されています。

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


2 GraphQLとは何ですか?

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

クライアントが同時に複数のリソースからのデータを必要とするとき、これはまた苦しみます。たとえば、ブログ投稿とコメントを要求します。

通常、これはクライアントに複数の要求を出させるか、または常に必要とは限らない追加のデータをサーバーに提供させることによって解決され、応答サイズが大きくなります。

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

また、標準的な必須の一連のアクションの代わりに名前付きのクエリと突然変異を使用して、はるかにRPCの方法で機能します。これは、それが属するコントロールを配置するために機能し、API開発者は可能なものを指定し、APIコンシューマは望ましいものを指定します。

たとえば、ブログでは次のクエリを許可する可能性があります。

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

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

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


3 GraphQL Spring Boot Starter

の紹介


  • Spring Boot GraphQLスターター

    は、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ツールライブラリは、GraphQLスキーマファイルを処理して正しい構造を構築してから、この構造に特殊なBeanを接続することによって機能します。

Spring Boot GraphQLスターターはこれらのスキーマファイルを自動的に見つけます

これらのファイルは拡張子“ .

graphqls

”を付けて保存する必要があり、クラスパス上のどこにでも存在できます。これらのファイルを必要に応じていくつでも持つことができるため、スキームを必要に応じてモジュールに分割することができます。

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


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

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

唯一の要件は、Beanが

GraphQLQueryResolver

を実装していることと、スキームからのルートクエリ内のすべてのフィールドに、これらのクラスのいずれかに同じ名前のメソッドがあることです。

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

メソッドの名前は、この順序で次のいずれかでなければなりません。

  1. <field>

  2. is <field> – フィールドが

    Boolean

    型の場合のみ

  3. <field>を取得

このメソッドは、GraphQLスキーマ内の任意のパラメータに対応するパラメータを持っている必要があり、オプションで

DataFetchingEnvironment.

型の最後のパラメータを受け取ることができます。

これから説明するように、このメソッドはGraphQLスキーマ内の型に対して正しい戻り型も返さなければなりません。

String、Int、List、

などの単純な型は、同等のJava型と一緒に使用でき、システムはそれらを自動的にマッピングします。

上記で定義されたスキーマの

recentPosts

フィールドに対するGraphQLクエリを処理するために使用されるメソッド

getRecentPosts

を定義しました。


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スキーマにマッピングされていないJava Beanのフィールドまたはメソッドは無視されますが、問題は発生しません。これはフィールドリゾルバが機能するために重要です。

たとえば、ここの

authorId

というフィールドは、前に定義したスキーマ内の何にも対応していませんが、次のステップで使用することは可能です。


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

時々、フィールドの値はロードするのに自明ではありません。これには、データベース検索、複雑な計算、その他のものが含まれます。

  • GraphQLツールには、この目的のために使用されるフィールドリゾルバの概念があります** これらは、データBeanの代わりに値を提供できるSpring Beanです。

フィールドリゾルバは、データコンテキストと同じ名前を持ち、接尾辞

Resolver

を持ち、

GraphQLResolver

インタフェースを実装する、Springコンテキスト内のBeanです。フィールドリゾルバ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サーバーはそれを取得するための作業をしないことです。つまり、クライアントがPostを取得してAuthorを要求しないと、上記の__getAuthor()メソッドは実行されず、DAO呼び出しも行われません。


3.6. NULL可能値

GraphQLスキーマには、許容される型と許容されない型があります。

これは、Javaコードで直接null値を使用することで処理できますが、同様に、ここではnull許容型としてJava 8からの新しい

Optional

typeを直接使用でき、システムは値に対して正しい処理を実行します。

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


3.7. 突然変異

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

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

その代わりに、** Mutationsを使用して、クライアントにこれが格納されているデータを変更することを通知するようにします。

Javaコードでは、

GraphQLQueryResolver

ではなく

GraphQLMutationResolver

を実装するクラスを使用して、突然変異が定義されています。

それ以外の場合は、クエリと同じ規則がすべて適用されます。その後、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 Serverと通信し、それに対してクエリや突然変異を実行することができるUIです。ダウンロード版はElectronアプリとして存在し、https://github.com/skevy/graphiql-app[ここ]から取得できます。

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

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

ただし、これはデフォルトのエンドポイント

/graphql

上でGraphQL APIをホストしている場合にのみ機能するので、そうでない場合はスタンドアロンアプリケーションが必要になります。


5概要

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

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

コードスニペットはhttps://github.com/eugenp/tutorials/tree/master/spring-boot[over on GitHub]にあります。