概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: 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::ConnectionTypeGraphQL::Relay::NodeGraphQL::Relay::Edge
以上、私が学んできた経験をご紹介いたしました。この他にもGraphQLとRailsで何かよい手法を見つけたら、ぜひgistのコメントでお知らせください。
お読みいただきありがとうございました。