sinatra GraphQL導入(mongodbとの連携方法)


まえがき

友人より、REST APIの代替であるGraphQLというものを教えてもらった
どのようなものか気になったので試したことを備忘録として記事にします。
※sinatra導入の記事が少なかったので・・・

今回はsinatraにてGraphQLを利用するためにgraphql-rubyというgemを使用しています。
上記gemの公式ページに使用方法等書いてありますので、一読するのがよいでしょう。

上記gemを利用し、graphqlを通した、mongodbへのquery,insertまでをやっていきます。

参考

GraphQL公式ページ:https://graphql.org/
=>GraphQLの公式ページです。GraphQLがどんなものなのか、公式ページで学習することをおすすめします。

graphql-ruby公式ページ:https://graphql-ruby.org/
=>GraphQLをrubyで使用できるようにするgraphql-rubyの公式ページです。使用方法等細かく乗っているので参考にしてください。

sinatra導入参考:https://medium.com/hash32/graphql-server-with-sinatra-ruby-part-1-fdd664170715
=>sinatraへの導入で参考になった記事です。

sinatra GraphQL

今回の記事で最終的に作成するコードが下記となります。
https://github.com/Gen-Arch/graphql_test

mongodbの用意

本記事ではdockerで立てます。
上記、githubにdocker-compose.ymlがあるので、使用してください。

docker-compose up

mongo-expressも同時に入れるようにしてあるのでmongoの確認できます。
http://localhost:8081

gem install

gemの説明

今回導入するgemです。

install

gem file作成

mkdir graphql_test
bundle init

gemfileに追記

Gemfile
gem 'sinatra'
gem 'sinatra-contrib'
gem 'rack-contrib'
gem 'graphql'
gem 'mongoid'
gem 'bson_ext'

install

bundle install --path vendor/bundle

mongodbの設定

わかりやすいようにmongodb用のディレクトリを分けます。

mkdir models

mongodb接続用の設定ファイル

models/mongoid.yml
development:
  clients:
    default:
      database: test
      hosts:
        - localhost:27017

mongodb接続用のruby
※class名=table名と思ってください。

models/test.rb
require 'mongoid'
Mongoid.load!(File.join(__dir__, 'mongoid.yml'))

class Test
  include Mongoid::Document

  field :name,  type: String
  field :text,  type: String
end

graphqlの設定

こちらもgraphql用のディレクトリを分けます。

mkdir graphql

graphqlでは参照用のqueryと値追加のmutationを定義します。
ですが、まずはmongodbにて追加するデータのtypeを指定します。

type定義

ここもディレクトリを分けます。
typeを作成する際はGraphQL::Schema::Objectを継承します。

mkdir types
graphql/types/test.rb
module Types
  class Test < GraphQL::Schema::Object
    field :name, String, null: false
    field :text, String, null: false
  end
end

null: false => null値の追加を禁止

query定義

graphql/query.rb
require_relative 'types/test'
require 'models/test'

class QueryType < GraphQL::Schema::Object
  description 'The query root of this schema'

  field :all, [Types::Test], null: true do
    description 'Response All Data'
  end

  def all
    Test.all
  end
end

この定義で下記のように値が取れます。

{
  all {
    name
    text
  }
}

# response
#{
#  "data": {
#    "all": [
#      {
#        "name": "test",
#        "text": "test hoge"
#      }
#    ]
#  }
#}

mutation定義

ここもディレクトリを分けます。

mkdir mutation

値追加用のmutationを定義

graphql/mutation/create_test.rb
$: << File.expand_path('../../', __dir__)
require 'models/test.rb'

module Mutations
  class CreateTest < GraphQL::Schema::Mutation
    description 'Creates a Test'

    argument :name, String, required: true
    argument :text, String, required: true

    field :success, Boolean, null: false
    field :errors, [String], null: false

    def resolve(name: ,text:)
      test = Test.new(
        name: name,
        text: text,
      )

      if test.save
        {
          success: true,
          errors: []
        }
      else
        {
          success: false,
          errors: test.errors.full_messages
        }
      end
    end
  end
end

上記を呼び出すためのtypeを作成

graphql/mutation.rb
require_relative 'mutation/create_test'

class MutationType < GraphQL::Schema::Object
  field :createTest, mutation: Mutations::CreateTest
end

この定義で下記のように値が追加できます。

mutation {
  createTest(name: "test", text: "test hoge") {
    success
    errors
  }
}

# response
#{
#  "data": {
#    "createTest": {
#      "success": true,
#      "errors": []
#    }
#  }
#}

スキーマ定義

最後にqueryとmutationを呼ぶ用のスキーマを定義

graphql/appschema.rb
require 'graphql'
require_relative 'mutation'
require_relative 'query'

class AppSchema < GraphQL::Schema
  query QueryType          #=> query
  mutation MutationType    #=> insert
end

※sinatraではこれを使用します。

sinatra設定

sinatraの記述

app.rb
require 'sinatra'
require 'sinatra/json' #=> sinatraでjson形式でレスポンスする為
require_relative 'graphql/appschema'

# sinatraでjsonリクエストを受け取れるようにしています。
require 'rack/contrib'
use Rack::PostBodyContentTypeParser

# grapfqlのリクエスト処理
post '/graphql' do
  content_type :json
  puts params[:query]
  result = AppSchema.execute(
    params[:query],
    variables: params[:variables],
    context: { current_user: nil },
  )
  json result
end

sinatra起動

bundle exec ruby app.rb

graphql参照

graphqlを使用するために便利なツールがありますので使用をおすすめします。

GraphiQL.app

mutation

query

う〜ん、拡張性ありそうで好きになれそうですね。