Ruby on Rails&GraphQLのエラーレスポンス
IT技術
はじめに
前回の記事の続きになります!
2022.07.13Ruby on Rails & GraphQLの環境構築と実装はじめにRailsとGraphQLで開発しているプロジェクトに参画したのですが、環境構築を1からしたことがなかったので...
GraphQLにおけるエラーレスポンス
一般的なHTTP通信のエラーレスポンスでは、ステータスコードで判断しますが、基本的にGraphQLからは200か500しか返せません。
なので200のレスポンスの中でerrorsフィールドを返すというのがベストプラクティスとされているようです。
1{
2 "errors": [
3 {
4 "message": "エラー内容",
5 "locations": [...],
6 "path": [...],
7 "extensions": {
8 "code": "NOT_FOUND",
9 "timestamp": "..."
10 }
11 }
12 ]
13}
【参考】
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テーブル上に存在しない場合のエラーレスポンスを実装します。
1module Queries
2 module Resolvers
3 class User < GraphQL::Schema::Resolver
4 type Types::UserType, null: false
5 description "特定Userの取得"
6
7 argument :id, String, required: true, description: "Userのid"
8
9 def resolve(params)
10 begin
11 ::User.find(params[:id])
12 rescue ActiveRecord::RecordNotFound => exception
13 raise GraphQL::ExecutionError, "id:#{params[:id]} not found"
14 end
15 end
16 end
17 end
18end
User.findメソッドをbegin-rescueで囲って、GraphQL::ExecutionErrorをraiseしているだけですね。
早速実行結果を見ていきましょう!
コード上で指定したメッセージで表示されていて、backtraceも消えていますね。
Postmanで見ると、ステータスコードが200になっていることがわかります。
ただし、現状メッセージだけでしかエラーが判別できません。
ベストプラクティスにもあるように、errors内にextensions:codeで、ステータスコードのように大まかなエラー内容が区別できるようにしましょう!
エラーレスポンスにエラーコードを追加
まずはエラーコードを管理するファイルを作成します。
今回はUserが見つからないエラーなので、NOT_FOUNDのコードを実装します。
1module Error
2 class GraphqlError
3 class << self
4 def codes
5 {
6 not_found: "NOT_FOUND",
7 }
8 end
9 end
10 end
11end
私のプロジェクトではこれをlibフォルダに追加したので、config/application.rbに以下の変更を入れました。
1module TestGraphqlApi
2 class Application < Rails::Application
3 # Initialize configuration defaults for originally generated Rails version.
4 config.load_defaults 7.0
5 config.paths.add 'lib', eager_load: true # ここを追加
6
7 ...
8end
さきほど実装したGraphQL::ExecutionErrorに代入しましょう。
1def resolve(params)
2 begin
3 ::User.find(params[:id])
4 rescue ActiveRecord::RecordNotFound => exception
5 raise GraphQL::ExecutionError.new(
6 "id:#{params[:id]} not found",
7 extensions: {
8 code: Error::GraphqlError.codes[:not_found]
9 }
10 )
11 end
12end
こちらは単純に、GraphQL::ExecutionErrorの初期化時にextensions引数を用いてるだけですね。
これでエラーコードをレスポンスに代入することができました!
まとめ
HTTPステータスコードが指定できないので、若干ややこしい仕様になっていますが、エラーレスポンスを実装することができました。
GraphQLを学び始めたときに実装を悩んだ箇所だったので、参考になりましたらうれしいです!
今回のコードも以下のリポジトリにまとめてありますので、ぜひ確認してみてください。
https://github.com/ryuto-imai/test_graphql_api
※前回の記事時点のコードは以下のタグに残してあります
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
おすすめ記事
immichを知ってほしい
2024.10.31