コンテンツへスキップ

リクエスト例データを宣言する

アプリが受け取る可能性のあるデータの例を宣言できます。

いくつかの方法を以下に示します。

Pydanticモデルの追加JSONスキーマデータ

Pydanticモデルに対して、生成されたJSONスキーマに追加されるexamplesを宣言できます。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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

    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ]
        }
    }


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ]
        }
    }


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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

    class Config:
        schema_extra = {
            "examples": [
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ]
        }


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

    class Config:
        schema_extra = {
            "examples": [
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ]
        }


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

その追加情報は、そのモデルの出力JSONスキーマにそのまま追加され、APIドキュメントで使用されます。

Pydanticバージョン2では、Pydanticのドキュメント: Configurationで説明されているように、dictを受け取る属性model_configを使用します。

生成されたJSONスキーマに表示したい追加データ(examplesを含む)を含むdict"json_schema_extra"を設定できます。

Pydanticバージョン1では、Pydanticのドキュメント: Schema customizationで説明されているように、内部クラスConfigschema_extraを使用します。

生成されたJSONスキーマに表示したい追加データ(examplesを含む)を含むdictschema_extraを設定できます。

ヒント

同じ手法を使用してJSONスキーマを拡張し、独自のカスタム追加情報を追加できます。

たとえば、フロントエンドのユーザーインターフェースなどのメタデータを追加するために使用できます。

情報

OpenAPI 3.1.0(FastAPI 0.99.0以降で使用)は、JSONスキーマ標準の一部であるexamplesのサポートを追加しました。

それ以前は、単一の例を示すキーワードexampleのみをサポートしていました。これはOpenAPI 3.1.0でもまだサポートされていますが、非推奨であり、JSONスキーマ標準の一部ではありません。したがって、exampleexamplesに移行することが推奨されます。🤓

詳細については、このページの最後をご覧ください。

Fieldの追加引数

PydanticモデルでField()を使用する場合、追加のexamplesを宣言することもできます

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class Item(BaseModel):
    name: str = Field(examples=["Foo"])
    description: str | None = Field(default=None, examples=["A very nice Item"])
    price: float = Field(examples=[35.4])
    tax: float | None = Field(default=None, examples=[3.2])


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class Item(BaseModel):
    name: str = Field(examples=["Foo"])
    description: Union[str, None] = Field(default=None, examples=["A very nice Item"])
    price: float = Field(examples=[35.4])
    tax: Union[float, None] = Field(default=None, examples=[3.2])


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

JSONスキーマ - OpenAPIにおけるexamples

いずれかを使用する場合

  • Path()
  • Query()
  • Header()
  • Cookie()
  • Body()
  • Form()
  • File()

OpenAPI内のJSONスキーマに追加される追加情報を含むexamplesのグループを宣言することもできます。

examplesを含むBody

ここでは、Body()で期待されるデータの例を1つ含むexamplesを渡します。

from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results
from typing import Union

from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Item = Body(
        examples=[
            {
                "name": "Foo",
                "description": "A very nice Item",
                "price": 35.4,
                "tax": 3.2,
            }
        ],
    ),
):
    results = {"item_id": item_id, "item": item}
    return results

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from typing import Union

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Item = Body(
        examples=[
            {
                "name": "Foo",
                "description": "A very nice Item",
                "price": 35.4,
                "tax": 3.2,
            }
        ],
    ),
):
    results = {"item_id": item_id, "item": item}
    return results

ドキュメントUIの例

上記の方法のいずれかを使用すると、/docsでこのように表示されます。

複数のexamplesを含むBody

もちろん、複数のexamplesを渡すこともできます。

from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                },
                {
                    "name": "Bar",
                    "price": "35.4",
                },
                {
                    "name": "Baz",
                    "price": "thirty five point four",
                },
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                },
                {
                    "name": "Bar",
                    "price": "35.4",
                },
                {
                    "name": "Baz",
                    "price": "thirty five point four",
                },
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results
from typing import Union

from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                },
                {
                    "name": "Bar",
                    "price": "35.4",
                },
                {
                    "name": "Baz",
                    "price": "thirty five point four",
                },
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item = Body(
        examples=[
            {
                "name": "Foo",
                "description": "A very nice Item",
                "price": 35.4,
                "tax": 3.2,
            },
            {
                "name": "Bar",
                "price": "35.4",
            },
            {
                "name": "Baz",
                "price": "thirty five point four",
            },
        ],
    ),
):
    results = {"item_id": item_id, "item": item}
    return results

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from typing import Union

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item = Body(
        examples=[
            {
                "name": "Foo",
                "description": "A very nice Item",
                "price": 35.4,
                "tax": 3.2,
            },
            {
                "name": "Bar",
                "price": "35.4",
            },
            {
                "name": "Baz",
                "price": "thirty five point four",
            },
        ],
    ),
):
    results = {"item_id": item_id, "item": item}
    return results

これを行うと、例はそのボディデータの内部JSONスキーマの一部になります。

ただし、この記事の執筆時点では、ドキュメントUIを表示する役割を担うツールであるSwagger UIは、JSONスキーマのデータに複数の例を表示することをサポートしていません。しかし、回避策については以下をご覧ください。

OpenAPI固有のexamples

JSONスキーマexamplesをサポートする以前から、OpenAPIはexamplesと呼ばれる別のフィールドをサポートしていました。

このOpenAPI固有のexamplesは、OpenAPI仕様の別のセクションにあります。各JSONスキーマの内部ではなく、パス操作の詳細に含まれます。

そしてSwagger UIは、この特定のexamplesフィールドを以前からサポートしていました。したがって、これを使用してドキュメントUIでさまざまな例を表示できます。

このOpenAPI固有のフィールドexamplesの形式は、追加情報(リストではなく)を伴う複数の例を持つdictであり、これもOpenAPIに追加されます。

これは、OpenAPIに含まれる各JSONスキーマの内部ではなく、外部のパス操作に直接含まれます。

openapi_examplesパラメータの使用

FastAPIでOpenAPI固有のexamplesをパラメータopenapi_examplesで宣言できます。

  • Path()
  • Query()
  • Header()
  • Cookie()
  • Body()
  • Form()
  • File()

dictのキーは各例を識別し、各値は別のdictです。

examples内の各特定の例dictは、以下を含むことができます。

  • summary: 例の簡単な説明。
  • description: Markdownテキストを含むことができる長い説明。
  • value: これは実際に表示される例(例:dict)。
  • externalValue: valueの代替。例を指すURL。ただし、これはvalueほど多くのツールでサポートされていない可能性があります。

このように使用できます

from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Annotated[
        Item,
        Body(
            openapi_examples={
                "normal": {
                    "summary": "A normal example",
                    "description": "A **normal** item works correctly.",
                    "value": {
                        "name": "Foo",
                        "description": "A very nice Item",
                        "price": 35.4,
                        "tax": 3.2,
                    },
                },
                "converted": {
                    "summary": "An example with converted data",
                    "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
                    "value": {
                        "name": "Bar",
                        "price": "35.4",
                    },
                },
                "invalid": {
                    "summary": "Invalid data is rejected with an error",
                    "value": {
                        "name": "Baz",
                        "price": "thirty five point four",
                    },
                },
            },
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Annotated[
        Item,
        Body(
            openapi_examples={
                "normal": {
                    "summary": "A normal example",
                    "description": "A **normal** item works correctly.",
                    "value": {
                        "name": "Foo",
                        "description": "A very nice Item",
                        "price": 35.4,
                        "tax": 3.2,
                    },
                },
                "converted": {
                    "summary": "An example with converted data",
                    "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
                    "value": {
                        "name": "Bar",
                        "price": "35.4",
                    },
                },
                "invalid": {
                    "summary": "Invalid data is rejected with an error",
                    "value": {
                        "name": "Baz",
                        "price": "thirty five point four",
                    },
                },
            },
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results
from typing import Union

from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Annotated[
        Item,
        Body(
            openapi_examples={
                "normal": {
                    "summary": "A normal example",
                    "description": "A **normal** item works correctly.",
                    "value": {
                        "name": "Foo",
                        "description": "A very nice Item",
                        "price": 35.4,
                        "tax": 3.2,
                    },
                },
                "converted": {
                    "summary": "An example with converted data",
                    "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
                    "value": {
                        "name": "Bar",
                        "price": "35.4",
                    },
                },
                "invalid": {
                    "summary": "Invalid data is rejected with an error",
                    "value": {
                        "name": "Baz",
                        "price": "thirty five point four",
                    },
                },
            },
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


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


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item = Body(
        openapi_examples={
            "normal": {
                "summary": "A normal example",
                "description": "A **normal** item works correctly.",
                "value": {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                },
            },
            "converted": {
                "summary": "An example with converted data",
                "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
                "value": {
                    "name": "Bar",
                    "price": "35.4",
                },
            },
            "invalid": {
                "summary": "Invalid data is rejected with an error",
                "value": {
                    "name": "Baz",
                    "price": "thirty five point four",
                },
            },
        },
    ),
):
    results = {"item_id": item_id, "item": item}
    return results

ヒント

可能であれば`Annotated`バージョンを使用することをお勧めします。

from typing import Union

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


@app.put("/items/{item_id}")
async def update_item(
    *,
    item_id: int,
    item: Item = Body(
        openapi_examples={
            "normal": {
                "summary": "A normal example",
                "description": "A **normal** item works correctly.",
                "value": {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                },
            },
            "converted": {
                "summary": "An example with converted data",
                "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
                "value": {
                    "name": "Bar",
                    "price": "35.4",
                },
            },
            "invalid": {
                "summary": "Invalid data is rejected with an error",
                "value": {
                    "name": "Baz",
                    "price": "thirty five point four",
                },
            },
        },
    ),
):
    results = {"item_id": item_id, "item": item}
    return results

ドキュメントUIのOpenAPI例

Body()openapi_examplesが追加されると、/docsは次のようになります。

技術的な詳細

ヒント

すでにFastAPIバージョン0.99.0以降を使用している場合は、これらの詳細はスキップしても構いません。

OpenAPI 3.1.0が利用可能になる前の古いバージョンにより関連性があります。

これは、簡単なOpenAPIとJSONスキーマの歴史の授業と考えることができます。🤓

Warning

これらは、標準のJSONスキーマOpenAPIに関する非常に技術的な詳細です。

上記のアイデアがすでに機能している場合は、それで十分かもしれません。これらの詳細は必要ないかもしれませんので、自由にスキップしてください。

OpenAPI 3.1.0より前は、OpenAPIはJSONスキーマの古い修正バージョンを使用していました。

JSONスキーマにはexamplesがなかったため、OpenAPIは独自の修正バージョンに独自のexampleフィールドを追加しました。

OpenAPIは、仕様の他の部分にもexampleおよびexamplesフィールドを追加しました。

情報

この古いOpenAPI固有のexamplesパラメータは、FastAPI 0.103.0以降openapi_examplesになりました。

JSONスキーマのexamplesフィールド

しかし、その後JSONスキーマは、新しいバージョンの仕様にexamplesフィールドを追加しました。

そして、新しいOpenAPI 3.1.0は、この新しいフィールドexamplesを含む最新バージョン(JSON Schema 2020-12)に基づいています。

そして現在、この新しいexamplesフィールドは、現在非推奨となっている古い単一の(カスタム)exampleフィールドよりも優先されます。

JSONスキーマのこの新しいexamplesフィールドは、OpenAPIの他の場所(上記で説明)のように追加のメタデータを持つ辞書ではなく、単なる例のlistです。

情報

OpenAPI 3.1.0がJSONスキーマとのこの新しいシンプルな統合でリリースされた後も、しばらくの間、自動ドキュメントを提供するツールであるSwagger UIはOpenAPI 3.1.0をサポートしていませんでした(バージョン5.0.0以降はサポートしています🎉)。

そのため、FastAPIの0.99.0以前のバージョンは、3.1.0より古いバージョンのOpenAPIを使用していました。

PydanticとFastAPIのexamples

Pydanticモデル内にschema_extraまたはField(examples=["something"])を使用してexamplesを追加すると、その例はそのPydanticモデルのJSONスキーマに追加されます。

そして、そのPydanticモデルのJSONスキーマは、APIのOpenAPIに含まれ、ドキュメントUIで使用されます。

FastAPIの0.99.0より前のバージョン(0.99.0以降は新しいOpenAPI 3.1.0を使用)では、他のユーティリティ(Query()Body()など)でexampleまたはexamplesを使用した場合、それらの例はデータを記述するJSONスキーマに追加されず(OpenAPI自身のJSONスキーマバージョンにも)、OpenAPIのパス操作宣言に直接追加されました(JSONスキーマを使用するOpenAPIの部分の外側)。

しかし、FastAPI 0.99.0以降がOpenAPI 3.1.0(JSON Schema 2020-12を使用)とSwagger UI 5.0.0以降を使用するようになったため、すべてがより一貫性があり、例はJSONスキーマに含まれるようになりました。

Swagger UIとOpenAPI固有のexamples

さて、Swagger UIが複数のJSONスキーマの例をサポートしていなかったため(2023年8月26日現在)、ユーザーはドキュメントに複数の例を表示する方法がありませんでした。

それを解決するために、FastAPI 0.103.0は、新しいパラメータopenapi_examplesで同じ古いOpenAPI固有のexamplesフィールドを宣言するサポートを追加しました。🤓

概要

以前は歴史があまり好きではないと言っていましたが...今や「技術史」のレッスンをしているとは。😅

要するに、FastAPI 0.99.0以降にアップグレードすれば、物事ははるかにシンプルで、一貫性があり、直感的になり、これらの歴史的な詳細をすべて知る必要はありません。😎