
Ruby on Rails&GraphQLのエラーレスポンス
2022.11.07
はじめに
前回の記事の続きになります!
GraphQLにおけるエラーレスポンス
一般的なHTTP通信のエラーレスポンスでは、ステータスコードで判断しますが、基本的にGraphQLからは200か500しか返せません。
なので200のレスポンスの中でerrorsフィールドを返すというのがベストプラクティスとされているようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 | { "errors": [ { "message": "エラー内容", "locations": [...], "path": [...], "extensions": { "code": "NOT_FOUND", "timestamp": "..." } } ] } |
【参考】
https://spec.graphql.org/October2021/#sec-Errors
Ruby on Rails & GraphQLにおけるエラーレスポンスの実装
ではRails上ではどうするのかというと、GraphQL::ExecutionErrorという例外を発生させることでベストプラクティスな実装をすることができます。
【参考】
https://graphql-ruby.org/errors/error_handling.html
前回の記事で実装したUser APIにエラーレスポンスを組み込んでみましょう!
特定User取得のエラーレスポンスの現状
まず最初に現状を確認します。
rails s コマンドでサーバーを起動し、graphiqlにアクセスし、登録されていないidでリクエストしてみましょう。
errorsはレスポンスされましたが、backtraceがそのまま出ちゃっていますね。
Postmanで確認してみると、500エラーになっていることがわかります。
※ GraphqlControllerの中に protect_from_forgery を入れないと、422エラーになるので注意
これはUser Modelで発生したActiveRecord::RecordNotFoundエラーがそのまま伝播し、500エラーになっています。
なのでrescueコードを用いて、代わりにGraphQL::ExecutionErrorを伝播させましょう!
特定User取得のエラーレスポンスの実装
リクエストパラメータにあるidがUsersテーブル上に存在しない場合のエラーレスポンスを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | module Queries module Resolvers class User < GraphQL::Schema::Resolver type Types::UserType, null: false description "特定Userの取得" argument :id, String, required: true, description: "Userのid" def resolve(params) begin ::User.find(params[:id]) rescue ActiveRecord::RecordNotFound => exception raise GraphQL::ExecutionError, "id:#{params[:id]} not found" end end end end end |
User.findメソッドをbegin-rescueで囲って、GraphQL::ExecutionErrorをraiseしているだけですね。
早速実行結果を見ていきましょう!
コード上で指定したメッセージで表示されていて、backtraceも消えていますね。
Postmanで見ると、ステータスコードが200になっていることがわかります。
ただし、現状メッセージだけでしかエラーが判別できません。
ベストプラクティスにもあるように、errors内にextensions:codeで、ステータスコードのように大まかなエラー内容が区別できるようにしましょう!
エラーレスポンスにエラーコードを追加
まずはエラーコードを管理するファイルを作成します。
今回はUserが見つからないエラーなので、NOT_FOUNDのコードを実装します。
1 2 3 4 5 6 7 8 9 10 11 | module Error class GraphqlError class << self def codes { not_found: "NOT_FOUND", } end end end end |
私のプロジェクトではこれをlibフォルダに追加したので、config/application.rbに以下の変更を入れました。
1 2 3 4 5 6 7 8 | module TestGraphqlApi class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 7.0 config.paths.add 'lib', eager_load: true # ここを追加 ... end |
さきほど実装したGraphQL::ExecutionErrorに代入しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 | def resolve(params) begin ::User.find(params[:id]) rescue ActiveRecord::RecordNotFound => exception raise GraphQL::ExecutionError.new( "id:#{params[:id]} not found", extensions: { code: Error::GraphqlError.codes[:not_found] } ) end end |
こちらは単純に、GraphQL::ExecutionErrorの初期化時にextensions引数を用いてるだけですね。
これでエラーコードをレスポンスに代入することができました!
まとめ
HTTPステータスコードが指定できないので、若干ややこしい仕様になっていますが、エラーレスポンスを実装することができました。
GraphQLを学び始めたときに実装を悩んだ箇所だったので、参考になりましたらうれしいです!
今回のコードも以下のリポジトリにまとめてありますので、ぜひ確認してみてください。
https://github.com/ryuto-imai/test_graphql_api
※前回の記事時点のコードは以下のタグに残してあります
書いた人はこんな人

IT技術10月 27, 2023Jiraの自動化
IT技術8月 16, 2023Lighthouseで計測したパフォーマンススコアのばらつきを減らす方法
IT技術11月 7, 2022Ruby on Rails&GraphQLのエラーレスポンス
IT技術7月 13, 2022Ruby on Rails & GraphQLの環境構築と実装