概要
GraphQL Rubyの resolver
についてまとめました。
開発環境
- ruby 2.6.5
- rails 5.2.4
- graphql 1.10.10
resolverを使う前のGraphQL 実装
-
Rails Model
# == Schema Information
#
# Table name: subject_scores
#
# id :integer
# subject_name(科目名) :string
# user_name(ユーザー名) :string
# score(点数) :integer
class SubjectScore < ApplicationRecord
end
- Types class
module Types
class QueryType < GraphQL::Schema::Object
field :subject_scores, [SubjectScoreType], 'Returns all subject scores', null: true
end
end
module Types
class SubjectScoreType < GraphQL::Schema::Object
field :id, Integer, null: true
field :subject_name, String, null: true
field :user_name, String, null: true
field :score, Integer, null: true
end
end
- Request
subjectScores: {
id
subjectName
userName
score
}
- Response (データベースの値がそのまま返却される)
"data": {
"subjectScores": [
{"id": 1, "subjectName": "英語", "userName": "太郎1", "score": 25},
{"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50},
{"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60}
]
}
resolverで値を加工して返却する
scoreが30点未満のユーザー名をマスキングしてみます。
resolver パターン1
-
参考: https://graphql-ruby.org/fields/introduction.html#field-resolution
field名と同じメソッド名を定義するパターン -
resolverの定義
module Types
class SubjectScoreType < GraphQL::Schema::Object
field :id, Integer, null: true
field :subject_name, String, null: true
field :user_name, String, null: true
field :score, Integer, null: true
def user_name
if object.score < 30
'***'
else
object.user_name
end
end
end
end
- Response(30点未満のユーザー名がマスキングされる)
"data": {
"subjectScores": [
{"id": 1, "subjectName": "英語", "userName": "***", "score": 25},
{"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50},
{"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60}
]
}
resolver パターン2
-
参考: https://graphql-ruby.org/fields/introduction.html#field-resolution
field
のresolver_method
属性を使うパターン -
resolverの定義
module Types
class SubjectScoreType < GraphQL::Schema::Object
field :id, Integer, null: true
field :subject_name, String, null: true
field :user_name, String, null: true, resolver_method: :user_name_masking_resolver
field :score, Integer, null: true
def user_name_masking_resolver
if object.score < 30
'***'
else
object.user_name
end
end
end
end
- Response(30点未満のユーザー名がマスキングされる)
"data": {
"subjectScores": [
{"id": 1, "subjectName": "英語", "userName": "***", "score": 25},
{"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50},
{"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60}
]
}
resolver パターン3
https://graphql-ruby.org/fields/resolvers.html#using-resolver
field
の resolver
属性を使うパターン。
(resolverクラスを別で定義する必要があります)
- resolverクラスの定義
# app/graphql/resolvers/base.rb
module Resolvers
class Base < GraphQL::Schema::Resolver
end
end
module Resolvers
class MaskingScore < Resolvers::Base
type String, null: true
def resolve
if object.score < 30
'***'
else
object.user_name
end
end
end
end
-
user_name
fieldのresolver
属性にResolvers::MaskingScore
を指定します。
module Types
class SubjectScoreType < GraphQL::Schema::Object
field :id, Integer, null: true
field :subject_name, String, null: true
field :user_name, String, null: true, resolver: Resolvers::MaskingScore
field :score, Integer, null: true
end
end
- Response(30点未満のユーザー名がマスキングされる)
"data": {
"subjectScores": [
{"id": 1, "subjectName": "英語", "userName": "***", "score": 25},
{"id": 1, "subjectName": "英語", "userName": "太郎2", "score": 50},
{"id": 1, "subjectName": "英語", "userName": "太郎3", "score": 60}
]
}
まとめ
この例ではパターン1で十分ですが、
resolverを共通処理化し、複数のfieldでそのresolverを使う場合もあると思います。(実際にありました)
その場合はパターン2、3が有効かと。