Ruby on Rails での GraphQL API 実装時のTips |Offers Tech Blog
はじめに
こんにちは!!
プロダクト開発人材の副業転職プラットフォーム Offers を運営する株式会社 overflow 普通のバックエンドエンジニアの takkun7171 でございます。
最近の近況ですが、12 月の amazon のブラックフライデーで特に買うものがなくて、カニを買いました。🦀
自宅でひっそりとカニ鍋忘年会したいと思います。
Rails の GraphQL API について
最近 GraphQL API を Rails で実装したんですけど、
基本的にはググったら情報がたくさんあったのでサクサクと実装できました。
ですが所々調べてもよくわからず、つまずいた箇所があり、
調べた過程で得た知見を Tips としてまとめたいと思います。
誰かのかゆいところに手が届けばいいなと思ってます。
なお GraphQL の gem を bundle install している前提です。
resolvers は作ったほうがいい
query_type.rb に最初は直接書いていたんですけど、
それだと確実に肥大化し、見通しが悪くなるので
やはり app/graphql/resolvers ディレクトリを作って、
ファイルを分けたほうが筋が良さそうです。
その際、以下のように
GraphiQL での呼び出し例をコメントに書いておくと
動作確認が捗ると思います。
module Resolvers
class Member < Resolvers::BaseResolver
type Types::MemberType, null: true
description "Find member by code"
argument :code, String, required: true
# GraphiQL での呼び出し例
=begin
{
member(code: "S6_t8_76Ua") {
id
name
company {
id
name
}
}
}
=end
def resolve(code:)
Member.find_by(code: code)
end
end
end
mutationの引数について
単純な mutation はググればわかるのですが、
例えば course テーブルと member テーブルが多対多の関係にあり、
course_members テーブルを作成したとします。
この時に mutation で course_members テーブルに複数データを
配列で指定して、一気にデータ作成したい時、どう書けばいいのか
よくわかりませんでした。
結論としては以下のように、app/graphql/input_types ディレクトリを作成し、
mutation の引数の型ファイルを作ったらうまく行きました。
# frozen_string_literal: true
module InputTypes
class CourseMember < Types::BaseInputObject
# ObjectTypesと名前がバッティングしないようにする
graphql_name 'CourseMemberAttributes'
argument :course_id, Integer, required: true
argument :member_id, Integer, required: true
end
end
module Mutations
class CreateCourseMember < BaseMutation
field :created_row_count, Integer, null: true
argument :list, [InputTypes::CourseMember], required: true
# GraphiQL での呼び出し例
=begin
mutation {
CreateCourseMember(
input:{
list: [
{
courseId: 5
memberId: 301
},
{
courseId: 5
memberId: 304
}
]
}
){
createdRowCount
}
}
=end
def resolve(**args)
list = args[:list]
course_members = []
# 指定したcourse_idやmember_idのデータが存在するかのチェックなどは割愛
list.each do |row|
course_member = CourseMember.create!(
course_id: row.course_id,
member_id: row.member_id
)
course_members << course_member
end
return {
created_row_count: course_members.length
}
end
end
end
authorized?とvisible?による認証・認可
GraphQL でデータを取得する際の認可は、
authorized?と visible?を使えば簡単に実装できます。
例えば company に紐付いている product というデータがあり、
ログインしてるユーザには自社の product しか見せたくない場合は
以下のように書きます。
もし他社の product を閲覧しようとした場合はデータが nil になります。
# frozen_string_literal: true
module Types
class ProductType < Types::BaseObject
def self.authorized?(object, context)
super
return false if context[:current_user].blank?
return false if object.company_id != context[:current_user].company_id
return true
end
field :id, ID, null: false
field :company_id, Integer, null: false
field :name, String, null: false
end
end
secret_config というデータがあり
管理者にしか見せたくない場合は以下の通りです。
管理者以外にはこのデータの存在そのものが隠されます。
# frozen_string_literal: true
module Types
class SecretConfigType < Types::BaseObject
def self.visible?(context)
super
# 管理者でしか見れないという条件
return context[:current_user]&.admin?
end
field :id, ID, null: false
field :val, String, null: false
end
end
上記は object 単位での認可でしたが、
field に対しても visible?、authorized?の設定が可能で
このカラムは特定のロールの人にしか見せたくない、、というユースケースにも対応できます。
こちらの記事が詳しいので、読めばわかると思います
他にも mutation や enum にも
visible?、authorized?が適用できるらしいです。
まとめ
今回は Rails での GraphQL API 実装時の Tips でした。
ここに書いたもの以外の項目、例えば N+1 問題の対処などは
ググれば沢山情報が出てくるので頑張ってください w
GraphQL API の実装は初めてで、他の言語ではどうなのかわからないんですけど
Rails での実装は便利な gem のおかげで、ラクな印象でした。
関連記事
副業転職の Offers 開発チームがお送りするテックブログです。【エンジニア積極採用中】カジュアル面談、副業からのトライアル etc 承っております💪 jobs.overflow.co.jp
Discussion