コンテンツにスキップ

入力と出力でOpenAPIのスキーマを分けるかどうか

🌐 AI と人間による翻訳

この翻訳は、人間のガイドに基づいて AI によって作成されました。🤝

原文の意図を取り違えていたり、不自然な表現になっている可能性があります。🤖

AI LLM をより適切に誘導するのを手伝う ことで、この翻訳を改善できます。

英語版

Pydantic v2 のリリース以降、生成される OpenAPI は以前より少し正確で、より正しいものになりました。😎

実際には、場合によっては同じ Pydantic モデルに対して、入力用と出力用で OpenAPI に 2 つの JSON Schema が含まれることがあります。これは デフォルト値 の有無に依存します。

その動作と、必要に応じての変更方法を見ていきます。

入出力のPydanticモデル

次のようにデフォルト値を持つ Pydantic モデルがあるとします。

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None

# Code below omitted 👇
👀 Full file preview
from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

入力用モデル

このモデルを次のように入力として使うと:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item

# Code below omitted 👇
👀 Full file preview
from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

...description フィールドは 必須ではありません。デフォルト値が None だからです。

ドキュメントでの入力モデル

ドキュメントで確認すると、description フィールドには 赤いアスタリスク が付いておらず、必須としてはマークされていません:

出力用モデル

しかし同じモデルを次のように出力として使う場合:

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

...description にデフォルト値があるため、そのフィールドに何も返さなくても、その デフォルト値 が入ります。

出力のレスポンスデータ

ドキュメントから試してレスポンスを確認すると、コードでは一方の description フィールドに何も追加していないにもかかわらず、JSON レスポンスにはデフォルト値(null)が含まれています:

つまりそのフィールドには 常に値があります。値が None(JSON では null)になることがあるだけです。

したがって、この API を使うクライアントは値の有無を確認する必要がなく、フィールドが 常に存在する と仮定できます。場合によってはデフォルト値の None になるだけです。

これを OpenAPI で表現するには、そのフィールドを 必須 としてマークします。常に存在するためです。

このため、モデルの JSON Schema は、入力か出力か によって異なる場合があります:

  • 入力 では description必須ではない
  • 出力 では 必須(値は None、JSON では null の可能性あり)

ドキュメントでの出力モデル

ドキュメントで出力モデルを見ると、namedescription両方赤いアスタリスク必須 としてマークされています:

ドキュメントでの入力・出力モデル

さらに、OpenAPI に含まれる利用可能なスキーマ(JSON Schema)を確認すると、Item-InputItem-Output の 2 つがあることが分かります。

Item-Input では、description必須ではありません(赤いアスタリスクなし)。

一方、Item-Output では、description必須(赤いアスタリスクあり)です。

この Pydantic v2 の機能により、API ドキュメントはより 正確 になり、自動生成されたクライアントや SDK もより正確になります。これにより、より良い 開発者エクスペリエンス と一貫性が得られます。🎉

スキーマを分けない

一方で、入力と出力で同じスキーマ にしたい場合もあります。

主なユースケースは、すでに自動生成されたクライアントコードや SDK があり、まだそれらをすべて更新したくない場合です。いずれは更新したいとしても、今ではないかもしれません。

その場合は、FastAPI のパラメータ separate_input_output_schemas=False でこの機能を無効化できます。

情報

separate_input_output_schemas のサポートは FastAPI 0.102.0 で追加されました。🤓

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI(separate_input_output_schemas=False)


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

ドキュメントで入力・出力に同一スキーマを使用

これでモデルの入力と出力は単一のスキーマ、Item のみになり、description必須ではありません: