はじめに
最近、graphql-rubyでAPIを実装しています。
そこで、graphqlでのエラーハンドリングについて、ベストプラクティスを模索してみました。
まだ始めたばかりなので、もっといい方法あるよ!っていうコメント募集中です。
エラーの分類
エラーハンドリングの実装に移る前にエラーにはいくつか種類があるので、紹介します。
以下の記事が非常にわかりやすく、まとまっていたので参考にしています。
Railsアプリケーションにおけるエラー処理(例外設計)の考え方
ここでは、以下のように異常系と準異常系の2つに分類します。
- 異常系:nwエラーや、プログラムのエラー(システムエラー)
- 準異常系:作り手が予期できるエラー(業務エラー)
この2種類のエラーに対して、以下のような要件があります。
- 異常系:フロント側でテンプレートメッセージを表示 & 開発者に通知
- 準異常系:エラー内容や回避方法をフロント側(この辺りは要件によって変わってくるかもしれません)
肝となるのは、異常系は決まったテンプレートのメッセージを表示するが、準異常系はAPIからメッセージを送るというところです。この要件をどのように実装させるかが本題となります。
Graphql-rubyの仕様
graphql-rubyでは、エラーの場合以下のようなerrorsをkeyにしたjsonが返ってきます。
"errors": [
{
"message": "error message",
"locations": [],
"extensions": {
"code": "ERROR_CODE"
}
}
]
このエラーメッセージは、主にインターフェイスでバグがあったりすると発生します。
例えばフロント側で渡す引数にnullが入ってるとき等です。
上のようなエラーは以下のように直接コードを書いて発生させることもできます。
raise GraphQL::ExecutionError.new('error message', extensions: {code: "ERROR_CODE"})
実装
上記の仕様から異常系のエラーはerrorsが戻り値で返ってくることがわかります。
では準異常系はどうでしょうか?準異常系の要件は以下です。
- 画面に表示するエラーメッセージを渡す
- フロントが準異常系エラーと判断できる
まず1つ目の要件は上記のgraphqlエラーをraiseする方法でエラーメッセージを渡せるのでokです。しかし、そのままではフロント側で異常系のエラーと区別ができません。
そこで以下のようにjsonをカスタマイズすることにしました。
raise GraphQL::ExecutionError.new('error message', extensions: {customCode: "ERROR_CODE"})
code->customCodeとなっています。フロント側ではcustomCodeがkeyとして存在すれば、そのままerror messageを表示。それ以外はテンプレートのメッセージを表示させることで、異常系と準異常系の処理を切り分けることができます。