前提
GraphQL のレスポンスのフォーマットに関するベストプラクティスとして、レスポンスの HTTP ステータスコードは 200 で統一、レスポンスの errors
キーにエラーの詳細な情報を持たせる というのがある(公式な仕様ではないが、Apollo Client も graphql-ruby もこれに沿っている)
最初にまとめ
GraphQL のエラーハンドリングにはトップレベルの errors
キーにエラー内容を持たせる方法と、 data
キーの中にエラー内容を持たせる方法とある。
公式の仕様書には前者の トップレベルの errors
キーにエラー内容を持たせる方法のみが載っている。
後者は公式の仕様には無いが graphql-ruby に実装されている機能 なので使える。
- トップレベルの
errors
キーにエラー内容を持たせる方法のメリット data
キーの中にエラー内容を持たせる方法のメリット- エラーの type を定義することになるので、スキーマでエラーの構造を共有できる
しかしエラーの type を定義できないと、エラーの詳細が動的に変わりうるということなので、エラーハンドリングをクライアント側で UI として表現する場合は data
キーの中に含めた方が堅牢なアプリケーションになる。(トップレベルの errors
キーに含める場合も type 定義できれば良いのだけれど)
説明
レスポンスの形式でいうと以下のような感じ。
トップレベルの errors
キーにエラー内容を持たせる
{ "errors": [ { "message": "Can't continue with this query", "locations": [ { "line": 2, "column": 10 } ], "path": ["user", "login"] } ], "data": { // 同じレスポンスに data を含めることもできる // ... } }
data
キーの中にエラー内容を持たせる
{ "data": { "createPost": { "post": null, "errors": [ { "message": "Title can't be blank", "path": ["attributes", "title"] }, { "message": "Body can't be blank", "path": ["attributes", "body"] } ] } } }
その他
GraphQLにおけるエラーハンドリングの仕方 - ZOZO Technologies TECH BLOG がめっちゃ参考になった。
ちなみに graphql-ruby では 最近の PR でルートの Schema クラスに rescue_from
を定義できるようになり、セーフティネット的なエラーハンドリングを実現できるようになった。
これ調べてる間に graphql-ruby のドキュメントのリンク切れを見つけて投げた PR、 無事マージされた。