GraphQLに精通している風を装うためのセリフ3選

## はじめに

GraphQLに関するOSSにコミットする中で、それ以前のGraphQL APIの開発では気づかなかった、GraphQLの通なポイントを3つのセリフにまとめてみました。知ったかする際に役に立つと思うので、ぜひご活用ください！

## 1. GraphQLって9割がたフィールドだよね

GraphQLにはQuery, Mutation, Subscriptionの3つがある、というのはご存知だと思います。だいたい以下のような役割分担です。

- Query: データ取得用
- Mutation: データ変更用
- Subscription: サーバからのプッシュ用

例えば「ユーザ一覧を取得するための`users`というQuery」を作ったり「ユーザを新規登録するための`createUser`というMutation」を作ったりするイメージです。

ここで、`users`や`createUser`をQueryやMutationと呼んでも間違いではないのですが、正確には「Queryというルートオブジェクトのフィールドである`users`」や「Mutationというルートオブジェクトのフィールドである`createUser`」だったりします。

**つまり「ユーザ一覧を取得するための`users`というQueryを作ろう」は「ユーザ一覧を取得するための`users`フィールドをQueryオブジェクトに追加しよう」と言い換えたほうが、GraphQL的には正確で、ちゃんと理解しているんだぞ感をかもし出すことができます。**

もちろん、単にめんどくさがれる可能性が高いです。

## 2. インプットにUnion型がないのは残念だよね

GraphQLのインプットにはUnion型がありません。補足しておくと、Union型は`A | B`のように、AまたはBのどちらかの型を受け取れる型、みたいなやつです。

なお、アウトプットにはUnion型があります。以下のような感じで、直感的にUnionを定義することができます。しかしこれをインプットとして使おうとすると怒られてしまいます。

```graphql
union Media = Book | Movie
```

**この仕様はリッチな型定義に慣れた開発者に評判が悪かったのか、インプットでもUnion型をサポートしようよという[RFC](https://github.com/graphql/graphql-spec/issues/488)が立ちました。その後紆余曲折あり`@oneOf`というディレクティブが導入される予定になっています。**

これまでのGraphQLでは、型的に堅牢であろうとすると、例えば以下のように、IDでユーザを取得するため`userByID`とメールアドレスで取得するための`userByEmail`を別々に定義することを求められる場合がありました。

```graphql
type Query {
    userByID(id: ID!): User
    userByEmail(email: String!): User
}
```

これを`@oneOf`を使うことで、以下のように1つの`user`というQuery(正確にはQueryオブジェクトのフィールド)にまとめることができます。`@oneOf`によって`id`または`email`のどちらか1つのみが指定されることが保証されるため、型的にストイックな方も満足できる仕様になっていると思われます。

```graphql
type Query {
    user(by: UserByInput!): User
}

input UserByInput @oneOf {
    id: ID
    email: String
}
```

参考: [Coming Soon To GraphQL: The oneof Input Object](https://stepzen.com/blog/coming-soon-to-graphql-the-oneof-input-object)


## 3. ResolveInfoを使いこなして初めてGraphQLに価値が生まれるよね

ResolveInfoは(正式にはGraphQLResolveInof)は、多少込み入った処理をする際に必要になってくる奴だと思ってください。

例えば以下のクエリを例にResolveInfoの利用例を考えてみましょう。このクエリは、各ユーザの`id`と`name`、さらに各ユーザに紐づく投稿(posts)の`id`と`title`を取得するためのクエリです。

```graphql
query {
  users {
    id
    name
    posts {
      id
      title
    }
  }
}
```

GraqhQLではクエリで指定された各フィールドについて、それぞれのフィールドに設定されたresolverを順々に呼び出すことで、最終的な戻り値を生成します。上記のクエリの場合、実際の処理の流れは以下のようになります。

1. ルートオブジェクトであるQueryの`users`フィールドのresolverが呼ばれる
2. (1)のresolverの実行結果がN件(N > 0)のデータを返す場合、各データについて`id`、`name`、`posts`フィールドのresolverがN回呼ばれる
3. `posts`フィールドのresolverの実行結果がM件(M > 0)のデータを返す場合、各データについて`id`、`title`フィールドのresolverがM回呼ばれる

各フィールドのresolverのデフォルト(=resolverが未設定の場合)の挙動は、親フィールドのresolverが返した値(オブジェクト)から、自身のフィールド名のプロパティを返す、というものです。例えば(1)で返される値が`[{ id: 1, name: "Jon", posts: [] }]`だった場合、(2)の`id`フィールドのデフォルトresolverは`1`を返し、`name`フィールドは`"Jon"`、`posts`フィールドは`[]`を返します。

つまり、大本の`users`フィールドのresolverが、指定されたフィールドの値をすべて生成して返すことができれば、それ以外のフィールドはデフォルトresolverに任せることができます。今回の例であれば、`users`フィールドのresolver内で、上記のGraphQLクエリをもとに、以下のようなSQLなどを生成しDBから値を取得することができれば良さそうです。

```sql
SELECT
  User.id,
  User.name,
  Post.id as postId,
  Post.title
FROM
  User INNER JOIN Post ON User.id = Post.user_id
```

**このような、GraphQLクエリからSQLへの変換の仕組みが、例の[Hasura](https://hasura.io/)や[PostGraphile](https://www.graphile.org/postgraphile/)で実装されているものです(たぶん)。そして、そのために必要なのがResolveInfoになります。ResolveInfoからクエリで指定されたフィールドを取得し、そこからSQLを生成する、という流れです。**

それ以外にも、各フィールドに処理を任せるより、ResolveInfoを用いてルートのフィールドでまとめて処理をしたほうがパフォーマンスが出るなどの理由で、argsのバリデーションや権限制御などの処理で、ResolveInfoは活躍します。

実際にResolveInfoからどんな値が取得できるのか、詳しくはGraphQL.jsの[型定義](https://github.com/graphql/graphql-js/blob/a24a9f35b876bdd0d5050eca34d3020bd0db9a29/src/type/definition.ts#L891-L902)をご参照ください。

## まとめ

- GraphQLって9割がたフィールドだよね
- インプットにUnion型がないのは残念だよね
- ResolveInfoを使いこなして初めてGraphQLに価値が生まれるよね

さり気なくこの3フレーズを使うことで、皆さんの単価などが上がることを願っています。
もし興味が湧いた方は、この記事のきっかけとなったOSSとGraphQLに関する[チュートリアル](https://zenn.dev/dekimasoon/articles/31f4a740f670af)も見てみてください。