コンテンツへスキップ

リクエストのサンプルデータを宣言する

アプリが受信できるデータの例を宣言できます。

これを行うにはいくつかの方法があります。

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

生成されたJSONスキーマに追加されるPydanticモデルの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 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

    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

    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のドキュメント:構成で説明されているように、dictを受け取る属性model_configを使用します。

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

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

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

ヒント

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

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

情報

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

それ以前は、単一の例を持つキーワード example のみがサポートされていました。これは OpenAPI 3.1.0 でも引き続きサポートされていますが、非推奨であり、JSON Schema 標準の一部ではありません。そのため、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 Schema における examples - OpenAPI

次のいずれかを使用する場合

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

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

Bodyexamples

ここでは、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 Schema の一部になります。

ただし、この記事を書いている時点では、ドキュメント UI の表示を担当するツールである Swagger UI は、JSON Schema のデータに対して複数の例を表示することをサポートしていません。ただし、回避策については以下をお読みください。

OpenAPI 固有の examples

JSON Schemaexamples をサポートする以前から、OpenAPI は examples という別のフィールドをサポートしていました。

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

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

この OpenAPI 固有のフィールド examples の形状は、複数の例 (list の代わりに) を持つ dict であり、それぞれに OpenAPI にも追加される追加情報が含まれています。

これは、OpenAPI に含まれる各 JSON Schema の内部ではなく、パス操作に直接、外部に配置されます。

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 の例

openapi_examplesBody() に追加すると、/docs は次のようになります。

技術的な詳細

ヒント

すでに FastAPI バージョン 0.99.0 以上を使用している場合は、おそらくこれらの詳細を スキップできます。

これらは、OpenAPI 3.1.0 が利用可能になる前の古いバージョンに関連しています。

これは、OpenAPI と JSON Schema の簡単な 歴史レッスン と考えてください。🤓

警告

これらは、標準の JSON SchemaOpenAPI に関する非常に技術的な詳細です。

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

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

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

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

情報

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

JSON Schema の examples フィールド

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

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

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

JSON Schema のこの新しい examples フィールドは、単なる list であり、OpenAPI の他の場所 (上記) のように追加のメタデータを持つ dict ではありません。

情報

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

そのため、0.99.0 より前の FastAPI のバージョンでは、3.1.0 より低いバージョンの OpenAPI が使用されていました。

Pydantic および FastAPI の examples

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

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

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

しかし、FastAPI 0.99.0 以上が JSON Schema 2020-12 を使用する OpenAPI 3.1.0 を使用するようになり、Swagger UI 5.0.0 以上になったことで、すべてがより一貫性があり、例は JSON Schema に含まれるようになりました。

Swagger UI と OpenAPI 固有の examples

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

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

まとめ

昔は歴史があまり好きではなかったと言っていましたが…今では「技術史」のレッスンをしています。😅

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