概要
前回 Sinatra + ActiveRecord を使った Web アプリを作成してみました
ついでなのでアプリを GraphQL 化してみました
GraphQL は REST や RPC に変わる API で JSON のようなクエリを使ってデータの CRUD が行えます
1 つのリクエストで複数のデータを取得するバッチリクエストが送れます
また RPC のように一つのパス (POST /graphql
) に対してアクセスします
環境
- macOS 10.14.5
- sqlite3 3.24.0
- Ruby 2.6.2p47
- activerecord 5.2.3
- sqlite3 1.4.1
- 1.9.6
事前準備
必要なアプリなどは前回のものをそのまま使います
gem のインストールや app.rb の作成は済ませておいてください
graphql-ruby の追加
Ruby で GraphQL を実装するためのライブラリがあるので使わせていただきます
vim Gemfile
gem "graphql"
gem "rack-contrib"
bundle install --path vendor
上記 2 つを追記したらインストールしましょう
タイプの追加
まずはタイプを追加します
GraphQL の重要な要素として「タイプ」「クエリ」「スキーマ」があります
タイプはデータの構造を定義します
mkdir -p graphql/types
vim graphql/types/base_object.rb
require 'graphql'
module Types
class BaseObject < GraphQL::Schema::Object
end
end
vim graphql/types/user.rb
require './graphql/types/base_object.rb'
class Types::User < Types::BaseObject
description 'Resembles a User Object Type'
field :id, ID, null: false
field :name, String, null: false
end
BaseObject
を使って新しいタイプを追加します
今回は name と id という field
を持つ User
タイプを追加しました
前回 sqlite3 に同じようなテーブルを作成しましたが基本的にはそれに合わせましょう
クエリの追加
クエリはリクエストに含まれるフィールドの情報を定義します
リクエストに users
というフィールドが含まれている場合に sqlite3 からデータを取得して先程のタイプにバインドしてデータを返却します
vim graphql/query.rb
require 'graphql'
require './graphql/types/user.rb'
require './models/user.rb'
class QueryType < GraphQL::Schema::Object
description "The query root of this schema"
field :users, [Types::User], null: false do
description 'Get all users'
end
def users
User.all
end
end
スキーマの追加
スキーマでは定義したクエリを登録します
今回は 1 つしかクエリを定義してないのでそれだけ登録します
vim graphql/schema.rb
require 'graphql'
require './graphql/query.rb'
class TestDBSchema < GraphQL::Schema
query QueryType
end
アプリ修正
ではアプリを修正しましょう
アプリではスキーマを使います
vim app.rb
require 'sinatra'
require 'rack/contrib'
require 'sinatra/json'
require "sinatra/activerecord"
require './graphql/schema.rb'
class DBTestApp < Sinatra::Base
register Sinatra::ActiveRecordExtension
set :database, {:adapter => "sqlite3", :database => "usersdb.sqlite3"}
use Rack::PostBodyContentTypeParser
post '/graphql' do
ret = TestDBSchema.execute(
params['query'],
variables: params['variables'],
context: { current_user: nil },
)
json ret
end
end
スキーマには execute
というメソッドが実装されておりこれに query
の内容を投げます
それ以外に variables
と context
が投げれますが今回は特に使っていません
動作確認
アプリを起動して動作確認しましょう
bundle exec rackup config.ru
curl でリクエストを投げみます
curl -X POST -H 'Content-Type: application/json' -d '{"query":"{users{name}}"}' localhost:9292/graphql
=> {"data":{"users":[{"name":"hawk"},{"name":"snowlog"}]}}
こんな感じで users テーブルからデータが取得できると思います
GraphQL の場合 data
というフィールド配下に結果が入ってきます
おまけ: Argument
例えば id:1
のユーザだけ取得したい場合には GraphQL では以下のようにリクエストします
curl -X POST -H 'Content-Type: application/json' -d '{"query":"{users(id:1){id name}}"}' localhost:9292/graphql
これに対応するにはクエリを以下のように修正します
vim graphql/query.rb
require 'graphql'
require './graphql/types/user.rb'
require './models/user.rb'
class QueryType < GraphQL::Schema::Object
description "The query root of this schema"
field :users, [Types::User], null: false do
argument :id, Integer, required: false
description 'Get all users'
end
def users(**arg)
if arg.empty?
User.all
else
[User.find(arg[:id])]
end
end
end
ポイントは argument
を使って指定可能な引数を定義する点です
あとは呼び出されるメソッド (users
) に可変長のハッシュ引数を受け取れるようにします
こうすることで引数なしと引数あり両方のクエリに対応できます
あとは sqlite3 に発行するとクエリを all
と find
に分けます
また返り値は配列である必要があるので find
の場合に配列にします
最後に
Sinatra で GraphQL に入門してみました
タイプ、クエリ、スキーマの基本的な使い方は抑えておきましょう
このあとはデータを保存したり更新するための mutation などを勉強すると良いかなと思います
0 件のコメント:
コメントを投稿