やる事
rails
アプリケーションでGraphQL
を開発するための初期構築と周辺ツールのセットアップを行います。
-
graphql
gem のインストール -
graphql-ruby
の初期化コマンド実行 -
AppSchema
クラスの設定項目の変更 -
rubocop-graphql
のセットアップ - スキーマ情報をファイル出力する
rake task
追加
1. graphql
gem のインストール
GraphQL
サーバーの構築にgraphql
gemを使います
+ gem 'graphql'
bundle install
2. graphql
の初期化コマンド実行
graphql
gemの機能でGraphQL
開発のセットアップを行うコマンドgraphql:install
コマンドがrails generator
として追加されているので使用します。
rails g graphql:install --skip-graphiql --skip_keeps --api=true --relay=false --skip_mutation_root_type=true
ここでは以下のオプションを指定しています。
-
graphiql
は使用しないので--skip-graphiql
を指定 - 空ディレクトリを維持する
.keep
ファイルは不要なので--skip_keeps
を指定 -
api
モードでの実装にするので--api
オプションをtrue
に指定 -
relay
は使用しないので--relay
オプションをfalse
に指定 -
mutation
は今回使わないのでskip_mutation_root_type
オプションをtrue
に指定
生成ファイル
app/graphql
ディレクトリの作成などが行われます
$ tree app/graphql/
app/graphql/
├── app_schema.rb
└── types
├── base_argument.rb
├── base_enum.rb
├── base_field.rb
├── base_input_object.rb
├── base_interface.rb
├── base_object.rb
├── base_scalar.rb
├── base_union.rb
└── query_type.rb
graphql
のエンドポイントの定義とコントローラーの雛形も作成されます。
+ post "/graphql", to: "graphql#execute"
class GraphqlController < ApplicationController
# If accessing from outside this domain, nullify the session
# This allows for outside API access while preventing CSRF attacks,
# but you'll have to authenticate your user separately
# protect_from_forgery with: :null_session
def execute
variables = prepare_variables(params[:variables])
query = params[:query]
operation_name = params[:operationName]
context = {
# Query context goes here, for example:
# current_user: current_user,
}
result = AppSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
render json: result
rescue StandardError => e
raise e unless Rails.env.development?
handle_error_in_development(e)
end
private
# Handle variables in form data, JSON body, or a blank value
def prepare_variables(variables_param)
case variables_param
when String
if variables_param.present?
JSON.parse(variables_param) || {}
else
{}
end
when Hash
variables_param
when ActionController::Parameters
variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables.
when nil
{}
else
raise ArgumentError, "Unexpected parameter: #{variables_param}"
end
end
def handle_error_in_development(e)
logger.error e.message
logger.error e.backtrace.join("\n")
render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
end
end
3. AppSchema
クラスの設定項目の変更
自動生成で作成したapp/graphql/app_schema.rb
に設定の追加、不要箇所の削除を行います。
class AppSchema < GraphQL::Schema
+ disable_introspection_entry_points unless Rails.env.development?
+
query(QueryType)
+
+ max_complexity 200
+ max_depth 30
+ default_page_size 50
+
- # For batch-loading (see https://graphql-ruby.org/dataloader/overview.html)
- use GraphQL::Dataloader
-
- # GraphQL-Ruby calls this when something goes wrong while running a query:
- def self.type_error(err, context)
- # if err.is_a?(GraphQL::InvalidNullError)
- # # report to your bug tracker here
- # return nil
- # end
- super
- end
-
- # Union and Interface Resolution
- def self.resolve_type(abstract_type, obj, ctx)
- # TODO: Implement this method
- # to return the correct GraphQL object type for `obj`
- raise(GraphQL::RequiredImplementationMissingError)
- end
-
- # Stop validating when it encounters this many errors:
validate_max_errors(100)
end
変更後のファイルの内容は以下の通りです。
class AppSchema < GraphQL::Schema
disable_introspection_entry_points unless Rails.env.development?
query(QueryType)
max_complexity 200
max_depth 30
default_page_size 50
validate_max_errors(100)
end
これらの設定を適切に行う事で、高負荷なクエリの抑制や、不要な情報の出力を防ぎ堅牢なサーバーに繋がります。
逆に、設定不足や不適切な値の設定をしてしまうことで脆弱性やサービスの安定性にもつながるのでサービスに応じて適切に設定を行うことが必要です。
設定した値の詳細
disable_introspection_entry_points
GraphQL
スキーマ情報をIntorospection
として取得可能にするのは開発環境のみで良いので、開発環境以外の場合いはdisable_introspection_entry_points
で無効化しています
max_complexity
GeaphQL
はクエリが柔軟な分、複雑なクエリの構築が行えてしまいます。そのためクエリの複雑具合を検出して一定の閾値を設け抑制する設定をmax_complexity
として追加します。
- 同様に、リソースをネストとして無限にデータ検索を行う事も可能なのでネストに制限を加える
max_depth
を指定します
default_page_size
リソースの件数が大きくなることを想定しページングのサイズのデフォルトをdefault_page_size
で指定しています
validate_max_errors
不正な入力が行われ複数のバリデーションエラーが発生する際にエラー数に制限を設定しバリデーションを中断する設定をvalidate_max_errors
として設定します
4. rubocop-graphql
のセットアップ
Gemfile
に定義を追加しインストールします。
+ gem "rubocop-graphql"
bundle install
rubocop
の設定ファイルでrubocop-graphql
を読み込むように追記します。
require:
+ rubocop-graphql
rubocop
を実行し警告が出る場合は修正していきます
rubocop
特定のクラスでdiasble
を指定する
以下の二つのクラスでdescription
定義の強制が求められますが以下のclass
では定義メソッドが存在しないのでdiasble
します。
GraphQL::Schema::Field
GraphQL::Schema::Argument
- class BaseArgument < GraphQL::Schema::Argument
+ class BaseArgument < GraphQL::Schema::Argument # rubocop:disable GraphQL/ObjectDescription NOTE: `description`は`GraphQL::Schema::Argument`にはない
end
- class BaseField < GraphQL::Schema::Field
+ class BaseField < GraphQL::Schema::Field # rubocop:disable GraphQL/ObjectDescription NOTE: `description`は`GraphQL::Schema::Field`にはない
end
5. スキーマ情報をファイル出力するrake task
追加
フロントエンド開発などで活用可能するためにGraphQL
スキーマ情報をファイル出力するrake task
追加します。
graphql-ruby
gemにはrails task
を追加するGraphQL::RakeTask
クラスが用意されているので、こちらを使用します。
ソースコードはこちら
rake taskの定義
lib/tasks
配下にgraphql_schema_dump.rake
を作成して以下の様に定義します。
require 'graphql/rake_task'
load_context = Proc do
current_user = User.first
{
current_user:,
}
end
GraphQL::RakeTask.new(schema_name: 'AppSchema', load_context:, directory: 'graphql_schema')
-
GraphQL::RakeTask
クラスをnew
する事でrails task
が追加されます。 -
schema_name
はアプリケーションで用いているスキーマクラスの名前AppSchema
(app/graphql/app_schema.rb
)を指定します。 - ログイン情報等のコンテキストをスキーマの中で扱っている場合は
load_context
を使ってダミーデータをコンテキストに引き渡すProc
を指定する必要があります。- ここではDBに存在する
User
オブジェクトを取得して引き渡しています。
- ここではDBに存在する
- ファイルを出力するディレクトリを
graphql_schema
に指定します
rake task
の確認
上記を追加すると以下の様にスキーマ出力にrake task
が追加されています。
rails task -T | grep graphql
rails graphql:pro:validate[gem_version] # Get the checksum of a graphql-pro version and compare it to published versions on GitHub and graphql-ruby.org
rails graphql:schema:dump # Dump the schema to JSON and IDL
rails graphql:schema:idl # Dump the schema to IDL in graphql_schema/schema.graphql
rails graphql:schema:json # Dump the schema to JSON in graphql_schema/schema.json
追加されたrake task
の詳細
rails graphql:pro:validate[gem_version]
- Proバージョンを使用している場合にライセンスが有効化の検証をする
rails graphql:schema:dump
- スキーマを
IDL
とJSON
形式両方でそれぞれ出力する
rails graphql:schema:idl
- スキーマを
IDL
形式で出力する
rails graphql:schema:json
- スキーマを
JSON
形式で出力する
参考
- https://github.com/rmosolgo/graphql-ruby
- https://github.com/DmitryTsepelev/rubocop-graphql
- https://graphql-ruby.org/
- https://graphql.org/
開発環境
- graphql (2.1.0)
- rails 7.0.8
- ruby 3.2.2p53