【GraphQL】 There can be only one fragment エラーの対処法
GraphQL Code Generatorを用いたfragmentを含んだクエリで "There can be only one fragment" エラーが発生した場合の対処法を解説します。
( @graphql-codegen/cli@2.16
, @graphql-codegen/client-preset@1.2
を使用しています)
背景
とあるプロダクトでドキドキしながらGraphQLで通信するフロントエンドとBFFを初めて結合して動作確認してみたとき、BFFから "There can be only one fragment" エラーが返ってきてしまいました。
{
"errors": [
{
"message": "There can be only one fragment named \"AFragment\".",
"locations": [{ "line": 99, "column": 1 }],
"extensions": { "code": "GRAPHQL_VALIDATION_FAILED" }
}
],
"data": null
}
該当のqueryはこのように書いています。GraphQL Code Generatorのclient-presetを活用しており、それぞれのFragmentは子コンポーネントに定義してあります。
import { graphql } from "@/libs/gql";
const query = graphql`
query Query {
examples {
id
...AFragment # 子コンポーネントにて定義
...BFragment # 子コンポーネントにて定義
}
}
`;
原因
原因は1つのリクエストに同じ命名のfragmentが複数存在したためでした。
GraphQL Code Generatorで生成して実際に送信されたqueryは次の通りになっていました。
query Query {
examples {
id
...AFragment
...BFragment
}
}
fragment AFragment on Example {
id
name
}
fragment BFragment on Example {
id
...AFragment
}
fragment AFragment on Example {
id
name
}
ここで、 AFragment
が重複しているためエラーが発生したのだと推測できます。
対処方法
BFragment
で参照している AFragment
と、 Query
で参照している AFragment
は同じものなのですが、 GraphQL Code Generatorが生成する際、fragmentの参照を発見するたびに末尾へfragmentをコピーする仕様であるため発生しています。
コンポーネント駆動開発 + Fragment Colocation (Fragment Masking) のモデルを採用しており、1回のリクエストで同じFragmentを複数回参照することは避けられないため、設定変更で回避することにしました。
GraphQL Code GeneratorにおけるTypeScript Operationsの設定を変更します。
schema: "../graphql/**/*.graphqls"
documents:
- "src/**/*.{ts,tsx}"
generates:
./src/generated/gql/:
preset: "client"
config:
dedupeFragments: true
dedupeFragments: true
を設定することで、二階層目以上のFragmentの中身がすべて一階層目のFragmentへ集約されるため、結果として重複したFragmentが作られなくなります。
graphql-codegen
を実行して自動生成コードを更新することで問題が解決します。
まとめ
GraphQL Code Generatorでfragmentを使う際には、 dedupeFragments: true
を設定するようにしましょう。
client-presetは最高の開発体験だったので、また別の機会にご紹介します。
Discussion