- 前の記事: Part1 GraphQLとは何か
- 次の記事: Part3 ベストプラクティス集
概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Building a GraphQL API in Rails - Part 2
- 原文更新日: 2017/01/30
- 著者: Wayne Chu
Part 1〜3の導入部は共通のため、2と3では省略しました。
RailsでGraphQL APIをつくる: Part 2 - Railsで基本的なAPIを書く(翻訳)
このブログ記事シリーズでは以下の3つのトピックについて扱います。
- GraphQLとは何か
- Railsで基本的なAPIを書く(本記事)
- ベストプラクティス集
Railsで基本的なAPIを書く
元ネタは私のslide#4です。
コードを書き始める前に最も大まかなユーザー要件を定義してから、順に実装を進めます。
トップレベルのユーザー要件
1. ユーザーはAPIを使うためのアカウントを必要としている。
2. ユーザーはAPIから自分のアカウント情報を取得できる。
要件1. 「ユーザーはAPIを使うためのアカウントを必要としている」
認証用gemのインストール
Railsアプリなら好みの認証用gemをインストールするだけなので、実に簡単です。DeviseかClearanceあたりがよいでしょう。本記事の例では新しめのgemということでClearanceを使いますが、どれでも同じなので好みの認証gemを使えます。
Clearanceの詳しいインストール方法は省略します(ドキュメントをご覧ください)。インストール後、以下の3つのコマンドを実行します。
$ rails generate clearance:install
$ rails generate clearance:routes
$ rake db:migrate
実に簡単です。Rubyコミュニティにひたすら感謝。🙏
APIトークンとUser
モデルを設定する
先に進む前に、API経由でユーザーを許可(authorize)するauth_tokenが必要です。今回はhas_secure_token
(Rails 5以降ではgemのインストールは不要です)でUser#api_token
を生成することにしました。generate後のUser
モデルは次のような感じになります。
class User < ActiveRecord::Base
include Clearance::User
has_secure_token :api_token
end
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
# email :string not null
# encrypted_password :string(128) not null
# confirmation_token :string(128)
# remember_token :string(128) not null
# api_token :string
要件1.を達成できました。おめでとうございます🎉
要件2. 「ユーザーはAPIから自分のアカウント情報を取得できる」
これからGraphQL APIを作成します。目標は、以下のようなクエリをやりとりできるAPIエンドポイントの作成です。
query {
me: viewer {
id
email
created_at
}
}
上のようなクエリを受信し、以下のようなJSONを返します。
{
"data": {
"me": {
"id": 1,
"email": "wayne.5540@gmail.com",
"created_at": 1477206061
}
}
}
必要なgemをインストールする
# コアとなるgem
gem 'graphql', '~> 1.0.0'
# GraphQL APIエクスプローラを作成できる便利なgem(必須ではない)
gem 'graphiql-rails'
# TDD
group :development, :test do
gem 'rspec-rails', '~> 3.5'
gem 'shoulda-matchers'
gem 'factory_girl_rails'
gem 'faker'
end
APIエンドポイントを作成する
Authorizationヘッダー{ "Authorization" => "Token #{user.api_token}" }
を追加したPOST /graphql?query=graphql-query
リクエストを受信できるようにします。
そのために、まずリクエストの処理に必要なルーティングを書きます。
# config/routes.rb
Rails.application.routes.draw do
post "graphql" => "graphqls#create"
end
続いてコントローラを書きます。
class GraphqlsController < ApplicationController
before_action :authenticate_by_api_token!
def create
query_string = params[:query]
query_variables = JSON.load(params[:variables]) || {}
context = { current_user: current_user }
result = Schema.execute(query_string, variables: query_variables, context: context)
render json: result
end
end
Schema.execute(query_string, variables: query_variables, context: context)
行はgraphql
gemの基本的な利用法を示しており、ここでクエリ文字列、グラフ変数、コンテキストをSchema
に渡します。Schema
はこの後で定義します。
GraphQLの型について
コントローラのアクションで行われることを理解するために、ここでGraphQLの型システムについて簡単に説明します。前述のクエリ例をもう一度引用します。
query {
me: viewer {
id
email
created_at
}
}
実際には、このクエリにSchema、QueryType、UserTypeという3つの異なるスコープが含まれています。
各々の型はAPI開発者が定義します。以下の画像で各スコープの概要をご覧ください。
Schemaスコープ: 最も外側のスコープであり、Query
およびMutation
という特殊型を含みます。Query
はHTTPのGET
リクエストに相当し、Mutation
はPOST
リクエストに相当すると考えるとわかりやすいでしょう。
QueryTypeスコープ:
UserTypeスコープ:
GraphQLで型を定義する
それでは型定義コードを書いてみましょう。GraphQL::ObjectType
APIを利用すれば比較的簡単です。
UserTypeスコープ: User
型を定義するために、各フィールドを定義します。レスポンスのカスタマイズにはresolve
ブロックを使います。カスタマイズしない場合はフィールド名のメソッドから取得します(例: フィールドidでuser.id
がレスポンスとして使われる)。
今回の例ではupdated_at
とcreated_at
をInteger
に設定したいので、resolve
ブロックのupdated_at
フィールドでobj.updated_at.to_i
を呼び出します。
# app/graph/types/user_type.rb
UserType = GraphQL::ObjectType.define do
name "User"
description "1人のユーザー"
field :id, types.Int
field :email, types.String
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
end
QueryTypeスコープ: UserTypeの場合と同じ要領でviewer
フィールドを作成し、type
にUserType
を指定します。
# app/graph/types/query_type.rb
QueryType = GraphQL::ObjectType.define do
name "Query"
description "このスキーマのクエリのルート(root)"
field :viewer do
type UserType
description "Current user"
resolve ->(obj, args, ctx) {
ctx[:current_user]
}
end
end
context[:current_user]
はSchema.execute
メソッドに渡され、Schema
の下のすべてのフィールドで使えるようになります。
なお、UserType自身もcurrent_user
を取得できるので、実際にはUserTypeにcurrent_user
を渡す必要はありません。ここでは、QueryType#viewer
のresolve
ブロック内でcurrent_user
が返すuserオブジェクトがUserTypeに渡される様子を例示するために、あえて使っています。
Schemaスコープ:
# app/graph/schema.rb
Schema = GraphQL::Schema.define do
query QueryType
end
仕上げの設定
あとは設定をいくつか追加すれば完了です。
グラフや型のautoload:
# config/application.rb
module GraphBlog
class Application < Rails::Application
# ...
config.autoload_paths << Rails.root.join('app', 'graph', 'types')
end
end
GraphiQLの設定(Graph APIのインタラクティブUI):
最初に、GraphiQLエンジンをRailsルーティングにマウントします。
# config/routes.rb
Rails.application.routes.draw do
# ...
mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
# ...
end
次にデフォルトのAuthorization
ヘッダーを設定します。
# config/initializers/graphiql.rb
GraphiQL::Rails.config.headers['Authorization'] = -> (context) {
"Token #{context.request.env[:clearance].current_user.try(:api_token)}"
}
あとはRailsサーバーを起動してhttp://localhost:3000/graphiqlを開けば、GraphQLで遊べるようになります。
サンプルクエリをいくつか掲載します。
query {
viewer {
id
email
}
}
query {
me: viewer {
...userFields
}
alsoIsMe: viewer {
...userFields
}
}
fragment userFields on User {
user_id: id
email
}
以上で、Railsプロジェクトで初めてのGraphQL APIエンドポイントが動くようになりました。🎉
今回のガイドではRailsで簡単なGraphQL APIを書くにとどめましたので、次回の「RailsでGraphQL APIをつくる: Part 3 - ベストプラクティス集」でベストプラクティスをいくつかご紹介します。
- 前の記事: Part1 GraphQLとは何か
- 次の記事: Part3 ベストプラクティス集