OpenAPIで確認するFastAPIの正しいスキーマ定義
スキーマのプロパティで、必須か任意かのプロパティを含める場合があると思いますが、
厳密に区別すると、必須か任意かの2パターンではなく、以下の4パターンあります。
- プロパティが存在し、
null
ではない。 - プロパティが存在し、
null
でも良い。 - プロパティが存在しなくても良いが、存在するなら
null
ではない。 - プロパティが存在しなくても良く、
null
でも良い。
この4パターンの正しい定義ができているか確認するために、今回はOpenAPI Generatorを使って、フロントエンド(typescript)のコードを自動生成し、正しく定義できているか確認していきます。
OpenAPI Generatorとは
OpenAPI Generatorは、OpenAPI Specificationに則った仕様書からクライアントやサーバーのコードを生成できるツールです。
実はFastAPIは、OpenAPI Specificationに則った仕様書openapi.jsonを自動生成しています。このopenapi.jsonをOpenAPI Generatorに与えることで、クライアントやサーバーのコードを生成することができます。
つまり、FastAPIのスキーマ定義から自動でクライアントコードが生成することができるのです。
使用したバージョン
python == 3.11.4
fastapi == 0.101.1
pydantic == 2.2.1
正しいスキーマ定義
以下の3項目について記述していきます。
-
スキーマ定義
pydanticのBaseModel
を使ったスキーマの定義を記述します。ここで定義したスキーマをリクエストボディに設定します。 -
クライアントコード
OpenAPI Generatorで生成したコード(typescript)のリクエストボディの型(interface)を記述します。 -
仕様書(openapi.json)
FastAPIが生成する仕様書(openapi.json)のスキーマ定義を記述します。
null
ではない。
プロパティが存在し、スキーマ定義
class TestBody(BaseModel):
number: int
クライアントコード
export interface TestBody {
/**
*
* @type {number}
* @memberof TestBody
*/
number: number;
}
仕様書(openapi.json)
{
"TestBody": {
"properties": {
"number": {
"type": "integer",
"title": "Number"
}
},
"type": "object",
"required": ["number"],
"title": "TestBody"
}
}
null
でも良い。
プロパティが存在し、スキーマ定義
class TestBody(BaseModel):
number: int | None
クライアントコード
export interface TestBody {
/**
*
* @type {number}
* @memberof TestBody
*/
number: number | null;
}
仕様書(openapi.json)
{
"TestBody": {
"properties": {
"number": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Number"
}
},
"type": "object",
"required": ["number"],
"title": "TestBody"
}
}
null
ではない。
プロパティが存在しなくても良いが、存在するならスキーマ定義
class TestBody(BaseModel):
number: int = None
クライアントコード
export interface TestBody {
/**
*
* @type {number}
* @memberof TestBody
*/
number?: number;
}
仕様書(openapi.json)
{
"TestBody": {
"properties": {
"number": {
"type": "integer",
"title": "Number"
}
},
"type": "object",
"title": "TestBody"
}
}
null
でも良い。
プロパティが存在しなくても良く、スキーマ定義
class TestBody(BaseModel):
number: int | None = None
クライアントコード
export interface TestBody {
/**
*
* @type {number}
* @memberof TestBody
*/
number?: number | null;
}
仕様書(openapi.json)
{
"TestBody": {
"properties": {
"number": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Number"
}
},
"type": "object",
"title": "TestBody"
}
}
おまけ:OpenAPI Generatorのコード生成方法
今回、OpenAPI Generatorを使って、クライアントコード(typescript)を生成した方法について説明します。ここでは、dockerを使ってOpenAPI Generatorを起動しています。
openapi.jsonの取得
クライアントコードの生成には、openapi.jsonが必要です。FastAPIが自動生成しているopenapi.jsonを取得しましょう。FastAPIをhttp://localhost:8000/で起動していれば、http://localhost:8000/openapi.jsonから取得できます。
取得したopenapi.jsonをクライアントコードを生成したいディレクトリ(またはその上位のディレクトリ)に配置してください。
クライアントコードの生成
openapi.jsonのあるディレクトリで以下のコマンドを実行します。
docker run --rm \
-v $PWD:/local openapitools/openapi-generator-cli generate \
-i /local/openapi.json \
-g typescript-axios \
-o /local/{コードを配置するディレクトリ}
そうすると以下のようなファイルが指定したディレクトリに生成されます。
.
├── api.ts
├── base.ts
├── common.ts
├── configuration.ts
├── git_push.sh
└── index.ts
ここでは、typescript-axiosで生成しましたが、他に生成できるコードはたくさんあります。公式サイトで生成できるコードの一覧が確認できるので、自分のニーズに合う言語で生成してみてください。
Discussion