はじめに
本記事は、社内でGraphQL入門勉強会を実施した際の内容です。
本記事での対象は、
- graphql-ruby/graphiql-railsの設定
- Query(データ取得)
- Mutation(データ更新)
になります。
内部的な仕組みなど、細かい内容は出てきません。
とりあえず動かして雰囲気を掴むことを目的としています。
環境
バージョン | |
---|---|
Ruby | 2.6.0 |
Rails | 5.2.2 |
graphql-ruby | 1.9.3 |
graphiql-rails | 1.6.0 |
GraphQLとは
GraphQLは、2012年にFacebookによって開発されたデータクエリ言語および仕様です。
RESTベースのアーキテクチャに代わるものを提供します。
GraphQLはGitHubのAPI(GraphQL API v4
)でも採用されています。
GraphQL API v4 (https://developer.github.com/v4/)
GraphQLでは、クエリ言語を使用してデータの取得や更新を行います。
- query (データ取得系)
- mutation (データ更新系)
- subscription (イベント通知) ※本記事では対象外
Preparation
graphql-ruby
導入前のサンプルプロジェクトを用意しています。
本記事では、このサンプルプロジェクトにgraphql-ruby
を導入していきます。
git clone git@github.com:dkawabata/graphql-ruby-sample.git
cd graphql-ruby-sample
bundle install --path vendor/bundle
bundle exec rails db:migrate
bundle exec rails db:seed
サンプルプロジェクトは、users
テーブル/posts
テーブル/comments
テーブルの3テーブルで構成しています。
Setup
GraphQL Rubyのインストール
Gemfileを編集します。
+ gem 'graphql'
group :development do
+ gem 'graphiql-rails'
end
graphiql
というブラウザでクエリを実行して、GraphQLの動作確認ができるツールのRails版がgraphiql-rails
です。
GraphQLでの開発に必須ではありませんが、今回はgraphiql
で動作確認するため、追加しています。
Gemfileを編集したら、インストールします。
bundle install
bundle exec rails g graphql:install
APIモードでは無い場合、graphiql-rails
の設定(Gemfileへの追加や、以降で行うroutes.rb
の編集)は不要です。
rails g graphql:install
のタイミングで自動的に設定されますが、APIモードの場合はSkipされるようです。
Graphiqlの設定
routes.rbの編集
if Rails.env.development?
mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'
end
application.rbの編集
Rails APIモードでgraphiqlを利用するために、application.rb
のrequire "sprockets/railtie"
のコメントアウトを外します。
graphiql-rails
のREADMEにも記載されています。
If you're using Rails 5 in "API mode", you'll also need to add require "sprockets/railtie" to your application.rb.
- # require "sprockets/railtie"
+ require "sprockets/railtie"
サンプルクエリの実行
ここまでで一通りの準備は完了です。
rails s
でサーバを起動して、http://localhost:3000/graphiql
にアクセスしてみましょう。
graphiql
の画面が表示されます。
左側にクエリを入力して実行すると、右側に結果が表示されます。
下図のように、testField
というクエリを実行して、"Hello World!"
が表示されれば成功です
追記
Sprockets 4を使うと、Expected to find a manifest file in app/assets/config/manifest.js (Sprockets::Railtie::ManifestNeededError)
というエラーが発生します。
app/assets/config/manifest.js
を作成して、下記を記載すると、とりあえずエラーは回避できます。
//= link graphiql/rails/application.css
//= link graphiql/rails/application.js
Query
Queryはデータを取得するために定義します。
Queryを追加する前に、各モデルに対応したTypeを定義しましょう。
型の定義
user
/post
/comment
の各モデルに対応したTypeを定義します。
module Types
class UserType < Types::BaseObject
field :id, Int, null: false
field :name, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
field :posts, [Types::PostType], null: false
field :comments, [Types::CommentType], null: false
end
end
module Types
class PostType < Types::BaseObject
field :id, Int, null: false
field :user, Types::UserType, null: false
field :subject, String, null: false
field :body, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
field :comments, [Types::CommentType], null: false
end
end
module Types
class CommentType < Types::BaseObject
field :id, Int, null: false
field :post, Types::PostType, null: false
field :user, Types::UserType, null: false
field :body, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
graphql-ruby
の場合、各Typeに定義しているfield
と同名のメソッド/attributeがモデルに定義されている場合、自動的にデータを取得できます。
field
で型を指定しますが、標準で用意されている型には以下のようなものがあります。
- String
- Int
- Float
- Boolean
- ID
- ISO8601DateTime
※ []
で囲うと配列になります。
Queryの追加
それでは、実際にデータを取得するためのQueryを追加しましょう。
特定のid
を持つUser
を1件返すQueryと、全てのUser
を返すQueryを定義してみましょう。
QueryはTypes::QueryType
に定義を追加します。
module Types
class QueryType < Types::BaseObject
- # Add root-level fields here.
- # They will be entry points for queries on your schema.
- # TODO: remove me
- field :test_field, String, null: false,
- description: "An example field added by the generator"
- def test_field
- "Hello World!"
- end
+ field :user, Types::UserType, null: false do
+ description 'ユーザ情報を1件取得する'
+ argument :id, Int, required: true, description: 'ユーザID'
+ end
+ def user(id:)
+ User.find(id)
+ end
+ field :users, [Types::UserType], null: false, description: 'ユーザ情報を全件取得する'
+ def users
+ User.all
+ end
end
end
Queryの実行
実際にgraphiql
からQueryを実行してみましょう。
まずは、idを指定して1件取得してみます。
query {
user(id: 1) {
id
name
}
}
次に、登録されているuser全てを取得します。
query {
users {
id
name
posts {
id
subject
body
}
}
}
上記のQueryでは、各ユーザのid
とname
、そのユーザが投稿したPost
を全て取得しています。
Post
が不要な場合は、id
とname
だけ取得するということも可能です。
Queryを実行する側が、必要なデータを選択して取得することができる点は、GraphQLのいい点ですね!
Mutation
次はデータを登録するためのMutationを定義しましょう。
特定のPostにコメントを追加するMutationを定義します。
Mutationクラスの作成
module Mutations
class BaseMutation < GraphQL::Schema::RelayClassicMutation
end
end
module Mutations
class CreateComment < Mutations::BaseMutation
argument :user_id, Int, required: true
argument :post_id, Int, required: true
argument :body, String, required: true
field :comment, Types::CommentType, null: true
field :errors, [String], null: false
def resolve(user_id:, post_id:, body:)
comment = Comment.new(user_id: user_id, post_id: post_id, body: body)
if comment.save
{
comment: comment,
errors: []
}
else
{
comment: nil,
errors: comment.errors.full_messages
}
end
end
end
end
Mutation定義の追加
先ほど作成したCreateComment
を使えるようにするため、Mutationの定義を追加します。
module Types
class MutationType < Types::BaseObject
field :create_comment, mutation: Mutations::CreateComment
end
end
Mutationの実行
では、graphiql
からMutationを実行してみましょう。
mutation {
createComment(input: {
userId: 1
postId: 1
body: "コメント追加"
})
{
comment {
id
body
}
}
}
{
comment {
id
body
}
}
の部分は、登録したcomment
の内容を返却するための記述です。
まとめ
ざっくりですが、RailsのプロジェクトにgraphQLを導入して、Query
とMutation
を定義する部分を行いました。
一部古い情報もありますが、graphql-rubyのガイド( https://graphql-ruby.org/guides )を見ると、色々載っているので参考になります。
参考URL
https://github.com/rmosolgo/graphql-ruby
https://github.com/rmosolgo/graphiql-rails
https://graphql-ruby.org/guides