コンテンツへスキップ

パスパラメーター

Pythonのフォーマット文字列と同じ構文で、パスの「パラメーター」または「変数」を宣言できます。

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

パスパラメーターitem_idの値は、引数item_idとして関数に渡されます。

したがって、この例を実行してhttp://127.0.0.1:8000/items/fooにアクセスすると、次のようなレスポンスが表示されます。

{"item_id":"foo"}

型付きパスパラメーター

標準のPython型アノテーションを使用して、関数のパスパラメーターの型を宣言できます。

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

この場合、item_idintとして宣言されています。

確認

これにより、関数内でエラーチェックや補完などのエディターサポートが提供されます。

データ変換

この例を実行してブラウザーでhttp://127.0.0.1:8000/items/3を開くと、次のようなレスポンスが表示されます。

{"item_id":3}

確認

関数が受け取った(そして返した)値が文字列"3"ではなく、Pythonのintとして3であることに注意してください。

つまり、その型宣言によって、FastAPIは自動的なリクエストの「パース」を提供します。

データ検証

しかし、ブラウザーでhttp://127.0.0.1:8000/items/fooにアクセスすると、次のようなHTTPエラーが表示されます。

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": [
        "path",
        "item_id"
      ],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "foo"
    }
  ]
}

パスパラメーターitem_idの値が"foo"であり、これはintではないためです。

intの代わりにfloatを指定した場合も同様のエラーが表示されます(例:http://127.0.0.1:8000/items/4.2)。

確認

したがって、同じPythonの型宣言で、FastAPIはデータ検証を提供します。

エラーが、検証が失敗した正確なポイントを明確に示していることに注意してください。

これは、APIとやり取りするコードの開発およびデバッグ中に非常に役立ちます。

ドキュメンテーション

ブラウザーでhttp://127.0.0.1:8000/docsを開くと、次のような自動のインタラクティブなAPIドキュメントが表示されます。

確認

繰り返しますが、同じPython型宣言だけで、FastAPIは自動のインタラクティブなドキュメント(Swagger UIを統合)を提供します。

パスパラメーターが整数として宣言されていることに注意してください。

標準ベースの利点、代替ドキュメンテーション

生成されたスキーマはOpenAPI標準に基づいているため、多くの互換性のあるツールがあります。

このため、FastAPI自体は代替のAPIドキュメント(ReDocを使用)を提供しており、http://127.0.0.1:8000/redocでアクセスできます。

同様に、多くの互換性のあるツールがあります。これには、多くの言語用のコード生成ツールが含まれます。

Pydantic

すべてのデータ検証はPydanticによって内部的に実行されるため、そのすべての利点が得られます。そして、あなたは良い手に委ねられていると知ることができます。

strfloatbool、その他多くの複雑なデータ型と同じ型宣言を使用できます。

これらのいくつかは、チュートリアルの次の章で詳しく説明されます。

順序が重要

パス操作を作成する際、固定パスを持つ状況に遭遇することがあります。

/users/meのように、現在のユーザーに関するデータを取得するためだとしましょう。

そして、特定のユーザーIDで特定のユーザーに関するデータを取得するためのパス/users/{user_id}も持つことができます。

パス操作は順序通りに評価されるため、/users/meのパスが/users/{user_id}のパスよりも前に宣言されていることを確認する必要があります。

from fastapi import FastAPI

app = FastAPI()


@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

そうしないと、/users/{user_id}のパスが/users/meにも一致し、値が"me"のパラメーターuser_idを受け取っていると「考え」てしまいます。

同様に、パス操作を再定義することはできません。

from fastapi import FastAPI

app = FastAPI()


@app.get("/users")
async def read_users():
    return ["Rick", "Morty"]


@app.get("/users")
async def read_users2():
    return ["Bean", "Elfo"]

パスが最初に一致するため、常に最初のものが使用されます。

事前定義された値

パスパラメーターを受け取るパス操作があるが、可能な有効なパスパラメーターの値を事前に定義したい場合、標準のPythonのEnumを使用できます。

Enumクラスの作成

Enumをインポートし、strEnumを継承するサブクラスを作成します。

strを継承することで、APIドキュメントは値がstring型でなければならないことを認識し、正しくレンダリングできるようになります。

次に、利用可能な有効な値となる固定値を持つクラス属性を作成します。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

ヒント

「AlexNet」、「ResNet」、「LeNet」は、単なる機械学習のモデルの名前です。

パスパラメーターの宣言

次に、作成したenumクラス(ModelName)を使用して型アノテーションを付けたパスパラメーターを作成します。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

ドキュメントを確認する

パスパラメーターに利用可能な値が事前定義されているため、インタラクティブなドキュメントはそれらをきれいに表示できます。

Pythonの列挙型の操作

パスパラメーターの値は、列挙メンバーになります。

列挙メンバーの比較

作成したenum ModelName列挙メンバーと比較できます。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

列挙値の取得

model_name.value、または一般的にはyour_enum_member.valueを使用して、実際の値(この場合はstr)を取得できます。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

ヒント

また、ModelName.lenet.valueで値"lenet"にアクセスすることもできます。

列挙メンバーの返却

パス操作からenumメンバーを返すことができます。JSONボディ(例:dict)にネストされている場合でも可能です。

これらはクライアントに返される前に、対応する値(この場合は文字列)に変換されます。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

クライアントでは、次のようなJSONレスポンスが得られます。

{
  "model_name": "alexnet",
  "message": "Deep Learning FTW!"
}

パスを含むパスパラメーター

パス/files/{file_path}を持つパス操作があるとします。

しかし、file_path自体がhome/johndoe/myfile.txtのようなパスを含む必要があります。

したがって、そのファイルのURLは/files/home/johndoe/myfile.txtのようになります。

OpenAPIのサポート

OpenAPIは、パスパラメーターが内部にパスを含むことを宣言する方法をサポートしていません。これは、テストや定義が困難なシナリオにつながる可能性があるためです。

それにもかかわらず、FastAPIではStarletteの内部ツールの1つを使用して、まだそれを行うことができます。

そして、ドキュメントは、パラメーターがパスを含むべきであるというドキュメントを追加しないにもかかわらず、まだ機能します。

パスコンバーター

Starletteのオプションを直接使用して、次のようなURLでパスを含むパスパラメーターを宣言できます。

/files/{file_path:path}

この場合、パラメーター名はfile_pathで、最後の部分である:pathは、パラメーターが任意のパスに一致する必要があることを示しています。

したがって、次のように使用できます。

from fastapi import FastAPI

app = FastAPI()


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

ヒント

パラメーターに先頭のスラッシュ(/)を含む/home/johndoe/myfile.txtが必要な場合があります。

その場合、URLは/files//home/johndoe/myfile.txtとなり、fileshomeの間に二重スラッシュ(//)が入ります。

まとめ

FastAPIでは、短く、直感的で標準的なPythonの型宣言を使用することで、次のものが得られます。

  • エディターサポート:エラーチェック、オートコンプリートなど。
  • データ「パース」
  • データ検証
  • APIアノテーションと自動ドキュメンテーション

そして、それらを一度宣言するだけで済みます。

これはおそらく、FastAPIが他の代替フレームワークと比較して(純粋なパフォーマンスとは別に)最も目に見える利点です。