こんにちは、@きょーです!普段はデリッシュキッチン開発部のバックエンド中心で業務をしています。
はじめに
OpenAPI で API 仕様書を書く際、null
値を許容するプロパティの表現方法はバージョンによって異なります。たとえば、ユーザープロフィールのメールアドレスのように「値が存在しない(null
)」を許容したいケースはよくありますが、その書き方や推奨される方法は OpenAPI のバージョンごとに変化してきました。
この記事では、OpenAPI 3.0.0 と 3.1.0 それぞれでの null
許容プロパティの書き方や、その背景、なぜ仕様が変わったのか、どちらを使うべきかについて解説します。API 設計やスキーマ管理で迷ったときの参考になれば幸いです。
OpenAPI 3.0.0 での nullable
の基本的な使い方
OpenAPI 3.0.0 ではnullable: true
を使用することで、プロパティが null
値を許容することを表現できます。
components: schemas: User: type: object properties: id: type: integer format: int64 name: type: string email: type: string nullable: true # emailはnull値を許容
swagger で表示すると以下のようになります。
OpenAPI 3.1.0 での nullable
の表現方法
OpenAPI 3.1.0 では、nullable
キーワードが廃止され、代わりにtype
配列にnull
を含める形で表現するようになりました。
components: schemas: User: type: object properties: id: type: integer format: int64 name: type: string email: type: [string, "null"] # emailはnull値を許容
swagger で表示すると以下のようになります。
なぜ変更されたのか
OpenAPI 3.1.0 でのnullable
の廃止には、重要な背景があります。この変更はOpenAPI Specification の Proposalで詳しく議論され、主に以下のような理由が挙げられています。
nullable キーワードの曖昧さ
nullable: true
は「型指定されたスキーマにおいてnull
を許容する」という意図で導入されたが、OpenAPI 3.0 のドキュメントではその意味や他キーワードとの相互作用が十分に明確化されていなかった。nullable: false
(デフォルト値)についても、null
を明示的に禁止するのか、単に変更しないのかが明確でなかった。
JSON Schema との整合性
- OpenAPI の
nullable
は「許容範囲を拡張する」性質(expanding assertion)を持っているが、JSON Schema には存在しなく整合性がなかった。
これらの曖昧さや、JSON Schema への整合性の無さにより、バリデーターの挙動に一貫性がありませんでした。そのため OpenAPI 3.1.0 ではnullable
が廃止され、type にnull
を指定する方法に変更されました。
コード生成への影響
自分のチームでは OpenAPI で定義したスキーマを元にコードを自動生成しているプロジェクトがあります。oapi-codegenとopenapi-typescriptを用いて Go のモデル定義や TypeScript の型を自動生成しています。
OpenAPI のバージョンの違いによって変わる null
値の表現方法がツールによるコード生成にどう影響を与えるか確認してみようと思います。
TypeScript での生成コード
openapi-typescript を使用して TypeScript のコードを生成しようとした場合、以下のようになります。
// OpenAPI 3.0.0 での生成コード export interface components { schemas: { User: { id: number; name: string; email: string | null; }; }; } // OpenAPI 3.1.0 での生成コード export interface components { schemas: { User: { id: number; name: string; email: string | null; }; }; }
TypeScript の場合、生成されるコードに大きな違いはなく、nullalbe
として指定したプロパティは自動生成されたコードに反映されているのがわかるかと思います。
Go での生成コード
oapi-codegen を使用して Go のコードを生成しようとした場合、以下のようになります。
// OpenAPI 3.0.0 での生成コード type User struct { Email *string `json:"email"` Id int `json:"id"` Name string `json:"name"` } // OpenAPI 3.1.0 での生成コード // WARNING: You are using an OpenAPI 3.1.x specification, which is not yet supported by oapi-codegen (https://github.com/oapi-codegen/oapi-codegen/issues/373) and so some functionality may not be available. Until oapi-codegen supports OpenAPI 3.1, it is recommended to downgrade your spec to 3.0.x
Go の場合、OpenAPI 3.0.0 で生成されたコードは nullable
として指定したプロパティが反映されているのがわかります。一方 OpenAPI 3.1.0 ではコードの自動生成がされず 3.0.0 にバージョンを落として実行するように警告が出されます。これは oapi-codegen が依存している kin-openapi で OpenAPI 3.1 系にはまだ対応していないためこのような挙動になっているとのことでした。
- https://github.com/oapi-codegen/oapi-codegen/issues/373
- https://github.com/getkin/kin-openapi/issues/230
ここでは取り扱いませんがこちらで言及されている OpenAPI Overlay を使って OpenAPI 3.1.0 を 3.0.0 に downgrade する手法もあるみたいです。
まとめ
OpenAPI における null
値の表現方法について、仕様の違いとツールの対応状況を確認してきました。
仕様の違い
- OpenAPI 3.0.0 では
nullable: true
による独自拡張 - OpenAPI 3.1.0 では
type: [string, "null"]
による JSON Schema 準拠 - 3.1.0 での変更は、より標準的な方法での
null
の表現を可能に
- OpenAPI 3.0.0 では
実際のツール対応状況
- TypeScript (openapi-typescript)
- 3.0.0, 3.1.0 ともに問題なく動作
- Go (oapi-codegen)
- 3.0.0 では正常に動作
- 3.1.0 は現時点で未対応(kin-openapi の制限)
- 3.1.0 を使用する場合は OpenAPI Overlay による downgrade が必要
- TypeScript (openapi-typescript)
新規プロジェクトで新しく API スキーマを書く場合、JSON Schema との互換性がある 3.1.0 の方式が望ましいものの、実際の採用にあたってはツールの対応状況を十分に確認する必要がありそうです。もし既存プロジェクトで 3.1.0 に移行する場合でも使っているツールの対応状況を把握してから移行するのが良さそうです。