前回、Graphql-RubyでActiveRecordのInterfaceを定義しました。(前回はこちら)
今回は、ポリフォーリズムとしてのInterfaceを利用してみたいと思います。
#設定
あるシステムのエンドユーザは、店員(staff)と社員(employee)の2つのオブジェクトによって利用されるとしましょう。この2つのオブジェクトは,nameとemailという共通するインスタンス変数を持っています。よって、この2つをInterfaceとして定義します。
#Interface Type
以下のようにstaffとemployeeのインターフェースとしてUserInterfaceを定義できます。
module InterfaceTypes
module UserInterface
include InterfaceTypes::BaseInterface
description 'user(staff and employee) interface'
field :name, String, null: false
field :email, String, null: false
definition_methods do
def resolve_type(object, _context)
case object
when ::Employee
ObjectTypes::EmployeeType
when ::Staff
ObjectTypes::StaffType
else
raise "Unexpected UserType: #{object.inspect}"
end
end
end
end
end
まず、fieldで共通するプロパティ を定義します。その後resolve_typeでgraphqlでフロントにかえすオブジェクトによってEmployee Typeを返すかStaff Typeを分岐させています。
#クエリー定義
フロント側でのクエリー定義は以下のようになります。
query{
user {
id
name
email
__typename ## employee or staffがstringで渡される
... on Employee { ##employeeのみに存在するフィールド
role
... on Staff { ##staffのみに存在するフィールド
shopName
}
}
}
上記のように共通するフィールド以外は個別に定義できます。
__typenameはgraphql-rubyが自動で付けてくれるフィールドで、どちらのオブジェクトなのかを判別することができます。
#なぜInterfaceTypeを定義するか
返すObjectを意識しなくて良い
Interface TypeはGraphqlのクエリーの戻り値がstaffかemployeeかを意識したくない時に利用できます。例えば、Userのログインクエリーをフロントから叩きたい場合を考えてみましょう。
フロント側はユーザがemployeeかstaffかを意識せずにloginの同じクエリーを叩き、戻り値としてstaffかemployeeを返します。interface Typeを定義していないとログインユーザに返すオブジェクトによって別々にクエリーを定義する必要があります。しかし、Interface Typeを定義しておくと返り値としてUserInterfaceTypeと定義しておけば、どちらがログインしても対応することができます。
フロント側でもInterfaceTypeを定義できる
フロント側でtypescriptを使っている場合、graphql-codegenなどの自動型生成のプラグインを使うとUserInterfaceTypeを定義してくれます。これによって、フロント側でもログインユーザがstaffかempolyeeかを意識せずにemailやnameにアクセスできます。
また、typenameプロパティ によってstaffかemployeeかを判別して表示するページを切り替えたりできます。