概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Building a GraphQL API in Rails - Part 3
- 原文更新日: 2017/01/30
- 著者: Wayne Chu
RailsでGraphQL APIをつくる: Part 3 - ベストプラクティス集(翻訳)
このブログ記事シリーズでは以下の3つのトピックについて扱います。
- GraphQLとは何か
- Railsで基本的なAPIを書く
- ベストプラクティス集(本記事)
1. インターフェイス
2つの型が多数のフィールドを共有している状況を考えてみましょう。たとえば、2つの型のどちらにもid
、updated_at
、created_at
といったActiveRecordの基本的なフィールドがあるとします。こうした型を整然と扱うにはどうしたらよいでしょうか。1つの方法は、次の「インターフェイス」を使うことです。
リファクタリング前のUserType
とPostType
は次のようになっています。
# app/graph/types/user_type.rb
UserType = GraphQL::ObjectType.define do
name "User"
description "1人のユーザー"
field :id, types.Int
field :email, types.String
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
end
# app/graph/types/post_type.rb
PostType = GraphQL::ObjectType.define do
name "Post"
description "1件の投稿"
field :id, types.Int
field :title, types.String
field :content, types.String
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
end
そこで、active_record_interfaces.rb
ファイルを新たに作成して、id
、updated_at
、created_at
フィールドを定義します。
# app/graph/types/active_record_interfaces.rb
ActiveRecordInterface = GraphQL::InterfaceType.define do
name "ActiveRecord"
description "Active Recordインターフェイス"
field :id, types.Int
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
end
これで、UserType
とPostType
を次のようにすっきりと書き直せます。
# app/graph/types/user_type.rb
UserType = GraphQL::ObjectType.define do
interfaces [ActiveRecordInterface]
name "User"
description "1人のユーザー"
field :email, types.String
end
# app/graph/types/post_type.rb
PostType = GraphQL::ObjectType.define do
interfaces [ActiveRecordInterface]
name "Post"
description "1件の投稿"
field :title, types.String
field :content, types.String
end
2. Serviceオブジェクト
resolve
ブロックは各フィールドの振る舞いを扱いますが、resolve
ブロックにコードロジックを直接追加するとresolve
ブロックが不安定になり、テストも行いにくくなってしまいます。よりよい方法は、ロジックをresolve
ブロックから切り離してServiceオブジェクトに閉じ込めることです。なお、Serviceオブジェクトは抽象化のためのものなので、好みのデザインパターンがあればそれを使って抽象化してもかまいません。
以下のコード例をご覧ください。
CreatePostMutation = GraphQL::Relay::Mutation.define do
# ...
resolve -> (object, inputs, ctx) {
post = ctx[:current_user].posts.create(title: inputs[:title], content: inputs[:content])
{
post: post
}
}
end
上のコードは以下のように書き換えられます。
CreatePostMutation = GraphQL::Relay::Mutation.define do
# ...
resolve -> (object, inputs, ctx) {
Graph::CreatePostService.new(inputs, ctx).perform!
}
end
こうしておけばGraph::CreatePostService
だけをテストすれば済むので、テストやメンテナンスが楽になります。
3. Relay
モジュールを使う
graphql-ruby gemのRelay
名前空間にはいくつかの便利なモジュールがあります。APIをGitHub GraphQL APIのようにしたい場合や、Relayに統合するだけなら、これらのモジュールを使うことで多くの時間を節約できます。
以下のモジュールの使い心地を試してみるとよいでしょう。
GraphQL::Relay::ConnectionType
GraphQL::Relay::Node
GraphQL::Relay::Edge
以上、私が学んできた経験をご紹介いたしました。この他にもGraphQLとRailsで何かよい手法を見つけたら、ぜひgistのコメントでお知らせください。
お読みいただきありがとうございました。