rails-graphqlのメモ
railsに慣れてないので、色々調べながら作成したメモ
ローカルの環境を汚したくないので、Dockerで開発をする
この辺は前にチュートリアルを実行したときに触ったもの
FROM ruby:latest
RUN apt-get update && apt-get install -y nodejs imagemagick
RUN gem install rails
docker build ./ -t rails:test
set current=%~dp0
docker run -it --rm --name rails-test -v %current%:/home/rails -p 3000:3000 rails:test bash
exec.batを実行するとrailsがinstallされているコンテナ上で作業ができる。
なんで、WORKDIRを/home/railsにしていないんだろう。。
railsチュートリアルを基に、必要そうなものを入れていく
cd /home/rails
# graphqlのアプリケーションを作成
rails new graphql
# 必要だったかは忘れたけど、、
cd graphql
bundle install
# railsチュートリアルで必要ってなっていたのでとりあえず入れる。不要かも
rails wabpacker:install
# 起動確認
rails s -b 0.0.0.0
railsチュートリアルの2章らへん。
とりあえずユーザを作成する
rails generate scaffold User name:string email:string
rails db:migrate
railsチュートリアルに忠実に、Micropostも作成する
rails generate scaffold Micropost content:text user_id:integer
rails db:migrate
userとmicropostを関連づける
class User < ApplicationRecord
+ has_many :microposts
end
class Micropost < ApplicationRecord
+ belongs_to :user
end
SQLの実行状況がわかるように、ログに出力する
+ ActiveRecord::Base.logger = Logger.new("log/sql_#{Date.today}.log")
+ ActiveRecord::Base.logger.level = 0
これで下準備が完了
graphqlはこの記事を参考に進める。
+ gem 'graphql'
+ gem 'graphiql-rails'
bundle install
rails generate graphql:install
graphqlのTypeを作成する
bundle exec rails g graphql:object User
bundle exec rails g graphql:object Micropost
Graphql上でもUserとMicropostを結びつける
module Types
class UserType < Types::BaseObject
field :id, ID, null: false
field :name, String
field :email, String
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
+ field :microposts, [Types::MicropostType]
end
end
1行足すだけ。簡単。
Queryに追加する。とりあえず全件取得するUsersのみを定義する。
field :users, [Types::UserType], null: false
def users
User.all
end
これで実行してみる
rails s -b 0.0.0.0
http://localhost:3000/graphiql にアクセスすると、GprahiQlの画面が出るので、そこでクエリを実行する
{
users {
id
name
createdAt
updatedAt
microposts {
id
content
}
}
}
micropostの値が取れるようになった。
ログ的にはこんな感じになる。
ユーザーが2人登録している場合は、usersの値を全件取得後、micropostsの値を1件ずつ取得している。
つまり、ユーザがいるぶんだけSQLが発行するようになっている
User Load (34.1ms) SELECT "users".* FROM "users"
Micropost Load (1.6ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 1]]
Micropost Load (1.3ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]]
さらにMicropostにMicropostDetailという子供を付与する
User - Micropost - MicropostDetailという構造になる
Userが1に対してMicropostが多、Micropostに対してMicropostDetailが多となる
rails generate scaffold MicropostDetail content:text micropost_id:integer
rails db:migrate
bundle exec rails g graphql:object MicropostDetail
class Micropost < ApplicationRecord
belongs_to :user
+ has_many :micropost_details
end
class MicropostDetail < ApplicationRecord
+ belongs_to :micropost
end
module Types
class MicropostType < Types::BaseObject
field :id, ID, null: false
field :content, String
field :user_id, Integer
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
+ field :micropost_details, [Types::MicropostDetailType]
end
end
MicropostDetailにデータを追加して、Graphqlで実行してみる
{
users {
id
name
createdAt
updatedAt
microposts {
id
content
micropostDetails {
id
}
}
}
}
SQLは以下のものが発行される
User Load (34.1ms) SELECT "users".* FROM "users"
Micropost Load (1.6ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 1]]
Micropost Load (1.3ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]]
MicropostDetail Load (1.2ms) SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 1]]
MicropostDetail Load (1.0ms) SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 3]]
MicropostDetail Load (1.3ms) SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 2]]
Micropostに対してGrapqlのクエリを実行してみる
module Types
class MicropostType < Types::BaseObject
field :id, ID, null: false
field :content, String
field :user_id, Integer
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
field :micropost_details, [Types::MicropostDetailType]
+ field :user, Types::UserType
end
end
+ field :microposts, [Types::MicropostType], null: false
+ def microposts
+ Micropost.all
+ end
{
microposts {
id
user {
id
}
micropostDetails {
id
}
}
}
SQLの結果は以下の通り。
Micropostを取得後、UserとMicropostDetailsを別々にとりに行っている様子。
重くなりそう。。
Micropost Load (1.3ms) SELECT "microposts".* FROM "microposts"[0m
User Load (3.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
MicropostDetail Load (1.5ms) SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 1]]
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
MicropostDetail Load (2.1ms) SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 2]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
MicropostDetail Load (1.2ms) SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 3]]
もうちょっと複雑にしてみる。
{
microposts {
id
user {
id
microposts {
id
micropostDetails {
id
}
}
}
micropostDetails {
id
}
}
}
SQLのログは以下のようになった。
まずmicropostを取得する
次にmicropostの分だけ以下を実行する
usersを取得する
micropost_detailsを取得する
userからmicropostを取得する
userからmicropostを取得する
microposetからmicropost_detailを取得する
Micropost Load (1.5ms)SELECT "microposts".* FROM "microposts"[0m
User Load (1.3ms)SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
MicropostDetail Load (1.1ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 1]]
User Load (1.2ms)SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
MicropostDetail Load (1.1ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 2]]
CACHE User Load (0.0ms)SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
MicropostDetail Load (1.1ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 3]]
Micropost Load (1.8ms)SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 1]]
Micropost Load (1.2ms)SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]]
CACHE Micropost Load (0.0ms)SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 1]]
CACHE MicropostDetail Load (0.0ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 1]]
CACHE MicropostDetail Load (0.0ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 3]]
CACHE MicropostDetail Load (0.0ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 2]]
CACHE MicropostDetail Load (0.0ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 1]]
CACHE MicropostDetail Load (0.0ms)SELECT "micropost_details".* FROM "micropost_details" WHERE "micropost_details"."micropost_id" = ? [["micropost_id", 3]]
CACHEの文字があるので、一部キャッシュが効いているみたいですが、SQLのレイヤーっぽいですね。。
書いたコードは以下
次はgraphql-batchの検証を進めてみる
参考記事