2024.10.21
お互い疑心暗鬼になりがちな、経営企画と事業部の壁 組織に「分断」が生まれる要因と打開策
openapi と swagger TS codegen(全1記事)
リンクをコピー
記事をブックマーク
菅原弘太郎氏(以下、菅原):それでは「OpenAPI GeneratorとTypeScriptによる型安全なスキーマ駆動開発」と題して、発表します。自己紹介します。2020年11月に、フロントエンドエンジニアとしてREADYFORに入社しました。岩手県在住で、フルリモートで勤務しています。ReactとTypeScriptが好きで、React Hook Formのメンバーなので、もしフォローしてくれる方がいれば、フォローしてください。
まずはフロントエンドとバックエンドの分離について話します。READYFORのサービスは、さまざまドメインが入り組んだ、1つのモノリスなサービスです。これがドメインごとに分離されたサービスを目指しています。
現状、フロントエンドとバックエンドの分離フェーズで、フロントエンドはTypeScriptとNext.jsを用いたSPA、バックエンドはREST APIという構成になっています。
私はちょうどフロントエンドとバックエンドの分離フェーズ中に入社しました。スキーマ駆動開発を実施している話は聞いていましたが、「はたして何だろう」という状態でした。
自分の3ヶ月前と同じような方がいるかもしれないので、今日は、スキーマ駆動開発とは何か、どんなメリットがあるのか、スキーマ駆動開発を実践する上でどんなツールを活用しているのか。3ヶ月、スキーマ駆動開発を実践してみた所感などを話せればと思います。これからスキーマ駆動開発を実践したい方の参考になれば幸いです。
まずはスキーマ駆動開発ついて説明します。SPAとAPI開発において、さまざまな悩みごとがあると思います。例えば、API仕様書のフォーマットが人によってバラバラだったり、仕様書と実装の乖離が発生したり。APIの実装を待たないとフロントの開発に着手できなかったり、あとはフロント側で想定していたリクエストやレスポンスが返ってこなかったりということがあるかと思います。
ここで、スキーマ駆動開発の出番です。まずスキーマ駆動開発のスキーマについて説明します。スキーマとは、ある定められた形式で記述したものです。Web APIの仕様定義と思ってもらって問題ありません。エンドポイントや、リクエストやレスポンスのデータ構造、値の型やフォーマットなどを定義したものを指します。
では、スキーマ駆動開発がどんなものかというと、Web APIの開発者と利用者、いわゆるバックエンドエンジニアとフロントエンドエンジニアでスキーマ設計を行い、スキーマを中心としたスキーマファーストな開発を行うことです。
これを実現するためには、さまざまなツールを活用する必要がありますが、スキーマが中心になって、ドキュメントの生成やコードの生成、モックサーバーの起動ができるようになります。
続いて、開発フローの違いについてです。スキーマ駆動開発以前では、APIが実装されてからフロントの実装に入るパターンがほとんどかと思います。これはフロント側でモックデータなどを用意すれば一応解決はできますがそもそも用意するのが面倒だったり手間がかかったりします。
これがスキーマ駆動開発を実践することでどうなるかというと、フロントとバックエンドでスキーマの設計を行って、コード生成ツールやモックサーバーを活用することで、APIの実装を待たずにフロント側で実装に入れます。
それでは、READYFORで活用しているツールの紹介です。まずはスキーマ定義から見ていきます。
知っている方も多いとは思いますが、READYFORではOpenAPIでスキーマを定義しています。Swaggerと聞けば知っている方がほとんどだと思うのですが、これはWeb APIを記述するためのフォーマットで、JSONやYAML形式での記述が可能です。READYFORではOpenAPI 3.0を使用しています。
イメージ的にはこのような感じになっています。左側がOpenAPIで定義したスキーマですが、「string型のpetIdのパラメータを、petsエンドポイントに対してGETリクエストすることによって、id・name・tagを含んだJSON形式で200レスポンスするよ」という意味合いになります。
続いてスキーマ定義の仕方ですが、ほとんどの場合はSwagger Editorを用いてスキーマを定義するのが一般的かと思います。これはどんなものかというと、左側でスキーマを編集しながら右側でプレビュー。いわゆる左側でスキーマを定義したものが、リアルタイムでAPIドキュメントとして可視化されて見れるエディタになっています。
また、ほかの選択肢としてStoplight Studioというものがあります。Swaggerとは違って、GUIです。そのため、OpenAPIのスキーマ定義についてあまり詳しくない人でも、ポチポチGUIをいじるだけでスキーマを定義できます。
Swagger Editorの画像は、このようなイメージです。
Stoplight Studioはこんな感じです。実際に触ってみたほうがいいと思います。
続いて、ドキュメントの自動生成ツールについての説明をします。これも知っている方がほとんどかと思いますが、Swagger UIを活用しています。先ほど見せたSwagger Editorのプレビュー側だと思ってもらって問題ありません。スキーマからドキュメントが生成されるので、信頼できます。
続いて、コード生成について見ていきたいと思います。READYFORでは、コード自動生成ツールにOpenAPI Generatorを使用しています。RubyやTypeScriptをサポートしていて、ほかにもJavaやPHPなどもサポートしています。
今回はDockerの例ですが、左の意味としては「openapi.ymlファイルをもとに、src/types/apiディレクトリに生成ファイルを出力する」というコマンドになります。ツリーを見てもらえばわかると思いますが、このように、APIクライアントやモデルが作成されます。
READYFORでは、ジェネレーターとして「typescript-fetch」を使用しています。
実際に生成されたファイルはこんな感じです。APIクライアントや、あとはリクエスト・レスポンスの型が生成されます。
続いて、スキーマから自動で型が生成されるので、リクエストやレスポンスの型にミスがない。いわゆる自前で定義すると、JSONのキーに間違いがあったりなどすると思いますが、そういうのがなくなって、型を信頼できます。
あとは、APIクライアントなどのコードが生成されるので、定型コードを記述するコストも減るんじゃないかなと思います。また、スキーマが変更された際もビルドエラーで修正箇所を把握できるので、おすすめです。
続いて、ハマりどころを紹介したいと思います。まず1点目としては、tagsを指定せずにスキーマを定義すると、APIクラス名が「DefaultApi」 という名前で生成されてしまいます。これを回避するには、ちゃんとtagsを指定しておいたほうがよいです。
続いてハマりどころ2なんですが、operationIdを指定せずにスキーマを定義すると、APIクラスのメソッド名やリクエストデータの型名が自動で生成されてしまいます。これもoperationIdを付与することによって、自分の意図した命名ができるようになります。
ハマりどころ3としては、components/schemasを使用せずにスキーマを定義すると、レスポンスデータの型名というのも自動で生成されるため、「InlineResponseXXX」という感じで、けっこうわかりづらい感じに生成されてしまいます。これも、ちゃんとcomponent/schemaで区切って生成することで、意図した型名で生成できるようになります。
続いて、モックサーバーの活用についてです。モックサーバーを活用することで、APIの実装を待たずにフロント側で開発に着手できるようになります。READYFORでは、Prismというモックサーバーを使用しています。先ほど紹介したStoplight Studioにも、モックサーバーとして統合されています。
コマンドとしてはこんな感じです。openapi.yaml、スキーマをもとにモックサーバーを起動できます。デフォルトでは、毎回同じデータを返すスタティックレスポンスを生成します。一応-dフラグというものがあり、--dynamicオプションを付与することで、毎回異なるデータをレスポンスすることも可能です。
Prismの強力な機能の1つとして、Preferヘッダーを渡すことでレスポンスの変更が可能になります。例えば、コードに対して404のようなステータスコードを付与してあげると、404でレスポンスできるようになるし、さらにスキーマで定義したexampleを指定することによって、そのexampleをレスポンスできるようになります。
あとはdynamicというのがあり、先ほどCLIで紹介した-dオプションと同じダイナミックレスポンスを有効化する機能も、ヘッダーに付与できるようになります。
あとはなにより、Cypressと相性が非常によいです。例えば「入力フォームのsubmitボタンを押して422エラーがレスポンスされたときに、エラーメッセージが表示されるか?」といったテストが簡単にできます。
先ほど紹介したPreferヘッダーを使用してあげることで、これを実現できます。Cypressではリクエストヘッダーをカスマイズできるので、APIにリクエストする前に、ヘッダーをカスタマイズしてリクエストを投げることによって、このエラーレスポンスを返せるようになる、という感じです。
続いて所感についてですが、まずよかったことから説明したいと思います。もうすでに説明していると思いますが、スキーマからAPIドキュメントが生成されるので信頼できます。これはスキーマがメンテナンスされている前提ですが、スキーマファーストに開発していれば信頼できるものになります。
また、TypeScriptのコードが生成されるため、型安全に開発できます。あとは、モックサーバーがかなり強力なので、効率的・テスタブルな開発が可能です。さらに、PR上でOpenAPIのスキーマをベースにバックエンドと認識を合わせられるので、非常に効率的でやりやすいです。
いいところばかりでなくつらいところもあって。これはスキーマ駆動開発というよりは、openapi-generator-cliの問題ですが。CLIによって生成されたコードで、型エラーが発生する場合があります。oneOfやallOfを使用すると、けっこう発生してしまいます。
あえてジェネレータで-gオプションを指定して「typescript-fetch」から「typescript-axios」に変更して試してみました。そうすると、正しいコードが生成できたりするので、改善の余地はありそうだと思っています。
続いて、生成される型が最善ではないところです。enumは、Enum型で出力されます。これは当たり前といえば当たり前なんですが、できればUnion型に出力するオプションが欲しいです。また、nullableなenumをスキーマで定義しても、出力される型としてはnullableになりません。
あとはdate/date-timeフォーマットで指定したものがDate型で出力されます。本来であればstring型にしてほしいところですが、typescript-fetchのみ、そういう感じになってしまいます。
一応--type-mappingsオプションで型をマッピングできるので、これでstring型にはできます。しかし、型が修正されてもAPIクラス側で型エラーが発生するので、ここはフォーマットを指定しない手段をとるしかないかなと思っています。
続いて、スキーマ駆動開発を実践した上で「こういうのを検討したほうがよさそう」ということを記載しています。
まず1点目が「OpenAPI Generatorによって生成されたAPIクライアントを使用するか、それとも型定義のみを使用するか?」です。生成されたAPIクライアントを使うのは非常に効率的ですが、当然それに依存することになります。フロントエンド 側の設計に対応できるかどうか、コードをちゃんと読んで、判断した上で使用するほうがよいかと思います。
続いて、ジェネレータをどれにするかの問題です。そのままtypescript-axiosを使っているならaxiosでもいいとは思いますが、型定義のみ使用する場合は、先に説明した出力される型定義との兼ね合いを見て決定したほうがよさそうだと感じています。
続いて、スキーマ駆動開発を実践してみて重要と感じたことです。これはもうスキーマファーストな上で最も重要だと思いますが、フロントエンドとバックエンドともに、スキーマからコードを生成しましょう、というところです。
仮にですが、フロント側のみスキーマからコードを生成しても、スキーマとAPIの実装に乖離が発生する可能性が高いです。最悪の場合、スキーマがメンテナンスされなくなってしまうので、避けたほうがよいでしょう。
いろいろ紹介しましたが、スキーマ駆動開発は、メリットがデメリットを遥かに上回ると思っています。興味をもった方は、ぜひ実践してみてください。ご清聴、ありがとうございました。
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.21
40代〜50代の管理職が「部下を承認する」のに苦戦するわけ 職場での「傷つき」をこじらせた世代に必要なこと
2024.11.20
成果が目立つ「攻めのタイプ」ばかり採用しがちな職場 「優秀な人材」を求める人がスルーしているもの
2024.11.20
「元エースの管理職」が若手営業を育てる時に陥りがちな罠 順調なチーム・苦戦するチームの違いから見る、育成のポイント
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.19
がんばっているのに伸び悩む営業・成果を出す営業の違い 『無敗営業』著者が教える、つい陥りがちな「思い込み」の罠
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.15
好きなことで起業、赤字を膨らませても引くに引けない理由 倒産リスクが一気に高まる、起業でありがちな失敗