狠狠撸

狠狠撸Share a Scribd company logo
RailsでのRESTAPI開発の知見共有
株式会社UZUMAKICTO 今 佑介@kon_yu
銀座Rails#402021/12/17
自己紹介
今 佑介@kon_yu
株式会社UZUMAKICTO
株式会社NoftyCTO
趣味
オンライン英会話
平日毎日レアジョブ
深夜ラジオ
最近のイチオシはマヂラブのANN
クソアプリづくり
株式会社UZUMAKIについて
今日のスポンサー株式会社UZUMAKI
フリーランスのシニアエンジニア同士でチームを組んで仕事をしたら
仕事しやすんじゃないのという仮説のもと作られた会社
全員が調査?実装、仕様を決められれて、マネジメントスキルがあると仕事しやすいです
今年のクソアプリアドベントカレンダー
CO2センサーを使ったポモドーロタイマーを作って二酸化炭素濃度と生産性の相関を調べて
みよう!
础尘产颈别苍迟を使ってグラフ表示
目次
OpenAPIを利用する要件
各ライブラリの特徴と要件を満たすか
各ライブラリの比較
まとめ
OpenAPIを利用する要件
OpenAPIを使う要件を分解してみよう
メンテナブルなAPI設計書をつくりたい
モックサーバを使ってスキーマ駆動開発がしたい
TypeScriptのAPIアクセス部分を自動生成したい
メンテナブルなAPI設計書をつくりたい
API設計書を自動生成してドキュメント作成コストを下げたい
ソースコードとドキュメントの二重管理を避けたい
手動で作るとだんだんメンテナンスされなくなる
この規模のサービスでもドキュメントない/メンテされてないの!?なんてことある
よね?
参考:HowtouseOpenAPI3forAPIdeveloper
モックサーバを使ってスキーマ駆動開発がしたい
OpenAPIversion3に対応したメジャーなモックサーバ
Prism
使い方はとても簡単
> prism mock doc/openapi.yaml

レスポンスはopenapi.yamlの各メソッドのexampleを返してくれる
TypeScriptのAPIアクセス部分を自動生成したい
OpenAPIから各種言語のフロントエンドの自動生成
openapi-generator
Rubyも生成できるし、Swift,Kotlin,Dartも生成できる
TypeScriptの生成も様々なタイプができるAngular,Aurelia,Axios,Fetch,Inversify,
jQuery,Nestjs,Node,redux-query,Rxjs)
生成コマンド(axios対応のTypeScriptの場合)
> openapi-generator-cli generate -i 

./YOUR_PATH/openapi.yaml -g typescript-axios -o openapi/types 

--additional-properties=modelPropertyNaming=camelCase,supportsES6=true
自動生成されたコード例
async getShops() {

state.isLoading = true

const { data, error }: any = await new ApiV1ShopApi(

undefined,

$config.baseURL,

axios as AxiosInstance

)

.apiV1ShopsGet(1, 1000)

.catch((error) => {

return { error }

})

state.isLoading = false

if (error) {

state.error = error

return

}

state.shops = data.shops

},
各ライブラリの特徴と要件を満たすか
RailsでOpenAPIでググるとこの3つよく出る
Grape
committee-rails
rspec-openapi
※ 以降生成されるOpenAPIのファイルopenapi.yamlを
OpenAPI定義ファイル表記します
Grape
RailsのAPIモードが出る前まで、RailsでAPIといえばこれ
Grape:https://github.com/ruby-grape/grape
現在ではあまり人気のないDSL形式でAPIを作成できる
OpenAPIもRailsが動的に作成してくれる
Grapeのメリット、デメリット
メリット
grape-entity
レスポンスで返すモデルで表示させる属性を明示的にできる
OpenAPIのレスポンスのコンポーネントを自動で作成してくれる
grape-swagger
OpenAPI定義ファイルを動的に生成、さらにSwaggerUIもついてくるオールインワ
ン
先人の日本語情報が比較的多い
Grapeのメリット、デメリット
デメリット
DSLのため学習コストがかかる
スキーマを先に作って実装する駆動開発ができない
grape-swaggerがOpenAPIのv2にしか対応されておらずv3のブランチがずっと本体にさ
れていない
Grapeが要件を満たすか
メンテナブルなAPI設計書をつくりたい
SwaggerUIもv2なので機能が少ない
モックサーバを使ってスキーマ駆動開発がしたい
PrismはOpenAPIv2にも対応
TypeScriptのAPIアクセス部分を自動生成したい
未確認、v2なのでだんだんメンテされなくなるだろう
committee-rails
committeeをrailsで組み込みやすくしてくれるgem
committeeはOpenAPI定義ファイルは自分で書く必要があるが、
その定義通りのリクエスト?レスポンスをテストできる assert_schema_conform が便利
committee-rails:https://github.com/willnet/committee-rails
committee-railsのメリット、デメリット
メリット
純粋なスキーマ駆動開発ができる
Prismでモックを動かせるのでRails側とフロント側で同時並行に実装できる
他2つと違ってRailsで入出力だけ作るなどからめ手が不要
OpenAPIの定義どおりにRailsを実装できたかテストできる
committee-railsのメリット、デメリット
デメリット
OpenAPI定義ファイルを手作業で書かなければならい
OpenAPI定義ファイルだけ先に作るので他2つよりも実装に不安がある
特に初期は項目の追加など手戻りが頻発する状態でフロントの担当に共有していいもの
か悩ましい
OpenAPI定義ファイルを手作業で書かなければならい問題は
Spotlightstudioを使ってGUIで開発するとちょっと楽
committee-railsが要件を満たすか
メンテナブルなAPI設計書をつくりたい
OpenAPIも最新のv3に対応
モックサーバを使ってスキーマ駆動開発がしたい
PrismはOpenAPIv3に対応
TypeScriptのAPIアクセス部分を自動生成したい
正しいOpenAPIファイルなのできれいに生成する
rspec-openapi
Rspecのリクエストスペックを書けばOpenAPI定義ファイルを出力してくれる
テスト駆動開発大好きな人に朗報
rspec-openapi:https://github.com/k0kubun/rspec-openapi
rspec-openapiのメリット、デメリット
メリット
requestspecを書けば定義ファイルを作成してくれる
各APIのレスポンス例のexampleもテスト結果から作成してくれる(他2つにはない)
requestspec例
it"hogehoge"doの箇所がOpenAPIのdescriptionにこの分が反映されるのでrspecのいつも
の書き方をすると違和感が出やすいので注意
RSpec.describe 'Tables', type: :request do

describe '#index' do

it 'テーブルの配列を取得する' do

get '/tables', params: { page: '1', per: '10' }, headers: { authorization: 'k0kubun' }

expect(response.status).to eq(200)

end

it 'does not return tables if unauthorized' do

get '/tables'

expect(response.status).to eq(401)

end

end

# ...

end
生成されるOpenAPI定義ファイル例
openapi: 3.0.3

info:

title: rspec-openapi

paths:

"/tables":

get:

summary: index

tags:

- Table

parameters:

- name: page

in: query

schema:

type: integer

example: 1

- name: per

in: query

schema:

type: integer

example: 10

responses:

'200':

description: returns a list of tables

content:

application/json:

schema:

type: array

items:

type: object

properties:

id:

type: integer

name:
type: string

# ...
rspec-openapiのメリット、デメリット
デメリット
仕様を満たすOpenAPI定義ファイルを作るには、手動で対応するワークアラウンドが結
構ある
※ 今年ぶつかった問題なので急に具体的になります
rspec-openapiのワークアラウンド
ログイン必須の場合は各メソッドの定義に手動で追記する必要がある
devise_token_authで認証用のgemを利用した場合
client,access_token,uidをヘッダにつける必要あり
各メソッドにつける
paths:

"/api/v1/shops":

get:

security:

- client: []

access_token: []

uid: []
認証情報の定義
components:

securitySchemes:

client:

type: apiKey

description: ClientID

in: header

name: client

access_token:

type: apiKey

description: access-token

in: header

name: access-token

uid:

type: apiKey

description: uid

in: header

name: uid
rspec-openapiのワークアラウンド
レスポンスがヘッダだけの場合不要な情報が付与されるので削除する必
要がある
responses:

'204':

description: 商品カテゴリを更新

# これ以降を削除する

content:

'':

schema:

type: string

example: ''

responses:

'204':

description: 商品カテゴリを更新
rspec-openapiのワークアラウンド
specを実行時にIDがインクリメントされるとパラメタが追加されてし
まう
letでidを指定してレコードを保存して実行すると、idがインクリメントされてparametersの
内容が増えてしまうため自動生成時に同じ入力パラメータが重複してエラーになる
let(:category) { create(:category, name: '名前') }

IDを指定してやる
let(:category) { create(:category, id:1, name: '名前') }
rspec-openapiのワークアラウンド
コンポーネントを自分で作らなければならない
自動生成されたもの
responses:

'200':

description: 製品情報のリストを取得

content:

application/json:
schema:

properties:

products:
type: array

items:

type: object

properties:

id:

type: integer

name:
type: string
この状態だとコンポーネントを使わないとこのようなよろしくないクラス名になる
/**

*

* @export

* @interface InlineResponse2006

*/

export interface InlineResponse2006 {

/**

*

* @type {number}

* @memberof Shop

*/

id: number;

...

}
こういう意味の分かる名前のクラスを生成したい
/**

*

* @export

* @interface Shop

*/

export interface Shop {

/**

*

* @type {number}

* @memberof Shop

*/

id: number;

...

}
そのためOpenAPI定義ファイルに
responses:

'200':

description: お店のリスト取得する

content:

application/json:
schema:

properties:

products:
type: array

items:

"$ref": "#/components/schemas/Shop"
OpenAPI定義ファイルのコンポーネントの定義
components:

schemas:

Shop:

nullable: true

type: object

properties:

id:

type: integer

name:

type: string
レスポンスをコンポーネントに移動したあと
新しいメソッドを追加し、OpenAPI定義ファイルを自動生成すると
コンポーネントと、レスポンスのオブジェクトが2つ存在してしまう。
つまりここうなってしまう
responses:

'200':

description: お店のリストを取得

content:

application/json:
schema:

properties:

products:
type: array

items:

"$ref": "#/components/schemas/Shop"

# せっかくコンポーネントを作ったのに下記の余分なものがついてしまう

type: object

properties:

id:

type: integer

name:
type: string
OpenAPIgeneratorでTypeScriptを生成する際にエラーになるため都度消す必要がある。
※ SwaggerUIやPrismを動かす分には先に書いてあるコンポーネントを利用するため気になら
ない
rspec-openapiが要件を満たすか
メンテナブルなAPI設計書をつくりたい
最新のOpenAPIv3に対応するが、ワークアラウンドが多くAPIの本数が増える
とどんどん辛くなる
モックサーバを使ってスキーマ駆動開発がしたい
PrismはOpenAPIv3に対応
TypeScriptのAPIアクセス部分を自動生成したい
特にコンポーネント周りのワークアラウンドを実施しないと、自動で命名された
TypeScriptのクラスになってしまう
まとめ
今回の要件要件
メンテナブルなAPI設計書をつくりたい
モックサーバを使ってスキーマ駆動開発がしたい
TypeScripのAPIアクセス部分を自動生成したい
これらすべての要件を満たす のはcommittee-rails
まとめ
ただし開発の初期段階(1-3週間ぐらい)で、仕様が変わりやすい段階でcommittee-railsを使う
と
rspec-openapiで動作するRailsでOpenAPIを作成しつつ、
徐々に仕様が固まってAPIの本数が増えてきたらcommittee-railsに切り替えるのが良いのでな
いか
まとめ
実行時のOpenAPIの厳密さは右に行くほど適当な書き方だとエラーになる確率が上がる(体感)
SwaggerUI>Prism>OpenapiGenerator
そのためOpenAPIのファイルをコミットする場合は自動生成が通ってからコミットするのが
コツ

More Related Content

RailsでのREST API開発の知見共有