LaravelでOpenAPIを利用する際のエコシステム
モチベーション
LaravelでBaaSなAPI群を作成する所存。
クライアントもLaravelでフロントエンドからアクセスすることは想定していない。
APIは社内の複数サービスから利用するが、汎用的なプラットフォームとして社外利用も想定している。
扱うAPIの特性としてはざっくり以下
- 完全ステートレスなAPIではなく、リクエストIDを発行してデータ蓄積からデータを加工・出力まで行う
- 蓄積するデータの項目数が多く数十となり日別実績データも扱うので結構なデータ量となる
データ項目が多く、相関チェックでのバリデーションもある - ベースとなる基本I/Fがあり、基本的な区分値は複数APIで利用する。扱うデータ種類によってAPIのエンドポイントをかえる
データ種類は30Over - 3年ごとにAPIのバージョンが変わり、バージョンの管理も必要となる
新しいバージョンのI/F公開からサービスインまでの時間的な制約が大きい(Draftは-2Month、ベータ版で利用できるようになるのは-1Monthぐらいと短期)
API仕様を公開共有するにあたり、標準仕様としてOpenAPIをベースで考える。
ただOpenAPI自体はバージョンによって記述方法が異なったり、関連するエコシステムが多数存在する為に、書き味が違ったり周辺ツール(主にLaravel)との整合性が出てくるので調査が必要。
調査を行う上で、以下の視点で掘り下げを行う
- Stubサーバー構築
I/F公開からベータ版利用までにクライアント側で利用を想定 - 仕様書とコードとの整合性担保
- テスティング
- 開発サイクルへの組み込みやすさ
モックサーバーはPrismが使えそう。
examplesを複数書いて、クライアント側から指定することができるのは良さそう。
リクエストIDを受け付けてコードを発行して、次回リクエスト時にコードをリクエストに指定してもらいたいけれど、コードの生成とかは流石に難しいよね?
fakerを使う場合は x-faker
をつけてあげると良い模様
V3の記述方法はこちらを参考にしたほうが良さそう
OpenAPIのv3でここまで記述できるぞという良い例
フロントでは扱う予定はないけれど参考まで
型定義は大変だよねーと思う。
protoファイルまでにしたら、型まで扱えるっぽいけれどOpenAPIはどうなんだろう?
gPRCだったり、GraphQLだといい感じにI/Fかけるかもだけれども、クライアント側の実装難易度もあがりそうなので、社外公開も考えるとちょっと難しそう
定義が大きくなるので、どっかで分割しないと厳しそうな印象はある。
バージョン管理する際にはファイルは分けたいよね。。
あーでもバージョン違いは別物として公開するかなー
yamlなので &
参照?できたはずだけれどもいい感じに共通部分は定義したいとは思う
goでの仕様書からコード生成する開発フローのイメージを完結にまとめてある
Laravelでクリーンアーキテクチャ採用しているので、コード生成うまくいくんだろうか?
OpenAPI Generatorを後で調べる
原因は、既にプロパティ名とexample値が同一のobject型のプロパティがあることでした。
どうやら、OpenAPI Generatorはモデル自動生成時に同一のものがある場合は、YAMLファイル上でより上に定義されたものをDRYに生成してくれるようです。
ちなみに、あえてDRYにしたくない場合は、$refを使ったり、exmaple値を異なるものにすることでも回避できます。
自動生成する際にも、書き方を調整しないとうまく期待通りにならないのかー
まあ自動生成だから仕方がないか
providersとして登録し、スキーマの定義を行えば自動でスキーマを出力してくれる便利なツールとなります。
クラス属性を記述してくタイプ
このライブラリではスキーマは別ファイルで定義しなければなりません。
自動でModelまで作成してくれるコマンドが用意されていますが、今回はそれを利用せずに実装します。
自動でModelまで定義されると、今回の利用イメージとはマッチしそうにないので、別フアイルで定義するのが良さそう。
リクエストクラスと、レスポンスクラスに定義情報をDSLっぽくかけるのかな?
試した感じ、コメント内でannotationではくattributeで書くのは未来を感じた
ただスキーマを専用のクラスに書かないといけなくてプロダクションコードとのマッピングが出来なくてイマイチ。
SchemaFactoryを継承する必要があるけれど、リクエストクラスではFormRequestを継承するけれど多重継承できない。。
インターフェースにしておいてくれれば良さそう。
また各プロパティーをFactoryのbuildで一箇所にまとめるのではなく、プロパティのattributeに書けると嬉しい
今後のバージョンアップに期待したい
周辺ツールをカテゴリにまとめてくれている
API Specificationを先に書くことによって、フロントエンドとサーバサイドの同時開発ができる、コードの雛形の生成ができる、APIドキュメントを別途用意・メンテする必要がなくなるというメリットが享受できます。
今回のイメージとしてはAPI Specificationを先に書くパターンがマッチしているけれど
yamlで書くのがいいか、コードを書きつつソースに近いところ(Laravel Open APIだとattribute)で書くのがいいのか?
ここが悩みのタネになる。どちらがプロジェクトにマッチするか?
コード自動生成は個人的に懐疑的だが興味がある。手直ししたあとにドキュメントへの反映ってどうなんでしょう?
OpenAPIドキュメントでAPI実装を検証する
こちらのアプローチも正解なような気がする。
これはプロダクションコード側のvalidatorかな
どっかでファイル読み込むからパフォーマンス的に気になるという記事を見た気がする。
Swagger v2用の記事だったのでOpenAPI Specification v3は下に書いている通り以下の記事を参照
最後のやつかな。
apidocみたいに外部ファイルではなく、Docコメントからアウトプットするパターン
L5-Swagger
以前はswagger-phpを使っていたけれど、ほとんど同じかな?
OpenAPIのバージョンが違うぐらい?
内部的にはswagger-phpを使っていた。
artisanコマンドから呼び出せるのと、APIの公開(Swagger-UI)をやってくれる感じだった。
Redocがいいなー
.envに L5_SWAGGER_GENERATE_ALWAYS
をtureにしてページをリロードするだけで最新の状態を確認できるのが、なかなか体験的に良かった。
Redocの方が見た目が好きだけれども、書いた内容確認するのにはSeagger-UIでもそんなに変わらない。OpenAPIの対応バージョン的にも問題ない
L5-Swaggerをベースにローカルの検証をして、外部公開用に出力したspecをCIでRedoc化してS3 Webホスティングする方法に落ち着いた
フルスクラッチで書くなら専用クライアントがあるといいよね。
書き味を試してみたい
ある程度書いてみてアウトプットして構文を覚えるのも良さそう。
linter欲しくなるよね
ファイルを分割してスキーマを管理する
L5-Swaggerを使って書くと必然にスキーマが別ファイルになるイメージだと思うけれど
直接書く場合は、ここらへんを自分で管理しないといけないということか
スキーマを結合する
これもできるのね。
最終的に1ファイルにまとめて公開という流れが良いよね。
generatorのLaravelサポート状況
ここらへんは実際試してみないとわからないよね。
あんまり記事がないから試している人も少なさそう。
標準的なLaravelの書き方なら使えるのかな?
こういう場合にフルスタックなフレームワークだとコード生成のツールはバージョン上がっていたりすると大変だよね。
クリーンアーキテクチャを採用しているプロジェクトだとレールを外れているので、やるまえから微妙っぽさが漂う。
やはりDDDのディレクトリ構成にマッチしなかった。
リクエストクラスとレスポンスクラスも自動生成までは作成されないっぽい
Controllerに直接validationが入る感じ。Laravelの対応バージョンも古い。
関連ツール一覧
バージョンと対応言語をまとめてくれているだけでもありがたい
なんか前見たときよりも種類が増えている気がw
新しいツールを探すときはウォッチしても良さそう
こちらの図が良かったのでメモ
開発サイクルに組み込めるか?というのが重要なことがわかる図
組み込んじゃってマッチしなくて(ツールが合わなくて)辛くなるという面もあるかもだけれども
3.0も理解してないのに3.1が既にでているけれど、変更点どかでまとまっているかな?
v3だとnullableが使えるのか
条件付き必須は検討中っぽい
他のドキュメントでは表現できるものはあるのかな?
しかし、残念ながら、今後は状況が変わるかもしれません。 discriminatorキーワードは現在策定中のOpenAPIの次期バージョンv3.1にて、非推奨となることが予定されているのです(OpenAPI v3.1 and JSON Schema 2019-09)。 v3.1はJSON Schemaの最新の仕様への準拠を目指しており、OpenAPI独自の拡張を撤廃する方向で仕様が検討されています(Update Schema Objects to JSON Schema Draft 2019-09)。 その一環として、discriminatorも非推奨となるようです。
ただ、discriminatorが使えなくなることによって、上述の問題が発生することは把握されており、非推奨化の見直しに関する議論も行われているようです(Deprecate discriminator?)。
discriminatorは今の所使わない方が良さそうかな。
タイムリーな記事
メリットと課題がまとめられていて良い
swagger-phpでattributeで書けるじゃん!
IDEのサポートも受けられるし、これでいいのでは?
こちらを採用した。
Enum項目をattribute定義する際にPHP8.1のEnumと親和性があるかなーと思ったけれど、現状ではEnum定義を手動で書く必要があった。
swagger-php側でenumのクラスを指定したらいい感じにvaluesを展開してくれないかな。。
swagger-phpというよりもPHPのAttributeの仕様的な制約がある感じだけれども。
PHP8.2になるとある程度救えるか?
OpenAPIのSpecのvalidator。プロダクション向けではなくテスト向けで使いたい
Laravel用のvalidator
内部的には別途 https://zenn.dev/link/comments/128ac4fb41c1c2 に記載したvalidatorがあってラップしている感じだと思う
コードスニペットを追加するコマンド
これではモックサーバとしての意味を為さないため、エンドポイントのパスを変更する必要があります。
これメンドイなー
prism側でbasePath指定出来るようにならないかなー?
議論はされている模様
basePathが指定できた
apisprout -p 8080 --add-server http://localhost:8080/api --validate-server api-docs.json
Prefer: example=hoge
でexmapleを変更出来る模様
未指定の場合は複数のexampleがランダムでレスポンスされるっぽい
Prefer: code=404
とかは使えないっぽい?
--validate-request
オプションでリクエストのチェックも行ってくれる
間違ったパラメータだと該当のスキーマを表示してくれる!便利!
Prefer: status=409
で大丈夫だった。
ここらへん微妙にツールで違うな
--validate-request
でこちらの書き方が不味いのか invalid memory address or nil pointer dereference
が発生しやすい。
あんまりメンテナンスされていないっぽいので安定してないのかもしれない
mockサーバー探索の旅。
$ref
があると駄目っぽい?
[FATAL] Error while mocking schema: Expected a value of type `undefined | {parameters,responses,requestBody}
って言われる
parameters:
- $ref: '#/components/parameters/HogeRequest'
があると駄目っぽい。
ReDocとかでは変換できているから問題ない記法っぽいけれど。。
大変申し訳ございません。
components側の記載内容に誤りがありました mm
正しくした所、問題なく動きました。
swagger-ui側のvalidationは単体のコマンドってあるのかな?
どちらもlinterとして使える
spectralの方がより細かく見てくれる印象。
unusedなものも見つけてくれたり、exampleも定義通りになっているか?確認してもらえる
apisproutみたいにyml若しくはjsonをhttp経由で指定できるとうれしいなー。
ローカルファイルの変更監視はあるけれど、l5-swaggerの自動生成モードはローカルファイルを書き換えてくれない。
swagger-php以外でattributeで定義できるcomposerがあった。
refを RefSchema::class
クラス文字列で参照できるのいいな。少し直感的になる
オブジェクトをnestした定義を書く時にどれくらい直感でかけるだろう?
swagger-phpだと new Property
しないといけなく(他に書き方ある?)、 Constant expression contains invalid operations
と怒られるのがなくなると嬉しいが。
オブジェクトをnestした定義を書く時にどれくらい直感でかけるだろう?
swagger-phpの場合
こちらの書き方がやはり正しいっぽい。
TOPレベルだけいい感じにmergeしてくれるっぽい。
validatorのライブラリ
上記ライブラリをラップしたライブラリ
php-openapi依存のライブラリ
どれを選べば良いんだw
laravel-openapi-validatorを軽く触ってみた。
既存のテストと遜色なくテストできそう。
あえてメソッドチェーンにしなくても良い。設定もconfigのみ。
リクエストのエラーになるパターンの場合もvalidationを無効化できる
ドキュメント読んだ感じではspectatorが一番やれることが多そう。
勝手にテストされるのではなく、
assertValidRequest
assertValidResponse
といったassert系のメソッドが生やされるので、意図がわかりやすくなりそう。
laravel-openapi-validatorを採用してみた。
リクエストこちらは設定後に勝手にリクエストとレスポンスが評価される。
コントローラーテストの品質の担保されている安心感がある。
残念な所として
勝手にAuthorizationを書き換えてしまう所。Overrideしてしまえば解決できたけれど、設定済みの場合は書き換えないでほしい
PR出してみた
mergeされました。
現状特に課題なしに使えていてgood!
APIのりナリオテストが出来るツールを探している
postmanはopenapi specが読み込めたが、そこから環境変数の埋め込みも含めて手直ししないといけない。
シナリオをflowsで定義してみたが、newmanでCI実行できなさそう
他のツールでも良いのがあるのだろうか?
自前で全部書くのも何なんだし。openapiで書いているので、postmanみたいに一度流し込んでから仕様を満たしたパターンでシナリオを書きたい
APIテストのアプローチ(種類)
どこを必要とするか?コントラクトテスト
laravel-openapi-validatorでPHPUnit(Controllerテスト/コンポーネントテスト)ではできているけれどE2Eでもその仕様にあっているか?確認したい。
パターンによってレスポンスが変わってくる、その際にレスポンスの形式がspec通りかみたい。
コンポーネントテスト
PHPUnitである程度カバーしているけれど、ユースケースやビジネスルールはモックしてしまうので、厳密なテストにはなってない。
featureテストでモック無しでやればいいけれど
シナリオテスト
流石にここは、E2Eでテストを行うべきか。
Controllerのテストと実際にAPI呼び出した場合のパラメータの扱いが微妙に違うケースがある。
レスポンスを引き継いで次のAPIを呼び出すということをやりたい
API testingのツールのトップ10
知らないものも結構ありそうだ
なんかAPI testingツールだけでもう一本スクラップが書けるような気がしてきたw
karateが良さそう。
凄い読みやすい。DSLの力か。
OpenAPI specと絡ませてコントラクトテストってできないかな?
DSLで自動生成してくれても嬉しいけれど、scenarioのリクエストが正しい範囲内でレスポンスか?というのを透過的にチェックしてくれれば良さそう。
まだ咀嚼しきれていないが、karateIDEでContract Testingをいい感じにできるよ!って記事。
やりたいことは網羅していそう。
openapiのspec読み込んでfeatureを自動生成してくれる。
逆にopenapiの定義に x-apimock-seed
という感じで、モックサーバーのデータを埋め込む所が熱い
こんなのもあるみたい
導入記事があった
annotationで書くのはつらそう
どこまで楽になるんだろう?
swagger-phpで書くのに慣れてしまったというのと、attributeでValidation Ruleの自動生成が実現できたので
あまり調べるモチベーションがなくなった