🐈

【GraphQL】boolean以外のvariableの値に応じてfieldを呼ぶか決めたい(できないっぽい)

2024/07/27に公開

頭出し

GraphQL APIをバックエンドにしているフロントエンドアプリケーションを開発する際は、gqlリテラルをよく書くかと思います。
普通のユースケースであれば、fieldを書いてcodegen!ってやっていれば特段問題ないかと思います。
ですが、まれに「variableの値に応じてfieldを呼ぶか決めたい」ケースがあるかと思います。

このvariableがboolean型であれば特に問題ありません。
GraphQLではDirectivesがサポートされていて、こんな感じ↓でboolの値をfieldの前(この例で言うとfriendsの@includeの後)につけてあげればOK!

query Hero($episode: Episode, $withFriends: Boolean!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}

Directivesのドキュメント:https://graphql.org/learn/queries/#directives

@include(if: Boolean) Only include this field in the result if the argument is true.
@skip(if: Boolean) Skip this field if the argument is true.

本題

では、今回のタイトルにあるように、variableがstringやnumberの場合はどうでしょうか?
例えば、friendsフィールドの何人分の友達を取得するか、のargumentとして$withFriendsCountがあるとします。
クエリに$withFriendsCountというvariableを追加して、$withFriendsCountが渡された時だけ$withFriendsCount人分のfriendsフィールドを取得したい(並び順はBackendに任せる想定とします)、逆にvariableを渡さないときはfriendsフィールドを呼ばない、としたい場合はこんな感じ↓に書けるのかな〜と妄想しながらググってみました。

query Hero($episode: Episode, $withFriendsCount: Number) {
  hero(episode: $episode) {
    name
    # こんな感じで条件書けると良いな〜
    friends(withFriendsCount: $withFriendsCount) @include(if: $withFriendsCount > 0) {
      name
    }
  }
}

解決方法

残念ながら、上記のように@includeもしくは@skipに条件は書けないようです。

理由

GraphQLはシンプルに設計されているから!
確かに@includeの中に条件を書くのはシンプルでないかもしれない。

GraphQL is designed to be as simple as possible so it doesn't support constructions from programming languages, e.g. comparations.

こちらのissueにてGraphQLのdirectorの人が、「GraphQLではdirectives内での条件分岐コードをサポートしていなく、booleanを複数渡してあげえると良いかもね〜」と仰っています。
(badがthumbs upの倍になっているw)

To answer the question directly - GraphQL does not support conditions like this. Multiple booleans may be a fit for your use case

じゃあどうするの?

解決策1

上記の僕の妄想を現実にするには、booleanのvariableを1個追加してあげればなんとかなりそうです。

query Hero(
    $episode: Episode,
    # デフォルト値を入れておく
    $withFriendsCount: Number = 0,
    $shouldCallFriends: Boolean = false) {
  hero(episode: $episode) {
    name    
    friends(withFriendsCount: $withFriendsCount) @include(if: $shouldCallFriends) {
      name
    }
  }
}

うーん、、、、variableを1つで収めたいところではありますね。。

解決策2

クエリそのものの設計を見直しましょう!
上記の例だとheroフィールドのargumentにwithFriendsCountを追加して、Backendで値に応じて処理してあげると良さそうです。

参考

https://github.com/graphql/graphql-spec/issues/414

株式会社モニクル

Discussion