Open22

Rails + GraphQL w/ Auth

rikusen0335rikusen0335

Gemfileを記述

gem 'graphql'
gem 'graphiql-rails'
gem 'sprockets', '< 4'

sprocketsの4系だと動かないので、とりあえず4未満、3系の最新をいれるようにする

rikusen0335rikusen0335

config/application.rb# require "sprockets/railtie"のコメントアウトをはずす

rikusen0335rikusen0335

必要なGemをインストールする。bundlerがなければbundlerもインスコする

gem install graphql graphiql-rails bundler
rikusen0335rikusen0335
bundle install
rails generate graphql:install

これでだいたいのセットアップは終わり

rikusen0335rikusen0335

当たり前のことだけど、わかってなければ、sudoは使わないこと。対応できるならまあいいけど

rikusen0335rikusen0335

Queryの作成

GraphQLにはQueryという概念がある。まずはそれの作成から

rails g model Post title:string description:text
rake db:migrate
rikusen0335rikusen0335

テストデータを追加する。

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")

>は含めずコマンドを打ってね

rikusen0335rikusen0335

先程作成したモデルに沿うようにGraphQL用の型を追加する

rails g graphql:object Post id:ID! title:String! description:String!
rikusen0335rikusen0335

app/graphql/types/post_type.rbを見てみると、こんな感じで最初エグいことになってるかもしれない。

app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: true
    field :description, String, null: true
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
    field :id, ID, null: false
    field :title, String, null: false
    field :description, String, null: false
  end
end

理由はわからないけど、多分graphqlライブラリの最初からあるExample用のモデルとかぶるためだと思われる。これらはいまは必要ないので、修正する。

app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: false
    field :description, String, null: false
  end
end

こうなればおk

rikusen0335rikusen0335

Query Resolverを書く。ResolverっていうのはGraphQLでの概念として説明すると、QueryやMutationを行う際にそのオペレーションがなにを使うのかみたいなのを解決するやつ(多分)。Resolveは解決っていう意味です。

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

丸コピでおk

rikusen0335rikusen0335

config/routes.rbに以下を追加。

config/routes.rb
  if Rails.env.development?
    # add the url of your end-point to graphql_path.
    mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql" 
  end

一応、こちらの環境ではこんな感じに。

config/routes.rb
Rails.application.routes.draw do
  post "/graphql", to: "graphql#execute"
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html

  if Rails.env.development?
    # add the url of your end-point to graphql_path.
    mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
  end
end
rikusen0335rikusen0335

コメントとかは、必要でなければ消しちゃっても構いません。

rikusen0335rikusen0335

Mutationの作成

rails g graphql:mutation CreatePost

余談だが、ggenerateのショートハンド(短縮形)です

rikusen0335rikusen0335

上記のコマンドは以下の2つの事を行います。

  • (1)graphql/mutations/create_post.rbの作成。
  • (2)field :createPost, mutation: Mutations::CreatePostをgraphql/types/mutations_type.rbに追記する。

らしい。

参考記事に沿って、

app/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

こんな感じに書く。

rikusen0335rikusen0335

プロジェクトルートでrails sしてみて、http://localhost:3000/graphiqlにアクセスし、以下を入力

mutation A {
  createPost(
    input: {
      title: "title1"
      description: "description1"
    }
  ){
    post {
      id
      title 
      description
    }
  }
}

query B {
  posts {
    id
    title
  }
}

mutation Aを実行してみる。結果はこんな感じ

{
  "data": {
    "createPost": {
      "post": {
        "id": 3,
        "title": "title1",
        "description": "description1"
      }
    }
  }
}

idはシーケンシャルに変わるので3じゃない可能性もあります。

rikusen0335rikusen0335

更新用のMutationを作成する。

rails g graphql:mutation UpdatePost

そのあと、更新用のMutationの修正

app/graphql/mutations/update_post.rb
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
rikusen0335rikusen0335

また余談だが、今どきの(?)Railsはホットリロードに対応しているので、別のウィンドウないしタブでターミナルを開いたままコマンドを実行とかでそのままコードの更新が引き継がれる。

rikusen0335rikusen0335

そしたら、さっきと同じ感じでhttp://localhost:3000/graphiqlにアクセスして、以下を実行

mutation {
  updatePost(
    input:{
      id: 1
      title: "Updated"
      description: "Updated"
    }
  ){
    post {
      id
      title 
      description
    }
  }
}

結果はこう

{
  "data": {
    "updatePost": {
      "post": {
        "id": "1",
        "title": "Updated",
        "description": "Updated"
      }
    }
  }
}
rikusen0335rikusen0335

今回は、削除については割愛する。というのも、RailsでDBを操作する方法は定型化されていて、書くMutation Typeも似たようなものなので、どうしても書きたければ元記事を参照してください(面倒くさかった)。