GraphQL Error、下から見るか?横から見るか?
タイトルに深い意味はなくて、思いついちゃったので書いてみただけです。
GraphQL API を設計するときに「クエリの一部がエラーとなってしまった場合に、そのエラー情報をどう伝播させるか」という部分について、悩みの種になりそうだなーといつも思っていたのだけど、実はもう決着がついていそう。
結論を先に書いてしまうと「Partial Error については GraphQL errors field を使わずに、Schema 上で Model として設計しろ」をベストプラクティスとして良さそう。
Facebook Relay がアプリケーションエラーについて下記のように見解を示していたことを社内で教えてもらったのがきっかけ。
If you wish to access error information in your application to display user friendly messages, the recommended approach is to model and expose the error information as part of your GraphQL schema.
要するに、ユーザーにわかりやすいエラー情報を表示したければ、Schema の Model 情報としてエラー設計しなさいよ、ということだ。
そのまま Relay のドキュメントから転載するが、下記のように明示的に Schema 上で Error type を表すイメージ。
type Error {
# User friendly message
message: String!
}type Foo {
bar: Result | Error
}
クエリ側からは、下のように Type Condition を使い、エラーなのか正常にデータ取得ができたのかを判別する。
fragment fooFragment {
bar {
__typename
... on Result {
# 正常系のセレクション
}
... on Error {
message
}
}
}
一方、GraphQL 自体に備わっている errors
から エラー情報(どのフィールドが取得できなかったのか)を拾う、という選択肢もあるにはある。こちらは GraphQL の spec で規定されているやり方。
{
errors: [
{
message: "なんかだめ",
path: ["hoge", "foo", "bar"]
}
],
data: {
hoge: {
foo: {
// 正常に取得できた部分
}
}
}
};
冒頭の Relay のドキュメントを読んだ際に「なんで Relay は(GraphQL に用意されている方法をつかわずに)エラーを Schema で表現する方法を推しているんだろう?」という疑問が湧いた(how だけが書いてあって why が見当たらなかった)。
GraphQL のエラー設計自体についての考察としてはそれほど新しい話でもなくて、上記のパターンの比較も色々と書いてあるんだけど、一番納得感があったのが https://blog.logrocket.com/handling-graphql-errors-like-a-champ-with-unions-and-interfaces/ の記事。
この記事の中で次のように書いてあり、この一文が最も納得感があった。
The errors are not collocated to where they occur
GraphQL の errors
フィールドの場合、コロケーションと組み合わせとの相性があまりにも悪いのだ。
コロケーションを活用している場合、Fragment とともに対となる Component が存在しているはずで、その Component からしたら「末端の情報にしか興味がない」状態であり、逆に集約したクエリを叩く層から見ると、Fragment で管理されている枝葉末節についてのエラーを伝えられても困る」となる。
useFragment
や FragmentContainer
のように、Fragment を一級市民として扱ってきた Relay においては、エラー情報も Fragment に詰まっている方が圧倒的に扱いやすいわけだ。
ところで、GraphQL 自体も元はと言えば Facebook が自社のために作っていたプロトコルなわけで、仕様側はエラー設計についてどう考えてるんだろと思い、ついでに調べてみたところ、Lee Byron も下記のように書いていた。
Since discussing this issue, a common best practice has been to include user errors as part of the schema itself so they can contain domain specific information.
2024.10.16 追記
Relay v18 からは Relay Runtime が GraphQL errors
フィールドを useFragment
などの結果に分配する機能が備わったため、コンポーネントレイヤから GraphQL Errors が扱いやすくなっている: