Open13

rails-graphqlのメモ

merutinmerutin

railsに慣れてないので、色々調べながら作成したメモ

ローカルの環境を汚したくないので、Dockerで開発をする
この辺は前にチュートリアルを実行したときに触ったもの

FROM ruby:latest

RUN apt-get update && apt-get install -y nodejs imagemagick
RUN gem install rails 
exec.bat
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
merutinmerutin

exec.batを実行するとrailsがinstallされているコンテナ上で作業ができる。
なんで、WORKDIRを/home/railsにしていないんだろう。。
railsチュートリアルを基に、必要そうなものを入れていく
https://railstutorial.jp/

cd /home/rails
# graphqlのアプリケーションを作成
rails new graphql
# 必要だったかは忘れたけど、、
cd graphql
bundle install
# railsチュートリアルで必要ってなっていたのでとりあえず入れる。不要かも
rails wabpacker:install
# 起動確認
rails s -b 0.0.0.0
merutinmerutin

userとmicropostを関連づける

app/modules/user.rb
class User < ApplicationRecord
+  has_many :microposts
end
app/models/micropost.rb
class Micropost < ApplicationRecord
+  belongs_to :user
end

SQLの実行状況がわかるように、ログに出力する

config/environments/development.rb
+ ActiveRecord::Base.logger = Logger.new("log/sql_#{Date.today}.log")
+ ActiveRecord::Base.logger.level = 0

これで下準備が完了

merutinmerutin

graphqlのTypeを作成する

bundle exec rails g graphql:object User
bundle exec rails g graphql:object Micropost

Graphql上でもUserとMicropostを結びつける

app/graphql/types/user_type.rb
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のみを定義する。

graphql/type/query_type.rb
    field :users, [Types::UserType], null: false
    def users
      User.all
    end
merutinmerutin

これで実行してみる

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]]
merutinmerutin

さらに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
app/models/micropost.rb
class Micropost < ApplicationRecord
	belongs_to :user
+	has_many :micropost_details
end
app/models/micropost_detail.rb
class MicropostDetail < ApplicationRecord
+	belongs_to :micropost
end
app/graphql/types/micropost_type.rb
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
merutinmerutin

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]]
merutinmerutin

Micropostに対してGrapqlのクエリを実行してみる

app/graphql/types/micropost_type.rb
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
graphql/type/query_type.rb
+    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]]


merutinmerutin

もうちょっと複雑にしてみる。

{
  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のレイヤーっぽいですね。。