GraphQL Rubyの紹介です。
Todoアプリ用の簡単なAPIサーバーの作成をチュートリアル形式でまとめます。
こちらで紹介するコードは全て以下リポジトリにあります。
https://github.com/kawamataryo/practice-graphql-ruby-2
GraphQL Ruby とは?
RubyでGraphQLのAPIサーバーを立てるためのGemです。
他のライブラリと異なりGraphQLのスキーマを直接書かず、Rubyのコードで実装とともにスキーマを定義していくという特徴があります。
Setup
ここからTodoアプリのAPIサーバーを作っていきます。
いつも通りrails new
でプロジェクトを作成します。不要なモジュールはスキップします。
rails new todo-app --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-storage --skip-action-cable --skip-javascript --skip-system-test --skip-webpack-install
rails s
で起動すると、おなじみの画面が表示されます。
cd todo-app
bundle exec rails s
GraphQL Ruby の導入
次にGraphQL Rubyを導入します。
Gemfileに以下を追記してください。
graphql
はGraphQL Ruby本体。
grphiql-rails
はプラウザ上で実行できるGraphQLのクライアントGraphiQL
を立てるためのものです。
gem 'graphql'
gem 'graphiql-rails', group: :development
bundle installした後で、generateコマンドを実行し関連ファイルを作成します。
bundle i
bundle exec rails generate graphql:install
ローカルサーバーを再起動して以下にアクセスします。
GraphiQLのコンソールが起動するので、以下クエリを実行しましょう。
query {
testField
}
無事Hello Worldが表示されましたか? これでGraphQL Rubyの導入は完了です。
※ 2020/04/19現在はgraphiqlで以下エラーが発生するようです
Asset `graphiql/rails/application.css` was not declared to be precompiled in production.
Declare links to your assets in `app/assets/config/manifest.js`
その場合は、config/environments/development.rb
のconfigの末尾に以下を追記しましょう。
config.assets.precompile += ['graphiql/rails/application.js', 'graphiql/rails/application.css']
タスク一覧取得のQuery
タスク一覧を取得するクエリを作成します。
Taskモデルをgenerateで作成し、さらにDBのマイグレーションを実行しましょう。
bundle exec rails g model Task title:string note:string completed:boolean
bundle exec rails db:migrate
続いてgraphql-rubyのgeneraterでTaskのTypeを作成します。
このTypeがレスポンスの型となります。
rails g graphql:object TaskType id:ID! title:String! note:String completed_at:Boolean
app/graphql/types/task_type.rb
が作成されます。
module Types
class TaskType < Types::BaseObject
field :id, ID, null: false
field :title, String, null: false
field :note, String, null: true
field :completed, Boolean, null: true
end
end
次にresolvers
ディレクトリを作りTasksのリゾルバを作成します。
resolve関数にてTask.allでを返しています。これがレスポンスとなります。
module Resolvers
class Tasks < GraphQL::Schema::Resolver
type [Types::TaskType], null: false
def resolve
Task.all
end
end
end
そしてquery_type.rbでtasks
fieldをリゾルバと紐づけて宣言します。
module Types
class QueryType < Types::BaseObject
field :tasks, resolver: Resolvers::Tasks
end
end
以上で最初のQueryの実装は完了です。
初期データがないと空配列が返るのでbundle exec rails c
でRailsコンソールを起動して、タスクを追加しておきます。
Task.create!(title: '買い物', note: 'たまご, 牛乳')
Task.create!(title: '掃除', note: 'リビング')
これで確認の準備が整いました。
にアクセスしてGraphiQLで以下クエリを実行してみましょう。
query {
tasks {
title
note
completed
}
}
Task.all
の取得結果が返ってきますね。graphql-rubyがActiveRecordをパースしてGraphQLのデータを返してくれています。
タスク追加のMutation
次にタスクの追加をできるようにしましょう。
まずmutaitions
ディレクトリにcreate_task.rb
を作ります。
module Mutations
class CreateTask < GraphQL::Schema::Mutation
argument(:title, String, required: true)
argument(:note, String, required: false)
type Types::TaskType
def resolve(title: nil, note: nil)
Task.create!(
title: title,
note: note
)
end
end
end
(本来はBaseMutationを継承するべきなのですが、簡略化のためGraphQL::Schema::Mutation
を直接継承させています。)
そしてmutation_type.rbで:create_task
fieldをCreateTaskミューテーションと紐づけて宣言します。
module Types
class MutationType < Types::BaseObject
field :create_task, mutation: Mutations::CreateTask, null: false
end
end
これだけでタスクの作成ができるはずです。
以下ミューテーションを実行しましょう。
mutation {
createTask(title: "勉強" note: "graphql-rubyのチュートリアル") {
id
title
note
completed
}
}
無事Taskが作成できましたね。
タスク更新のMutation
続いてタスクの更新のMutationを作っていきます。
created_task.rbをコピーしてmutationsディレクトリにupdate_task.rbを作成しましょう。
module Mutations
class UpdateTask < GraphQL::Schema::Mutation
argument(:id, ID, required: true)
argument(:completed, Boolean, required: true)
argument(:title, String, required: false)
argument(:note, String, required: false)
type Types::TaskType
def resolve(id: nil, completed: nil, title: nil, note: nil)
task = Task.find(id)
task.completed = completed
task.title = title if title
task.note = note if note
task.tap(&:save!)
end
end
end
そしてmutation_type.rbにupdate_taskを追加します。
module Types
class MutationType < Types::BaseObject
field :create_task, mutation: Mutations::CreateTask, null: false
field :update_task, mutation: Mutations::UpdateTask, null: false # こちらを追加
end
end
これで更新は完了です。
GraphiQLコンソールで以下ミューテーションを実行しましょう。
mutation {
update_task(id: 3 completed: true) {
id
title
note
completed
}
}
タスク削除のMutation
最後にタスクの削除を実装します。
delete_task.rbを作成します。
deleteのレスポンスはTaskのオブジェクトではなくdeleted: Boolean
としたかったので、
Resolver内でTypes::BaseObject
を継承したDeleteResultType
を定義し、typeで指定しています。
module Mutations
class DeleteTask < GraphQL::Schema::Mutation
argument(:id, ID, required: true)
class DeleteResultType < Types::BaseObject
field :deleted, Boolean, null: false
end
type DeleteResultType
def resolve(id: nil)
result = Task.find(id).destroy
{ deleted: result }
end
end
end
mutation_type.rbにもdlete_taskを追加します。
module Types
class MutationType < Types::BaseObject
field :create_task, mutation: Mutations::CreateTask, null: false
field :update_task, mutation: Mutations::UpdateTask, null: false
field :delete_task, mutation: Mutations::DeleteTask, null: false # これを追加
end
end
ここまで出来たら以下ミューテーションを実行してみましょう。
mutation {
deleteTask(id: 2) {
deleted
}
}
無事タスクが削除できましたね。
これでTODOアプリのCRUD処理の完成です。
Next step
今回紹介出来なかった認証や、ページネーションなどの実装方法はGraphQL公式のチュートリアルに記載されています。是非、次のステップとしてトライしてみてください。
終わりに
以上、GraphQL Rubyの紹介でした。
RailsでさくっとGraphQLのAPIサーバーを作れるのは良いですよね。
REST APIとの共存もできるので、新規API作成時の選択肢の一つになるのかなと思います。