1. 概要

GraphQL は、直感的で柔軟な構文に基づいてクライアントアプリケーションを構築し、データ要件と相互作用を記述することを目的としてFacebookによって作成されたクエリ言語です。

従来のREST呼び出しの主な課題の1つは、クライアントがカスタマイズされた(制限または拡張された)データのセットを要求できないことです。 ほとんどの場合、クライアントがサーバーに情報を要求すると、すべてのフィールドを取得するか、まったく取得しません。

もう1つの問題は、複数のエンドポイントを操作および維持することです。 プラットフォームが成長するにつれて、その結果、その数は増加します。 したがって、クライアントは多くの場合、さまざまなエンドポイントからのデータを要求する必要があります。

GraphQLサーバーを構築する場合、すべてのデータのフェッチと変更に必要なURLは1つだけです。 したがって、クライアントは、必要なものを記述したクエリ文字列をサーバーに送信することで、一連のデータを要求できます。

2. 基本的なGraphQLの命名法

GraphQLの基本的な用語を見てみましょう。

  • クエリ:はGraphQLサーバーに要求される読み取り専用操作です
  • Mutation:は、GraphQLサーバーに要求される読み取り/書き込み操作です。
  • Resolver: GraphQLでは、 Resolver は、リクエストの処理を担当するバックエンドで実行されている操作とコードのマッピングを担当します。 これは、RESTFulアプリケーションのMVCバックエンドに類似しています
  • Type: Type は、他の Type のエッジであるフィールドを含め、GraphQLサーバーから返される応答データの形状を定義します。
  • Input: Type、に似ていますが、GraphQLサーバーに送信される入力データの形状を定義します
  • Scalar:は、 String Int Boolean FloatなどのプリミティブなTypeです。 など
  • インターフェース:インターフェースはフィールドの名前とその引数を格納するため、GraphQLオブジェクトはそれを継承でき、特定のフィールドの使用を保証します
  • Schema: GraphQLでは、スキーマがクエリとミューテーションを管理し、GraphQLサーバーで実行できるものを定義します

2.1. スキーマの読み込み

スキーマをGraphQLサーバーにロードする方法は2つあります。

  1. GraphQLのインターフェース定義言語(IDL)を使用する
  2. サポートされているプログラミング言語の1つを使用する

IDLを使用した例を示しましょう。

type User {
    firstName: String
}

次に、Javaコードを使用したスキーマ定義の例を示します。

GraphQLObjectType userType = newObject()
  .name("User")  
  .field(newFieldDefinition()
    .name("firstName")
    .type(GraphQLString))
  .build();

3. インターフェイス定義言語

インターフェース定義言語(IDL)またはスキーマ定義言語(SDL)は、GraphQLスキーマを指定するための最も簡潔な方法です。 構文は明確に定義されており、公式のGraphQL仕様で採用されます。

たとえば、ユーザー/電子メールのGraphQLスキーマを次のように指定して作成しましょう。

schema {
    query: QueryType
}

enum Gender {
    MALE
    FEMALE
}

type User {
    id: String!
    firstName: String!
    lastName: String!
    createdAt: DateTime!
    age: Int! @default(value: 0)
    gender: [Gender]!
    emails: [Email!]! @relation(name: "Emails")
}

type Email {
    id: String!
    email: String!
    default: Int! @default(value: 0)
    user: User @relation(name: "Emails")
}

4. GraphQL-java

GraphQL-javaは、仕様とJavaScriptリファレンス実装に基づく実装です。正しく実行するには少なくともJava8が必要であることに注意してください。

4.1. GraphQL-javaアノテーション

GraphQLを使用すると、 Javaアノテーションを使用して、従来のIDLアプローチを使用して作成されたすべてのボイラープレートコードなしでスキーマ定義を生成することもできます。

4.2. 依存関係

この例を作成するために、最初に Graphql- java-annotationsモジュールに依存している必要な依存関係のインポートを開始しましょう。

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-annotations</artifactId>
    <version>3.0.3</version>
</dependency>

また、アプリケーションでのセットアップを容易にするためにHTTPライブラリを実装しています。 Ratpack を使用します(ただし、Vert.x、Spark、Dropwizard、Spring Bootなどでも実装できます)。

Ratpackの依存関係もインポートしましょう。

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.4.6</version>
</dependency>

4.3. 実装

例を作成しましょう。ユーザーに「CRUDL」(作成、取得、更新、削除、リスト)を提供する単純なAPIです。 まず、 UserPOJOを作成しましょう。

@GraphQLName("user")
public class User {

    @GraphQLField
    private Long id;
 
    @GraphQLField
    private String name;
 
    @GraphQLField
    private String email;
  
    // getters, setters, constructors, and helper methods omitted
}

このPOJOでは、 @GraphQLName( “user”)アノテーションが表示されます。これは、このクラスが@GraphQLField。でアノテーションが付けられた各フィールドとともにGraphQLによってマップされていることを示しています。

次に、UserHandlerクラスを作成します。 このクラスは、選択したHTTPコネクターライブラリ(この場合はRatpack)からハンドラーメソッドを継承します。ハンドラーメソッドは、GraphQLのリゾルバー機能を管理および呼び出します。 したがって、リクエスト(JSONペイロード)を適切なクエリまたはミューテーション操作にリダイレクトします。

@Override
public void handle(Context context) throws Exception {
    context.parse(Map.class)
      .then(payload -> {
          Map<String, Object> parameters = (Map<String, Object>)
            payload.get("parameters");
          ExecutionResult executionResult = graphql
            .execute(payload.get(SchemaUtils.QUERY)
              .toString(), null, this, parameters);
          Map<String, Object> result = new LinkedHashMap<>();
          if (executionResult.getErrors().isEmpty()) {
              result.put(SchemaUtils.DATA, executionResult.getData());
          } else {
              result.put(SchemaUtils.ERRORS, executionResult.getErrors());
              LOGGER.warning("Errors: " + executionResult.getErrors());
          }
          context.render(json(result));
      });
}

これで、クエリ操作をサポートするクラス、つまり、 UserQuery。 前述のように、サーバーからクライアントにデータを取得するすべてのメソッドは、次のクラスによって管理されます。

@GraphQLName("query")
public class UserQuery {

    @GraphQLField
    public static User retrieveUser(
     DataFetchingEnvironment env,
      @NotNull @GraphQLName("id") String id) {
        // return user
    }
    
    @GraphQLField
    public static List<User> listUsers(DataFetchingEnvironment env) {
        // return list of users
    }
}

UserQuery、と同様に、 UserMutation、を作成します。これは、サーバー側に保存されている特定のデータを変更するすべての操作を管理します。

@GraphQLName("mutation")
public class UserMutation {
 
    @GraphQLField
    public static User createUser(
      DataFetchingEnvironment env,
      @NotNull @GraphQLName("name") String name,
      @NotNull @GraphQLName("email") String email) {
      //create user information
    }
}

両方の注釈に注意する価値があります UserQuery UserMutation クラス: @GraphQLName(“ query”) @GraphQLName( “mutation”)。 これらのアノテーションは、クエリ操作とミューテーション操作をそれぞれ定義するために使用されます。

GraphQL-javaサーバーでクエリおよびミューテーション操作を実行できるため、次のJSONペイロードを使用して、サーバーに対するクライアントのリクエストをテストできます。

  • CREATE操作の場合:
{
    "query": "mutation($name: String! $email: String!){
       createUser (name: $name email: $email) { id name email age } }",
    "parameters": {
        "name": "John",
        "email": "[email protected]"
     }
}

この操作に対するサーバーからの応答として:

{
    "data": {
        "createUser": {
            "id": 1,
            "name": "John",
            "email": "[email protected]"
        }
    } 
}
  • RETRIEVE操作の場合:
{
    "query": "query($id: String!){ retrieveUser (id: $id) {name email} }",
    "parameters": {
        "id": 1
    }
}

この操作に対するサーバーからの応答として:

{
    "data": {
        "retrieveUser": {
            "name": "John",
            "email": "[email protected]"
        }
    }
}

GraphQLは、クライアントが応答をカスタマイズできる機能を提供します。 したがって、例として使用された最後のRETRIEVE操作では、名前と電子メールを返す代わりに、たとえば、電子メールのみを返すことができます。

{
    "query": "query($id: String!){ retrieveUser (id: $id) {email} }",
    "parameters": {
        "id": 1
    }
}

したがって、GraphQLサーバーから返される情報は、要求されたデータのみを返します。

{
    "data": {
        "retrieveUser": {
            "email": "[email protected]"
        }
    }
}

5. 結論

GraphQLは、REST APIの代替アプローチとして、クライアント/サーバー間の複雑さを最小限に抑える簡単で非常に魅力的な方法です。

いつものように、この例はGitHubリポジトリで入手できます。