Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What is going on with this article?
@ryo2132

入門 GraphQL Ruby 〜基本のCRUD処理〜

More than 1 year has passed since last update.

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

スクリーンショット 2020-04-19 20.19.45.png

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 20.38.05.png

※ 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/environments/development.rb
  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が作成されます。

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でを返しています。これがレスポンスとなります。

app/graphql/resolvers/tasks.rb
module Resolvers
  class Tasks < GraphQL::Schema::Resolver
    type [Types::TaskType], null: false

    def resolve
      Task.all
    end
  end
end

そしてquery_type.rbでtasksfieldをリゾルバと紐づけて宣言します。

app/graphql/types/query_type.rb
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
  } 
}

スクリーンショット 2020-04-21 6.04.15.png

Task.allの取得結果が返ってきますね。graphql-rubyがActiveRecordをパースしてGraphQLのデータを返してくれています。

タスク追加のMutation

次にタスクの追加をできるようにしましょう。
まずmutaitionsディレクトリにcreate_task.rbを作ります。

app/graphql/mutations/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_taskfieldをCreateTaskミューテーションと紐づけて宣言します。

app/graphql/types/mutation_type.rb
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
  }
}

スクリーンショット 2020-04-21 6.05.50.png

無事Taskが作成できましたね。

タスク更新のMutation

続いてタスクの更新のMutationを作っていきます。
created_task.rbをコピーしてmutationsディレクトリにupdate_task.rbを作成しましょう。

app/graphql/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を追加します。

app/graphql/types/update_task.rb
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    
  }
}

スクリーンショット 2020-04-21 6.29.10.png

タスク削除のMutation

最後にタスクの削除を実装します。
delete_task.rbを作成します。
deleteのレスポンスはTaskのオブジェクトではなくdeleted: Booleanとしたかったので、
Resolver内でTypes::BaseObjectを継承したDeleteResultType を定義し、typeで指定しています。

app/graphql/mutations/delete_task.rb
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を追加します。

app/graphql/types/mutation_type.rb
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
  }
}

スクリーンショット 2020-04-21 7.05.50.png

無事タスクが削除できましたね。

これでTODOアプリのCRUD処理の完成です。

Next step

今回紹介出来なかった認証や、ページネーションなどの実装方法はGraphQL公式のチュートリアルに記載されています。是非、次のステップとしてトライしてみてください。

終わりに

以上、GraphQL Rubyの紹介でした。
RailsでさくっとGraphQLのAPIサーバーを作れるのは良いですよね。
REST APIとの共存もできるので、新規API作成時の選択肢の一つになるのかなと思います。

参考

7
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ryo2132
Frontend engineer / フルリモートワーク / 元消防士🚒 / 一児の父 / Ruby / Typescript / Vue.js / Firebase
admin-guild
「Webサービスの運営に必要なあらゆる知見」を共有できる場として作られた、運営者のためのコミュニティです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
7
Help us understand the problem. What is going on with this article?