概要
GraphQL Rubyのバージョン1.8~
以降で導入されたClass-based
の書き方を使用し、簡単なAPIを作成する方法を紹介します。
ネット上でClass-based
の書き方に関する記事が少なかったので、誰かの助けになれば幸いです。
GraphQL
,GraphQL Ruby
の基本概念等は以下で学習して頂けます。
- GraphQL入門 - 使いたくなるGraphQL - Qiita
- GraphQL Rubyに関して
- GraphQL公式
- How to GraphQL - The Fullstack Tutorial for GraphQL
GraphQL with Ruby on Rails
ユーザーが記事(post
)の取得、作成、更新、削除を行える簡単なapiをRuby on Rails
とGraphQL
を用いて作成します。
ソースコードはこちらから確認する事ができます。
Installation
まず、以下のコマンドでapi
モードでrailsアプリケーションを作成します。
$ rails new blog --api
Gemfile
にgraphql
とgraphql-rail
を加えます。
gem 'graphql'
gem 'graphiql-rails' #開発環境でgraphiqlを使用可能にします。こちらは必須ではありません。
通常のrailsアプリならこれだけでセットアップは完了ですが、apiモード
として作成する場合は、config/application.rb
に以下の文を加えます。
(開発環境でgraphiqlを使用する可能にする為です。こちらが必要ない場合は下記を追加する必要はありません。)
require "sprockets/railtie"
必要なgem等を追加したら、以下のコマンドを流しましょう。
2つめのコマンドを流すとapp/graphql
ディレクトリが作成され、GraphQL関連のファイルはこのディレクトリ下に配置されます。
$ bundle install
$ rails generate graphql:install
Queries
大雑把に説明するとGraphQL
では全てがType
(オブジェクトの設計書のようなもの)を持っており、そのType
に従い、Queries
を使用して、データの取得を行い、Mutations
を使用して、データの作成・更新を行います。
まず、データの取得を行うQueries
からみていきましょう。
Post
モデルを作成し、 rake db:migrate
を流します。
$ rails g model Post title:string description:text
$ rake db:migrate
Queryでデータを取得できる事を確認するため、 rails console
を使用して事前にデータを作成しましょう。
$ rails c
$ Post.create(title: "What is Ruby?", description:"Ruby is a programming language")
$ Post.create(title: "How to learn Ruby", description:"Read some books brah")
GraphQL
では各Type
を基にQuery
の実行を行なっていきます。
そこで、Post
のType
を定義するため、以下のコマンドを流しPost Type
を作成します。
$ rails g graphql:object Post id:ID! title:String! description:String!
以下が上記のコマンドで作成されたファイルの中身です。
module Types
class PostType < Types::BaseObject
field :id, ID, null: false
field :title, String, null: false
field :description, String, null: false
end
end
class~end
を使用してよりSytaxが以前と比べてよりわかりやすくなっています。
Class Based Syntax
について詳しく知りたい方は公式でわかりやすく説明されています。
Query Resolver
Typeは定義されましたが、サーバはこのタイプをどのように扱えば良いかわかりません。
そこで、実際にqueries
を実行するためにresolver
を作成していきます。
先ほどrails g graphql:install
を流した際に作成された app/graphql/types/query_type.rb
に以下のコードを追加する事で、全てのPost
の取得と特定のid
を持つPost
の取得ができます。
# app/graphql/types/query_type.rb
module Types
class QueryType < Types::BaseObject
field :posts, [Types::PostType], null: false
def posts
Post.all
end
field :post, Types::PostType, null: false do
argument :id, Int, required: false
end
def post(id:)
Post.find(id)
end
end
end
GraphiQLで確認
以下をroutes.rb
に追加します。
Rails.application.routes.draw do
if Rails.env.development?
# add the url of your end-point to graphql_path.
mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
end
post '/graphql', to: 'graphql#execute'
end
上記のif~end
を加える事でhttp://localhost:3000/graphiql
にアクセスし、実際に先ほど定義したquery
の確認を行う事ができます。
実際に rails s
をコンソールで流し、http://localhost:3000/graphiql
で以下のquery
を実行してみましょう。
# 全てのPost取得
{
posts {
id
title
description
}
}
############## 結果 #####################
{
"data": {
"posts": [
{
"id": 1,
"title": "title1",
"description": "description1"
},
{
"id": 2,
"title": "title2",
"description": "description2"
},
{
"id": 3,
"title": "title3",
"description": "description3"
},
]
}
}
# 特定のPostの取得
{
post(id:1) {
id
title
description
}
}
############## 結果 #####################
{
"data": {
"post": {
"id": 1,
"title": "title1",
"description": "description1"
}
}
}
Mutations
上記で簡単に説明したようにGraphQL
ではMutations
を使用してデータの作成・更新を行います。
以下のapp/graphql/types/mutation_type.rb
も最初にrails g graphql:install
を流した際に作成されています。
module Types
class MutationType < Types::BaseObject
# TODO: remove me
field :test_field, String, null: false,
description: "An example field added by the generator"
def test_field
"Hello World"
end
end
end
Mutation Create
Post
を作成する為に使用されるmutation
を作成する為に以下のコマンドを流します。
$ rails g graphql:mutation CreatePost
上記のコマンドは以下の2つの事を行います。
- (1)
graphql/mutations/create_post.rb
の作成。 - (2)
field :createPost, mutation: Mutations::CreatePost
をgraphql/types/mutations_type.rb
に追記する。
module Mutations
class CreatePost < GraphQL::Schema::RelayClassicMutation
# TODO: define return fields
# field :post, Types::PostType, null: false
# TODO: define arguments
# argument :name, String, required: true
# TODO: define resolve method
# def resolve(name:)
# { post: ... }
# end
end
end
# app/graphql/types/mutation_type.rb
module Types
class MutationType < Types::BaseObject
field :createPost, mutation: Mutations::CreatePost
end
end
graphql/mutations/create_post.rb
に書かれている TODOs
に従い、field
やargument
を定義して、Post
の作成が行えるよう、以下のように編集していきます。
:graphql/mutations/create_post.rb
module Mutations
class CreatePost < GraphQL::Schema::RelayClassicMutation
graphql_name 'CreatePost'
field :post, Types::PostType, null: true
field :result, Boolean, null: true
argument :title, String, required: false
argument :description, String, required: false
def resolve(**args)
post = Post.create(title: args[:title], description: args[:description])
{
post: post,
result: post.errors.blank?
}
end
end
end
GraphiQLで確認
先ほどと同じくrails s
でサーバを立ち上げ GraphQL でPost
の作成ができるか試してみましょう。
mutation {
createPost(
input:{
title: "title1"
description: "description1"
}
){
post {
id
title
description
}
}
}
############## 結果 #####################
{
"data": {
"createPost": {
"post": {
"id": 15,
"title": "title1",
"description": "description1"
}
}
}
}
Mutation Update
Post
を更新する為に使用されるmutation
を作成する為に以下のコマンドを流します
$ rails g graphql:mutation UpdatePost
自動作成された /graphql/mutations/update_post.rb
を以下のように変更しましょう。
/graphql/mutations/create_post.rb
と非常に似ており、違いはargument
にてid
を指定する箇所くらいです。
module Mutations
class UpdatePost < GraphQL::Schema::RelayClassicMutation
graphql_name 'UpdatePost'
field :post, Types::PostType, null: true
field :result, Boolean, null: true
argument :id, ID, required: true
argument :title, String, required: false
argument :description, String, required: false
def resolve(**args)
post = Post.find(args[:id])
post.update(title: args[:title], description: args[:description])
{
post: post,
result: post.errors.blank?
}
end
end
end
GraphiQLで確認
rails s
でサーバを立ち上げ GraphQL でPost
の更新ができるか試してみましょう。
mutation {
updatePost(
input:{
id: 1
title: "Updated"
description: "UPdated"
}
){
post {
id
title
description
}
}
}
############## 結果 #####################
{
"data": {
"updatePost": {
"post": {
"id": 1,
"title": "Updated",
"description": "UPdated"
}
}
}
}
Mutation Delete
作成、更新のmutations
を作成したのとほぼ同じ工程で削除の為のmutation
を作成します。
以下のコマンドを流します。
$ rails g graphql:mutation DeletePost
今回は削除の為に指定するid
のみをargument
にセットします。
module Mutations
class DeletePost < GraphQL::Schema::RelayClassicMutation
graphql_name 'DeletePost'
field :post, Types::PostType, null: true
field :result, Boolean, null: true
argument :id, ID, required: true
def resolve(**args)
post = Post.find(args[:id])
post.destroy
{
post: post,
result: post.errors.blank?
}
end
end
end
GraphiQLで確認
rails s
でサーバを立ち上げ GraphQL でPost
の削除ができるか試してみましょう。
mutation {
deletePost(
input:{
id: 1
}
){
post {
id
title
description
}
}
}
############## 結果 #####################
{
"data": {
"posts": [
{
"id": 2,
"title": "How to learn Ruby"
},
{
"id": 3,
"title": "title1"
}]
}
}
Connection fields(1)
GraphQL
ではtypes
,queries
,mutations
の働きがわかればアソーシエーションも簡単に対処できます。
実際に各ポストが持つコメントを取得できるようにしてみましょう。
まず、Comment
モデルを作成し、Post
と 多対1(has_many/belongs_to)
の関係をもたせます。
また、ここで確認の為のデータを作成しておきましょう。
$ rails g model Comment content:string post:references
$ rake db:migrate
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post
end
# app/models/post.rb
class Post < ApplicationRecord
has_many :comments, dependent: :destroy
end
Post.new.tap do |post|
post.title = 'title'
post.description = 'description'
post.comments.build(content: 'comment1')
post.save!
end
$ rake db:seed
次に、以下のコマンドでComment type
を作成しましょう。
$ rails g graphql:object Comment id:ID! content:String!
上記のコマンドは以下のファイルを作成します。
module Types
class CommentType < Types::BaseObject
description 'Comment'
field :id, ID, null: false
field :content, String, null: false
end
end
そして、comments
というfield
をPost type
に追加しましょう。
module Types
class PostType < Types::BaseObject
description 'Post'
field :id, Int, null: false
field :title, String, null: false
field :description, String, null: false
field :comments, [Types::CommentType], null: false
end
end
GraphiQLで確認
rails s
でサーバを立ち上げ GraphQL でPost
のcomments
が取得できるか試してみましょう。
{
posts {
id
title
comments {
id
content
}
}
}
############## 結果 #####################
{
"data": {
"posts": [
{
"id": "1",
"title": "title",
"comments": [
{
"id": "1",
"content": "comment1"
}
]
}
]
}
}
Connection fields(2)
Comment
を作成できるようにしてみましょう。
以下のコマンドを流して/graphql/types/mutation_type.rb
を作成します。
$ rails g graphql:mutation CreateComment
次のfield :post, Types::PostType, null: false
をapp/graphql/types/comment_type.rb
に追加しましょう。
module Types
class CommentType < Types::BaseObject
description 'Comment'
field :id, ID, null: false
field :content, String, null: false
field :post, Types::PostType, null: false
end
end
そして、 app/graphql/mutations/create_comment.rb
を以下のように変更します。
module Mutations
class CreateComment < GraphQL::Schema::RelayClassicMutation
graphql_name 'CreateComment'
field :comment, Types::CommentType, null: true
field :result, Boolean, null: true
argument :post_id, ID, required: true
argument :content, String, required: true
def resolve(**args)
post = Post.find(args[:post_id])
comment = post.comments.build(content: args[:content])
comment.save
{
comment: comment,
result: post.errors.blank?
}
end
end
end
GraphiQLで確認
rails s
でサーバを立ち上げ GraphQL でPost
のcomments
が作成できるか試してみましょう。
mutation {
createComment(
input:{
postId: 1
content: "NEW COMMENT"
}
){
comment {
id
content
post {
id
title
comments {
id
content
}
}
}
}
}
############## 結果 #####################
{
"data": {
"createComment": {
"comment": {
"id": "2",
"content": "NEW COMMENT",
"post": {
"id": "1",
"title": "title",
"comments": [
{
"id": "1",
"content": "comment1"
},
{
"id": "2",
"content": "NEW COMMENT"
}
]
}
}
}
}
}
Comment
の更新や削除を行いたい場合はPost
の更新、削除の方法を真似て作成してみてください。
参考
- GraphQL Ruby(Class-based API) - DEV Community 👩💻👨💻
- Building a GraphQL Server with Ruby Backend Tutorial
- A Guide to GraphQL in Plain English – freeCodeCamp.org
- GraphQL Ruby
- How to Implement a GraphQL API in Rails - via @codeship | via @codeship
- When you use graphiql-rails for your api only rails application
- When you use graphiql-rails for your api only rails application