0
0

Fast APIチュートリアルを試してみた-①(Path,Query Parameter)

Posted at

背景・目的

前回、Fast API(※下記の記事参照)について特徴を整理し簡単に試しました。

今回は、下記のチュートリアルを通して理解を深めたいと思います。

まとめ

下記に特徴を整理します。

特徴 説明
Starlette 軽量のASGIフレームワーク/ツールキットであり、Python で非同期 Web サービスを構築するのに最適。
ASGI Asynchronous Server Gateway Interfaceの略

非同期対応のPython Webサーバ、フレームワーク、アプリケーション間の標準インターフェイスを提供することを目的としている

WSGI が同期 Python アプリの標準を提供したのに対して、ASGI は WSGI 下位互換性実装と複数のサーバーおよびアプリケーション フレームワークを備え、非同期アプリと同期アプリの両方に標準を提供する
Pydantic Pydantic は、Python で最も広く使用されているデータ検証ライブラリ

高速で拡張可能な Pydantic は、linters/IDE/brainとうまく連携する

概要

Recap, step by step

下記を基にまとめます。

Step 1: import FastAPI

from fastapi import FastAPI

FastAPI は、API のすべての機能を提供する Python クラスです。
FastAPI は Starlette から直接継承するクラスです。 Starlette のすべての機能を FastAPI でも使用できます。

Step 2: create a FastAPI "instance"

app = FastAPI()

app変数がFastAPIクラスのインスタンスになる。
すべてのAPIを作成するための主要なポイント

Step 3: create a path operation¶

Path

  • ここでの「パス」とは、URL の最初の / から始まる最後の部分を指す
  • URLは下記のようになる
    https://example.com/items/foo
    
  • パスは下記のようになる
    /items/foo
    
  • 「パス」は一般に「エンドポイント」または「ルート」とも呼ばれる

Operation

  • ここでの「オペレーション」とは、HTTP の「メソッド」の 1 つを指す
  • HTTP プロトコルでは、これらの「メソッド」の 1 つ (または複数) を使用して各パスと通信できる
  • API を構築するときは、通常、これらの特定の HTTP メソッドを使用して特定のアクションを実行する
  • 通常の使用法は下記の通り
    • POST: to create data
    • GET: to read data
    • PUT: to update data
    • DELETE: to delete data
  • そのため、OpenAPI では、それぞれの HTTP メソッドを「オペレーション」と呼ぶ
  • Define a path operation decorator
    @app.get("/")
    
    • @app.get("/") は、すぐ下の関数が次の宛先へのリクエストの処理を担当することを FastAPI に伝えている
      • path=/
      • getオペレーションを利用している
  • その他のオペレーション
    • @app.post()
    • @app.put()
    • @app.delete()
    • @app.options()
    • @app.head()
    • @app.patch()
    • @app.trace()
  • 各操作 (HTTP メソッド) は必要に応じて自由に使用できる。FastAPI は特定の意味を強制しない
  • 上記は、こガイドラインとして提供されており、要件ではない
    • たとえば、GraphQL を使用する場合、通常は POST 操作のみを使用してすべてのアクションを実行する

Step 4: define the path operation function

@app.get("/")
async def root():
  • パス操作関数
  • GET 操作を使用して URL「/」へのリクエストを受信するたびに、FastAPI によって呼び出される
  • 非同期関数
  • async defの代わりに通常の関数として定義も可能
@app.get("/")
def root():

Step 5: return the content

@app.get("/")
async def root():
    return {"message": "Hello World"}
  • 辞書、リスト、特異値を str、int などとして返すことが可能
  • Pydantic モデルを返すこともできる
  • 自動的に JSON (ORM などを含む) に変換されるオブジェクトやモデルは他にも多数ある

関連知識

Starlette

下記のページを基に特徴を整理します。
https://www.starlette.io/

Starlette は軽量のASGIフレームワーク/ツールキットであり、Python で非同期 Web サービスを構築するのに最適です。
本番環境ですぐに使用可能で、次の機能が提供されます。

  • 軽量で複雑さの少ない HTTP Web フレームワーク。
  • WebSocket サポート。
  • インプロセスバックグラウンドタスク。
  • 起動およびシャットダウン イベント。
  • 上に構築されたテストクライアントhttpx。
  • CORS、GZip、静的ファイル、ストリーミング応答。
  • セッションと Cookie のサポート。
  • 100% テストカバレッジ。
  • 100% 型注釈付きのコードベース。
  • ハード依存関係はほとんどありません。
  • asyncioおよびバックエンドと互換性がありますtrio。
  • 独立したベンチマークに対して全体的に優れたパフォーマンスを発揮します。
  • Starlette
    • 軽量のASGIフレームワーク/ツールキット
    • Python で非同期 Web サービスを構築するのに最適
    • 下記の機能を提供している
      • 軽量で複雑さの少ない HTTP Web フレームワーク
      • WebSocketをサポート
      • In-process バックグラウンドタスク
      • 起動およびシャットダウン イベント
      • httpx上に構築されたテストクライアント
      • CORS、GZip、静的ファイル、ストリーミングレスポンス
      • セッションと Cookie のサポート
      • 100% テストカバレッジ
      • 100% アノテーション付きのコードベース
      • ハード依存関係はほとんどなし
      • asyncioおよびrioのバックエンドと互換性がある
      • 独立したベンチマークに対して全体的に優れたパフォーマンスを発揮する

ASGI

下記を基に整理します。

  • Asynchronous Server Gateway Interfaceの略
  • 非同期対応のPython Webサーバ、フレームワーク、アプリケーション間の標準インターフェイスを提供することを目的としている
  • WSGI が同期 Python アプリの標準を提供したのに対して、ASGI は WSGI 下位互換性実装と複数のサーバーおよびアプリケーション フレームワークを備え、非同期アプリと同期アプリの両方に標準を提供する

Pydantic

下記を基に整理します。

  • Pydantic は、Python で最も広く使用されているデータ検証ライブラリ
  • 高速で拡張可能な Pydantic は、linters/IDE/brainとうまく連携する
  • Python 3.8 以降で動作する

Why use Pydantic?

  • Powered by type hints — with Pydantic, schema validation and serialization are controlled by type annotations; less to learn, less code to write, and integration with your IDE and static analysis tools. Learn more…
  • Speed — Pydantic's core validation logic is written in Rust. As a result, Pydantic is among the fastest data validation libraries for Python. Learn more…
  • JSON Schema — Pydantic models can emit JSON Schema, allowing for easy integration with other tools. Learn more…
  • Strict and Lax mode — Pydantic can run in either strict mode (where data is not converted) or lax mode where Pydantic tries to coerce data to the correct type where appropriate. Learn more…
  • Dataclasses, TypedDicts and more — Pydantic supports validation of many standard library types including dataclass and TypedDict. Learn more…
  • Customisation — Pydantic allows custom validators and serializers to alter how data is processed in many powerful ways. Learn more…
  • Ecosystem — around 8,000 packages on PyPI use Pydantic, including massively popular libraries like FastAPI, huggingface, Django Ninja, SQLModel, & LangChain. Learn more…
  • Battle tested — Pydantic is downloaded over 70M times/month and is used by all FAANG companies and 20 of the 25 largest companies on NASDAQ. If you're trying to do something with Pydantic, someone else has probably already done it. Learn more…
  • Powered by type hints
    • Pydantic では、スキーマ検証とシリアル化は型注釈によって制御されるため、学習する内容が少なくなり、記述するコードも少なくなり、IDE や静的解析ツールと統合される
  • Speed
    • Pydantic のコア検証ロジックは Rust で書かれている
    • その結果、Pydantic は Python で最も高速なデータ検証ライブラリの 1 つとなっている
  • JSON Schema
    • Pydantic モデルは JSON スキーマを出力できるため、他のツールとの統合が容易になる
  • Strict and Lax mode
    • Pydantic は、厳密モード (データが変換されない) または緩いモード (適切な場合に Pydantic がデータを正しい型に強制変換しようとする) のいずれかで実行できる
  • Dataclasses, TypedDicts and more
    • Pydantic は、dataclassやを含む多くの標準ライブラリ型の検証をサポートしている
  • Customisation
    • Pydantic では、カスタム バリデータとシリアライザーを使用して、さまざまな強力な方法でデータの処理方法を変更できる
  • Ecosystem
    • PyPI 上の約 8,000 個のパッケージが Pydantic を使用している
    • これには、 FastAPI、huggingface、Django Ninja、SQLModel、LangChainなどの非常に人気のあるライブラリが含まれる
  • Battle tested
    • Pydantic は毎月 7,000 万回以上ダウンロードされており、すべての FAANG 企業と NASDAQ の上位 25 社のうち 20 社で使用されている

実践

First Steps

下記を基に試します。

OpenAPI

  1. openapi.pyというファイル名で下記の内容を書きます

    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/")
    async def root():
      return {"message": "Hello World"}
    
  2. 起動します

    fastapi dev openapi.py 
    
  3. ブラウザでhttp://127.0.0.1:8000/を開きます

  4. 下記の確認ができます

  5. ブラウザでhttp://127.0.0.1:8000/docsを開きます

  6. 下記の確認ができます
    image.png

Check the openapi.json

OpenAIの基になる素のopenapi.jsonをファイルを確認します。

  1. ブラウザで、http://127.0.0.1:8000/openapi.jsonを開きます
  2. 下記が表示されました

Path Parameters

Python フォーマット文字列で使用されるのと同じ構文を使用して、パスの「パラメータ」または「変数」を宣言できる

  1. pathparameter.pyというファイルにコードを書きます
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/items/{item_id}")
    async def read_item(item_id):
        return {"item_id": item_id}
    
  2. アプリケーションを起動します
    $ fastapi dev pathparameter.py
    
  3. ブラウザでhttp://127.0.0.1:8000/items/fooを開きます
  4. 下記のように表示されました

Path parameters with types

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

  1. pathparameter.pyのパラメータをint型に変更します

    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/items/{item_id}")
    async def read_item(item_id: int):
        return {"item_id": item_id}
    
  2. ブラウザでhttp://127.0.0.1:8000/items/fooを開きます

  3. 下記のように表示されました。intじゃなきゃだめと言われました
    image.png

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

Data conversion

  1. ブラウザで、http://127.0.0.1:8000/items/3を開きます

  2. 下記のように表示されました

    • 関数が受け取った (そして返した) 値は、文字列「3」ではなく、Python int としての 3 である
    • その型宣言により、FastAPI は自動的にリクエストを「解析」する

Data validation

  1. ブラウザで、http://127.0.0.1:8000/items/fooを開きます

  2. 下記のように表示されました。上記と同じようにstring ではなくintの必要があります
    image.png

  3. また、ブラウザで、http://127.0.0.1:8000/items/4.2を開きます

  4. 小数点もだめです
    image.png

Pydantic

すべてのデータ検証は Pydantic によって内部で実行されるため、そのメリットをすべて享受できます。
str、float、bool、およびその他の多くの複雑なデータ型で同じ型宣言を使用できます。

Order matters

パス操作を作成するときに、パスが固定されている状況が発生することがある。
/users/me と同様に、現在のユーザーに関するデータを取得するためだとしましょう。

また、パス /users/{user_id} を使用して、ユーザー ID によって特定のユーザーに関するデータを取得することもできます。 パス操作は順番に評価されるため、/users/me のパスが /users/{user_id} のパスより前に宣言されていることを確認する必要があります。

  1. user.pyファイルにコードを書きます

    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}
    
  2. アプリケーションを起動します

    fastapi dev user.py
    
  3. ブラウザで、http://127.0.0.1:8000/users/meを開きます

  4. 下記のように表示されました

  5. ブラウザで、http://127.0.0.1:8000/users/100を開きます

  6. 下記のように表示されました

上記の場合、/users/meは、下の`/users/{user_id}に該当するケースがあるかもしれないが、先に上に引っかかる。
似たようなケースを試す。

  1. 同一パスで異なる関数をを追加します

    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}
    
    # Added
    @app.get("/users")
    async def read_users():
      return ["Rick","Morty"]
    
    # Added
    @app.get("/users")
    async def read_users2():
      return ["Bean","Elfo"]
    
  2. ブラウザで、http://127.0.0.1:8000/usersを開きます

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

Predefined values

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

Create an Enum class

Enum をインポートし、str と Enum を継承するサブクラスを作成します。
str から継承することにより、API ドキュメントは値が文字列型である必要があることを認識し、正しくレンダリングできるようになります。

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

  1. enum_test.pyファイルにコードを書きます

    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 == 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"}
    
  2. アプリケーションを起動します

    fastapi dev enum_test.py
    
  3. ブラウザで、http://127.0.0.1:8000/models/alexnetを開きます

  4. ブラウザで、http://127.0.0.1:8000/models/lenetを開きます

  5. ブラウザで、http://127.0.0.1:8000/models/resnetを開きます

  6. ブラウザで、http://127.0.0.1:8000/models/XXXXXを開きます。事前定義されてないのでエラーになりました

Declare a path parameter

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

Check the docs

path パラメーターに使用できる値は事前に定義されているため、インタラクティブなドキュメントで適切に表示できます。

  1. ブラウザで、http://127.0.0.1:8000/docs#/default/get_model_models__model_name__getを開きます
    image.png

  2. 「Try it out」をクリックします

  3. 「model_name」を選択できます
    image.png

Path parameters containing paths

パス /files/{file_path} を使用したパス操作があるとします。
ただし、home/johndoe/myfile.txt のように、file_path 自体にパスを含める必要があります。 したがって、そのファイルの URL は /files/home/johndoe/myfile.txt のようになります。

OpenAPI support

OpenAPI は、内部にパスを含むパス パラメーターを宣言する方法をサポートしていません。これは、テストと定義が困難なシナリオにつながる可能性があるためです。 それでも、Starlette の内部ツールの 1 つを使用して、FastAPI でこれを行うことはできます。
また、パラメータにパスを含める必要があることを示すドキュメントは追加されませんが、ドキュメントは引き続き機能します。

Path convertor

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

/files/{file_path:path}

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

  1. filepath.pyに下記のコードを書きます
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/files/{file_path:path}")
    async def read_file(file_path: str):
        return {"file_path": file_path}
    
  2. アプリケーションを起動します
    fastapi dev filepash.py 
    
  3. ブラウザで、http://127.0.0.1:8000/files/files/home/johndoe/myfile.txtを開きます

Query Parameters

パス パラメーターの一部ではない他の関数パラメーターを宣言すると、それらは自動的に「クエリ」パラメーターとして解釈されます。
クエリは、? の後に続くキーと値のペアのセットです。 URL 内で & 文字で区切ります。

  1. ファイルquery_parameters.pyに、下記のコードを書きます

    from fastapi import FastAPI
    
    app = FastAPI()
    
    fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
    
    @app.get("/items/")
    async def read_item(skip: int = 0, limit: int = 10):
        return fake_items_db[skip : skip + limit]
    
    • クエリパラメータは次のとおりです
      • skip: 値が 0
      • limit: 値は 10
    • パス パラメーターに適用されたのと同じプロセスがクエリ パラメーターにも適用されます
  2. アプリケーションを起動します

    fastapi dev query_parameters.py 
    
  3. ブラウザで、http://127.0.0.1:8000/items/を開きます。デフォルトが表示されました

  4. 次にクエリパラメータを含めてhttp://127.0.0.1:8000/items/?skip=0&limit=2を開きます。2県表示されました

  5. 他にも、パラメータを変えてアクセスしてみます

Optional parameters

同様に、デフォルトを None に設定することで、オプションのクエリ パラメータを宣言できます。

  1. option_parameters.pyに、下記のコードを書きます

    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/items/{item_id}")
    async def read_item(item_id: str, q: str = None):
      if q:
        return {"item_id": item_id, "q": q}
      return {"item_id": item_id}
    
    • この場合、関数パラメータ q はオプションであり、デフォルトでは None になります
  2. アプリケーションを起動します

    fastapi dev option_parameters.py 
    
  3. ブラウザで、http://127.0.0.1:8000/items/1を開きます(パラメータなし)

  4. ブラウザで、http://127.0.0.1:8000/items/1?q=testを開きます(qあり)

Query parameter type conversion

bool 型を宣言することもでき、それらは変換されます。

  1. option_parameters.pyのコードを修正します

    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/items/{item_id}")
    async def read_item(item_id: str, q: str | None = None, short: bool = False):
      item ={"item_id": item_id}
      if q:
        item.update({"q": q})
      if not short:
        item.update({"description": "This is an amazing item that has a long description"})
      return item
    
  2. ブラウザで、http://127.0.0.1:8000/items/foo?q=1&short=falseを開きます

  3. ブラウザで、http://127.0.0.1:8000/items/foo?q=1&short=trueを開きます

Multiple path and query parameters

複数のパス パラメーターとクエリ パラメーターを同時に宣言でき、FastAPI はどれがどれであるかを認識します。 また、特定の順序で宣言する必要はありません。
これらは名前によって検出されます。

  1. multiple_path_query_parameters.pyファイルに下記のコードを書きます
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/users/{user_id}/items/{item_id}")
    async def read_user_item(
      user_id: int
      ,item_id: str
      ,q: str| None = None
      , short: bool = False 
      ):
      
      item = {"item_id": item_id, "owner_id": user_id}
      if q:
        item.update({"q": q})
      if not short:
        item.update(
          {"description": "This is an amazing item that has a long description"}
        )
      return item
    
  2. アプリケーションを起動します
    fastapi dev multiple_path_query_parameters.py
    
  3. ブラウザで、http://127.0.0.1:8000/users/1/items/item1000を開きます

Required query parameters

非パス パラメーター のデフォルト値を宣言する場合、それは必要ありません。
特定の値を追加せずにオプションにするだけの場合は、デフォルトを「なし」に設定します。
ただし、クエリ パラメータを必須にしたい場合は、デフォルト値を宣言しないだけで済みます。

  1. required_query_parameters.pyを作成し、下記のコードを書きます

    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/items/{item_id}")
    async def read_item(item_id: str, needy: str):
        item = {"item_id": item_id, "needy": needy}
        return item
    
  2. ここで、クエリ パラメータ needy は、str 型の必須クエリ パラメータです。 ブラウザでhttp://127.0.0.1:8000/items/foo-itemを開くと、必要なパラメータを追加しないと、次のようなエラーが表示されます

  3. needy は必須パラメータであるため、URL に設定する必要があります。http://127.0.0.1:8000/items/foo-item?needy=sooooneedyを開きます
    image.png

必要に応じていくつかのパラメータを定義できます。いくつかはデフォルト値を持つもので、いくつかは完全にオプションです。

  1. required_query_parameters.pyを修正します
    from fastapi import FastAPI
    
    app = FastAPI()
    
    
    @app.get("/items/{item_id}")
    async def read_user_item(
        item_id: str, needy: str, skip: int = 0, limit: int | None = None
    ):
        item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
        return item
    
  • クエリ パラメーターは 3 つあります
    • needy、必須の str
    • Skip、デフォルト値が 0 の int
    • kimit、オプションの int
  1. ブラウザで、http://127.0.0.1:8000/items/foo-itemを開きます(想定通りエラー)

  2. ブラウザで、http://127.0.0.1:8000/items/foo-item?needy=sooooneedyを開きます(skipはデフォルトの0が含まれている)

  3. ブラウザで、http://127.0.0.1:8000/items/foo-item?needy=sooooneedy&skip=10を開きます

考察

リクエストパスや、クエリストリングなどの扱いを試しました。今後も継続してチュートリアルを進めます。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0