FastAPIの拡張ライブラリの紹介

## はじめに

FastAPI は Pydantic によるリクエスト・レスポンスのバリデーション、DI、バックグラウンドタスクの実行、OpenAPIとしてのドキュメントの公開など、便利な機能が満載なフレームワークになっています。
そんな FastAPI で更に便利に開発するべく、様々な OSS の拡張ライブラリが実装されています。

FastAPI 拡張ライブラリは OR Mapper や、認証サービス連携などは特に多い印象なのですが、そちらは既に Qiita 記事もいくつか存在しているので割愛させていただくこととします。

本記事ではこれまであまり紹介されていない開発をちょっと便利にするようなライブラリについていくつか紹介します。

---

### [fastapi-camelcase](https://github.com/nf1s/fastapi-camelcase)

世の中の WebAPI では、リクエストやレスポンスのフィールドはキャメルケースの命名であることが多いと思います。
一方で Python ではクラスのメンバーはスネークケースで記述することが一般的であるため、Pydantic Model でスキーマを定義する場合に悩む場合も多いのではないでしょうか？

そんなときに使用すると便利なのが fastapi-camelcase です。
使用方法は簡単で、 `pytantic.BaseModel` の代わりに `fastapi_camelcase.CamelModel` を継承してスキーマを実装します。

```python
from fastapi_camelcase import CamelModel

class User(CamelModel):
    first_name: str
    last_name: str
    age: int
```

このモデルを使って FastAPI アプリケーションを起動して OpenAPI ドキュメントページ(/doc)を開き、スキーマ定義の欄を確認すると、以下のようになっていることを確認できると思います。
`first_name`, `last_name` が それぞれキャメルケースに変換されていますね。

```
Response Users object
  firstName string
  lastName string
  age integer
```

このライブラリの裏側の仕組みとしては、完全に Pydantic の機能をラップする形で実現されています。
Pydantic v2 においては、 model_config に `alias_generator` として、 `alias_generators.to_camel` を挿入することで、フィールド名のキャメルケースバージョンをエイリアスとして指定できるようになります。
加えてエイリアスとして定義された名前をオブジェクト作成時に入力することを許容するために、 `populate_by_name` を True に設定することで `CamelModel` を実現しています。

```python
from pydantic import BaseModel, ConfigDict
from pydantic import alias_generators

class CamelModel(BaseModel):
    model_config = ConfigDict(
        alias_generator=alias_generators.to_camel,
        populate_by_name=True
    )
```

pydantic だけで実現できるとはいえ、このあたりの仕組みを詳しく知らずとも継承だけで変換できるのはお手軽ですね。

:::note warn
CamelModelの継承先で model_config の `alias_generator` や `pupulate_by_name` の設定を変更してしまうと、キャメルケース変換ができなくなってしまうので注意が必要です。
:::

https://github.com/nf1s/fastapi-camelcase

---

### [fastapi-pagination](https://github.com/uriyyo/fastapi-pagination)

リストを取得するエンドポイントを実装する際、常に全件取得するような設計にしてしまうと何かとパフォーマンスの問題を抱えることになるので、ページネーションなどを設けることがよくあると思います。

fastapi-pagination を使用すると、ページネーションの実装を非常に簡単に導入することができます。

```python
from fastapi import FastAPI
from pydantic import BaseModel

from fastapi_pagination import Page, add_pagination, paginate

app = FastAPI()


class Person(BaseModel):
    name: str


persons = [
    Person(name="taro"),
    Person(name="jiro"),
    Person(name="saburo"),
    Person(name="shiro"),
    Person(name="goro"),
]


@app.get("/persons", response_model=Page[Person])
async def get_persons():
    return paginate(persons)


app = add_pagination(app)

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app)
```

キモになるのは、エンドポイントのレスポンスモデルに `Page[T]` クラスを設定している点と、`FastAPI` インスタンスを `add_pagination()` でラップしている点、そしてエンドポイントのレスポンスを返すときに、 `paginate` メソッドを実行している点です。

`Page[T]` クラスを `response_model` に指定すると、ページングのためのクエリパラメータ "page" と "size" が自動的にエンドポイントのスキーマに追加されます。
この仕組みのために `add_pagination()` wrapper を実行する必要があります。

最後に `paginate()` メソッドでは実際にレスポンスのデータにページング加工を施します。

上記のコード例を見て「これではメモリに一旦リストを全件持ってきているじゃないか」と思われた方もいらっしゃると思います。

fastapi-pagination にはインメモリデータのページング処理以外にも、 SQLAlchemy をはじめとしたデータベースフレームワークを介してページネーションを実現するためのバージョンも豊富に存在しているので、アプリケーションで使用しているデータベースに合わせたページネーション処理を使用することも、独自にページネーション処理を追加することも可能です。

```python: sqlalchemy での pagination の例
from sqlalchemy import select
from pydantic import BaseModel
from fastapi import Depends
from fastapi_pagination.ext.sqlalchemy import paginate  # SQLAlchemy 用の paginate 関数に変更

def get_db():
    ...


@app.get('/users', response_model=Page[Person])
def get_persons(db: Session = Depends(get_db)):
    return paginate(db, select(Person).order_by(Person.created_at))
```

https://github.com/uriyyo/fastapi-pagination


---

### [msgpack-asgi](https://github.com/florimondmanca/msgpack-asgi)

FastAPI では JSON 形式でシリアライズ・デシリアライズして通信することが主ですが、非常に大きなデータを通信する場合そのオーバーヘッドが気になってきます。そのような場合により効率的なデータフォーマットとして [MessagePack](https://msgpack.org/ja.html) のような形式が存在します。

msgpack-asgi を使用すると、FastAPI のコンテンツネゴシエーションを自動的に MessagePack(applications/x-msgpack) に変換してくれます。
使い方としては FastAPI に Messagepack 用の Middleware を追加するだけでお手軽に実装できます。

```python
from fastapi import FastAPI
from msgpack_asgi import MessagePackMiddleware

app = FastAPI()
app.add_middleware(MessagePackMiddleware)
```

これだけでメッセージの転送帯域を小さくすることができます。
その代わりに、 Messagepack 形式のメッセージをデコードする際に CPU コストが JSON に比べ大きくなります。
どちらを優先すべきかはユースケースに依存するので検討が必要です。

https://github.com/florimondmanca/msgpack-asgi

---

### [asgi-correlation-id](https://github.com/snok/asgi-correlation-id)

FastAPI のような ASGI アプリケーションに相関 ID の読み取り/生成を行うミドルウェアを提供します。
相関 ID は、マイクロサービスのような分散システム内の処理を追跡したり、アプリケーションのログを設計する際に、そのログの一連の流れを追うために役に立ちます。

```python
from asgi_correlation_id import CorrelationIdMiddleware
from fastapi import FastAPI

app = FastAPI()
app.add_middleware(CorrelationIdMiddleware)
```

この設定を加えることで、リクエストヘッダに設定された "X-Request-ID" から、外部の相関 ID 読み取って連携したり、それが無い場合には相関 ID を生成してレスポンスヘッダに返却することができるようになります。

https://github.com/snok/asgi-correlation-id

---

### [fastapi_profiler](https://github.com/sunhailin-Leo/fastapi_profiler)

fastapi_profiler は、名前の通り FastAPI のエンドポイントのパフォーマンスプロファイラーを提供します。
専用の Middleware を追加するだけですぐに使用することができます。

```python
from fastapi import FastAPI
from fastapi_profiler import PyInstrumentProfilerMiddleware

app = FastAPI()
app.add_middleware(PyInstrumentProfilerMiddleware)
```

この状態でアプリケーションを起動すると、各種エンドポイントを呼び出したときに、コンソールにプロファイリング結果がコンソールログに表示されます。

```:ログの例
INFO:     127.0.0.1:58274 - "GET /users HTTP/1.1" 200 OK
Method: GET, Path: /users, Duration: 0.18209500000000034, Status: 200

  _     ._   __/__   _ _  _  _ _/_   Recorded: 13:18:56  Samples:  1057
 /_//_/// /_\ / //_// / //_'/ //     Duration: 0.182     CPU time: 0.141
/   _/                      v4.6.1

Program: example.py
```

fastapi_profiler のプロファイリングは [pyinstrument](https://github.com/joerick/pyinstrument) というツールによって行われており、出力結果もこのツールによるものになります。

**プロファイリング結果のファイル出力**

プロファイリング結果を外部ファイルに出力して CI 結果などとして公開・共有したいというケースもあると思います。
そのような場合には Middleware 追加時にオプションを変更することで、測定結果をファイル出力することも可能です。

```python:htmlファイルを出力する場合
from fastapi import FastAPI
from fastapi_profiler import PyInstrumentProfilerMiddleware

app = FastAPI()
app.add_middleware(
    PyInstrumentProfilerMiddleware,
    server_app=app,
    profiler_output_type="html",
    html_file_name="example.html",
)
```

htmlファイルは FastAPI アプリケーションが終了したときに、 `html_file_name` で指定したファイルパスに出力されます。

https://github.com/sunhailin-Leo/fastapi_profiler

---

### [manage-fastapi](https://github.com/ycd/manage-fastapi)

FastAPI アプリケーション開発時に使えるコマンドラインツールになります。
manage-fastapi をインストールすると、 `fastapi` コマンドを使えるようになり

* プロジェクトの自動新規作成: __startproject__
* コンポーネントの自動新規作成: __startapp__
* アプリケーションの起動: __run__

といった操作がコマンドから実行可能になります。

```bash
$ fastapi startproject example-proj --interactive
```

特にプロジェクトの新規作成時には、ライセンスの選択、pythonバージョンの選択、Docker環境の使用有無、DB などを選択することで、設定にあったREADME、LICENSE、Dockerfile、ソースコードテンプレートなどが自動生成することができます。
このツールを使うことでプロジェクト開発初期のボイラープレートをほとんどそのまま使いまわすことができるため、開発効率の向上が見込めます。

https://github.com/ycd/manage-fastapi

## おわりに
FastAPI による開発を更に便利にするための OSS ライブラリ・ツールを限定的ではありますが紹介させていただきました。

本記事で紹介したもの以外にも、まだまだ数多くの便利ライブラリが存在しますので、興味がある方はぜひ探して使ってみてください。

ここまでお付き合いいただきありがとうございました。

## 参考
- [CamelCase Models with FastAPI and Pydantic | Medium](https://medium.com/analytics-vidhya/camel-case-models-with-fast-api-and-pydantic-5a8acb6c0eee)
- [mjhea0/awesome-fastapi | GitHub](https://github.com/mjhea0/awesome-fastapi)