これは何?
バックエンドは1つでクライアントごとに、GraphQLのスキーマを持ちたい場合などに複数のGraphQLスキーマを設定する方法を書きました。
確認環境
$ bundle exec ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-darwin19]
$ bundle exec rails --version
Rails 6.0.4.6
routing 設定
今回は、スキーマ2 を追加します。
rails/sample-6/config/routes.rb
Rails.application.routes.draw do
if Rails.env.development?
# スキーマ1
mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
# スキーマ2
mount GraphiQL::Rails::Engine, as: "new", at: "/new/graphiql", graphql_path: "/new/graphql"
end
# スキーマ1
post "/graphql", to: "graphql#execute"
# スキーマ2
post "/new/graphql", to: "new_graphql#execute"
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
controller 追加
使うスキーマを Sample6NewSchema
で指定しています。
rails/sample-6/app/controllers/new_graphql_controller.rb
class NewGraphqlController < 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 = Sample6NewSchema.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
GraphQLのスキーマの設定
rails/sample-6/app/graphql/sample6_new_schema.rb
class Sample6NewSchema < GraphQL::Schema
query(::NewSchema::Types::QueryType)
# 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
# Relay-style Object Identification:
# Return a string UUID for `object`
def self.id_from_object(object, type_definition, query_ctx)
# For example, use Rails' GlobalID library (https://github.com/rails/globalid):
object.to_gid_param
end
# Given a string UUID, find the object
def self.object_from_id(global_id, query_ctx)
# For example, use Rails' GlobalID library (https://github.com/rails/globalid):
GlobalID.find(global_id)
end
end
rails/sample-6/app/graphql/new_schema/types/query_type.rb
これは Sample6NewSchema
で呼び出している Query の定義です。
module NewSchema
module Types
class QueryType < ::Types::BaseObject
# Add `node(id: ID!) and `nodes(ids: [ID!]!)`
include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
# Add root-level fields here.
# They will be entry points for queries on your schema.
field :new_post, ::Types::PostType, "Find a post by ID" do
argument :id, ID
end
# Then provide an implementation:
def new_post(id:)
Post.find_by(id: id)
end
field :new_posts, [::Types::PostType], null: false
def posts
Post.all
end
end
end
end