openapi2aspidaをOpenAPI 3.1.0に雑対応した
私たちはtobaso(https://www.tobaso.jp)という配信に特化したおたよりサービスを8月末にリリースしました。
そのサービスの開発の中で、バックエンドとのAPI仕様の共有のためOpenAPIのスキーマファイルを作成し、共有することになり、バックエンドで導入したツールの影響でこれまでのOpenAPI 3.0.3
が利用できず、3.1.0
を利用することになりました。
そこで、これまで利用してきたaspidaのツールチェーンであるopenapi2aspidaが3.0.3
までしか利用できなかったため、forkして3.1.0
に対応することにしました。
今回は他ライブラリの対応状況とforkしてもaspidaを継続利用することとした理由、OpenAPIの変更の中身と実際に投入した改造内容を紹介していきます。
OpenAPI 3.1.0
への対応状況
他ライブラリと共に見た まず、3.1.0
は3.0.x
のマイナーチェンジだとSemantic Versioningとしてみると見えますが、v3.1.0からOpenAPIはSemantic Versioningを放棄しているため互換性はありません。(代わりにJSONSchemaとの互換性は生まれています)
3.0.xに対応しているライブラリで思い浮かぶものというと
openapi-typescript
orval
openapi-typescript-code-generator
openapi-typescript-codegen
openapi2aspida
あたりでしょうか
この中で3.1.0に対応しているものは
-
openapi-typescript
- 最も有名なTypeScriptの型へ変換するライブラリだと思う
- かなり早い段階で対応されていた印象
- 対応時のPR: https://github.com/openapi-ts/openapi-typescript/pull/968
対応しているかよくわからないけれど、動作上対応していそうに見えるものは
-
orval
- 最近話題に上がることが多い(ような気がする)
- 対応状況が若干不明
- 内部的には
@apidevtools/swagger-parser
に依存していそうだけれど、しっかり読めてないのでよくわからない- 内部のparser部分が
@apidevtools/swagger-parser
、その後のロジック部ではopenapi3-ts
に依存してるっぽいとのこと
- 内部のparser部分が
- 3.1.0を入れてもエラーにはならないし、["string", "null"]を string | nullのunionに変換はしてくれるっぽいのでもしかしたら対応している
のかもしれない- null系は対応済みでその他若干bugが残っているが、基本3.1に対応もしてるとのこと
対応していないものは
-
openapi-typescript-code-generator
- issueとしては立っている: https://github.com/Himenon/openapi-typescript-code-generator/issues/18
- かなり思想として好きなライブラリなのでContributeしようとも考えたが、TypeScript ASTを完全に理解するところから始まるので断念
-
openapi-typescript-codegen
-
@hey-api/openapi-ts
への移行を推奨している - フォークして対応をしたもの(移行推奨先のもの)はある
-
-
openapi2aspida
- 専用のissueはないが、一変更であるconstへの対応はissueが立っている: https://github.com/aspida/openapi2aspida/issues/252
- 内部の型定義が依存してる
openapi3-ts
は対応済み(両対応されてる)
という感じで、まず3.1.0に対応しているライブラリを見つけることが難しいです。
OpenAPI 3.0.3 から 3.1.0への変更点
破壊的変更なのでChangelogを全部見るとさすがに大変なので、公式に出ている Migarating from OpenAPI 3.0 to 3.1.0
を基にピックアップしてみます。
一部Migarating from OpenAPI 3.0 to 3.1.0
からサンプルコードを引用しています
null
が許容される際の記述の変更
type
プロパティが複数の型を定義できるようになるとともに、nullable
プロパティが削除(not 非推奨)になり、type
にnull
が追加されました。
3.0.x
type: string
nullable: true
3.1.0
type:
- "string"
- "null"
minimum
/ maximum
プロパティに指定した値を含めるかどうかを指定する方法の変更
minumum
/ maximum
プロパティに指定した値を含めないかどうかを exclusiveMinimum
/ exclusiveMaximum
にて boolean
で指定していたが、exclusiveMinimum
/ exclusiveMaximum
に直接値を指定する形に変更になっています。
(このプロパティほとんど使ってないでしょって書かれている...)
3.0.x
minimum: 7
exclusiveMinimum: true
3.1.0
exclusiveMinimum: 7
example
ではなくexamples
を利用するように
単一の例示を行うためにexample
プロパティがありましたが、これがJSON Schemaには存在しないためexamples
に変更になっています
3.0.x
type: string
example: fedora
3.1.0
type: string
examples:
- fedora
- ubuntu
File Upload系の書き方の変更
これまでtype: string
とformat
プロパティの組み合わせで設定されていたものが、contentEncoding
/ contentMediaType
プロパティによって定義されるようになりました
3.0.x
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
orderId:
type: integer
fileName:
type: string
format: binary
3.1.0
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
orderId:
type: integer
fileName:
type: string
contentMediaType: application/octet-stream
他にもJSON Schemaに完全に互換性があるものになったため、追加されたプロパティなどが存在しますが、ここでは3.0.3からの変更分の一部だけをピックアップしました。
(const
プロパティを使用するなどはあり得そうですが、ここではいったん....)
なぜAspidaを継続利用することにしたのか
元々Aspidaを利用してたというのももちろんですが、APIのPathをメソッドチェーンによって表現する方式が捨てがたいというところが一番大きな理由です。
対応しているライブラリの中で、openapi-typescript
の場合はpath
のstring
から型を取り出す形ですが、若干Intellisenseの効きが悪くクエリパラメータなどが指定された状況でのpathを取得することのできる関数が用意されていないため採用を見送りました。
また、orval
は基本的にOpenAPI内のoperationId
を使ってメゾット名を自動生成しますが、このoperationId
がバックエンドで利用しているツールが自動生成するため、すぐに想像の付くものではなく、わかりやすい共通したルールを整備する時間的猶予があまりなかったため採用を見送っています。
実際に行った改造内容
弊社の環境では上であげた変更点のうち、null関連の記述変更とconstプロパティへの対応のみ影響があったため、それらの対応のみにしています。(typeがstring | numberなどの状態は対応していません。)
null関連の変更は事前にschemaのtypeがArrayかを確認し、nullが含まれる場合はnullbleのフラグを立てるのみ (.diff)
constについても元々あったenumを生成する仕組みを流用し、単一のvalueが存在するenumを生成するように(.diff)
といったかなり大雑把な対応にしています。
あまり大きな変更を入れると今後のopenapi2aspidaの変更に追従するのが大変になりそうということで、openapi-typesのV3.1の型定義に合わせて他にも対応を一部入れていますが、あまり大きな変更はなく、$ref
の解決方法を若干変えたり、schema.typeがArrayになる可能性が出たのでそのTypeGuardの追加などを行う程度にしています。
最後に
OpenAPI 3.1.0からAPI Clientをつくるライブラリを探したらなかなかなく、3.0.3に戻したという方も少なからず居るのではないでしょうか。
すべてのユースケースにこのようにforkと改造が合うとは思っていませんし、orval
やopenapi-typescript
、そのほかのライブラリが合う会社も多くあると思いますがこういう選択肢もあるよというご紹介でした。
また、バックエンドで導入したツールについても後日紹介できればと思います!
最後にはなりますが、aspida / openapi2aspida を開発されている solufaさんをはじめとしたContributorの皆様に深く感謝申し上げます。
Discussion