GraphQL
GraphQLについて
一言でいうと
クライアントとデータベースの間に立つ、型付けされたスキーマのグラフ。
何ではない
ストレージやDBのクエリ言語ではない。
何が嬉しい
共通
型によるDXの向上。
クライアント観点
REST APIに比べてエンドポイントを減らせる。複数の呼び出しが一つの呼び出しにまとめられる。欲しいデータだけにアクセスできる柔軟性。
巷の情報だとHow to createとHow to useがごちゃ混ぜになってるので分けて考えた方が良さそう。
どう使うか
どう作るか
とりあえず自分の好みの環境用に手を動かしてみると良さそう。Railsで使ってみたいので下記をやってみる。 code:bash
# Create project
$ gem install rails
$ rails new rails-graphql-api -T --api -d postgresql
$ cd rails-graphql-api
# Install graphql tools
$ vim Gemfile
gem 'graphql'
$ bundle install
$ brew install --cask graphiql
$ brew install postgresql
$ brew services restart postgresql
# Create DB
$ bin/rails db:create
# Start server
$ bin/rails s
# Create models
$ bin/rails g graphql:object post comments:Comment
$ bin/rails g graphql:object comment
$ bin/rails db:migrate
$ vim app/models/post.rb
class Post > ApplicationRecord
has_many :comment # <- Add
end
# Add data to DB
$ bin/rails c
post = Post.create(title: 'test', body: 'body')
post.comments.create(body: 'comment 1')
post.comments.create(body: 'comment 2')
# Create ObjectType of graphql
$ bin/rails g graphql:object post comments:Comment
$ bin/rails g graphql:object comment
# Query with graphql
query {
post(id: "1") {
id
title
body
comments {
id
body
}
}
}
# Define Resolver
$ mkdir app/graphql/resolvers
$ vim app/graphql/resolvers/base_resolver.rb
module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
argument_class Types::BaseArgument
end
end
$ vim app/graphql/resolvers/post_resolver.rb
module Resolvers
class PostResolver < Resolvers::BaseResolver
description "Find a post by ID"
type Types::PostType, null: false
argument :id, ID, required: true
def resolve(id:)
Post.find(id)
end
end
end
$ vim app/graphql/types/query_type.rb
module Types
class QueryType < Types::BaseObject
# Add node(id: ID!) and nodes(ids: ID!!)` include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
field :post, resolver: Resolvers::PostResolver
end
end
# Create mutations
$ bin/rails g graphql:mutation create_post
$ vim app/graphql/types/mutation_post.rb
module Types
class MutationType < Types::BaseObject
field :create_post, mutation: Mutations::CreatePost
end
$ vim app/graphql/mutations/create_post.rb
module Mutations
class CreatePost < BaseMutation
field :post, Types::PostType, null: false
argument :body, String, required: true
argument :title, String, required: true
def resolve(**params)
post = Post.create!(params)
{ post: post }
end
end
end
# Query
mutation ($input: CreatePostInput!) {
createPost(input: $input) {
post {
title
body
}
}
}
{
"input": {
"title": "new title",
"body": "new body"
}
}
# Create Common InputObject
vim app/graphql/types/inputs/post_input_type.rb
module Types
module Inputs
class PostInputType < Types::BaseInputObject
argument :id, Int, required: true
argument :body, String, required: false
argument :title, String, required: false
end
end
end
# Create update mutation
$ bin/rails g graphql:mutation update_post
$ vim app/graphql/mutations/update_post.rb
module Mutations
class UpdatePost < BaseMutation
argument :params, Types::Inputs::PostInputType, required: true
field :post, Types::PostType, null: false
def resolve(params:)
post_params = params.to_h
post = Post.find(post_params.delete(:id))
post.update!(post_params.compact)
{post: post}
end
end
end
# Query to execute mutation
mutation ($input: UpdatePostInput!) {
updatePost(input: $input) {
post {
id
title
body
}
}
}
{
"input": {
"params": {
"id": 1,
"title": "updated title"
}
}
}