【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