FastAPI 学習・実装に役立つリソースと注意点

本記事では、**[FastAPI](https://fastapi.tiangolo.com/)** を使用して Web API を実装する方を対象に、有益なリソースと留意すべき事項を整理しています。Web API に関連する具体的な内容には触れていません。

# リソース
## 公式ドキュメント
**FastAPI** は、[チュートリアル](https://fastapi.tiangolo.com/tutorial/)や[高度なユーザーガイド](https://fastapi.tiangolo.com/advanced/)が用意されており、FastAPI を初めて使用する方でも、簡単に理解することができます。

また、FastAPI の強みである「[型ヒント](https://fastapi.tiangolo.com/python-types/)」や「[非同期処理（async / await）](https://fastapi.tiangolo.com/async/)」についても、丁寧に解説されています。

さらに、日本語を含む様々な言語で書かれた FastAPI に関するブログ記事が紹介されているページがあります。[外部リンク・記事 - FastAPI](https://fastapi.tiangolo.com/external-links/)

:::note warn
FastAPI は高頻度で新機能が追加されているため、公式ドキュメントの日本語訳のページは、ほとんどが更新されておらず、古いバージョンの書き方が多く残っている可能性があります。そのため、最新の情報を得るためには、元の英文の記事を読むことを強くおすすめします。
:::

## MongoDB
**MongoDB** の公式サイトでは、以下のように MongoDB との連携に役立つ記事が用意されています。
+ **[Getting Started with MongoDB and FastAPI | MongoDB](https://www.mongodb.com/developer/languages/python/python-quickstart-fastapi/)**
　MongoDB を FastAPI プロジェクトと統合する方法を説明しています。

+ **[Introducing FARM Stack - FastAPI, React, and MongoDB | MongoDB](https://www.mongodb.com/developer/languages/python/farm-stack-fastapi-react-mongodb/?_ga=2.208451729.2034734222.1693281189-1166587812.1693281189)**
　FastAPI、React、MongoDB を組み合わせて、フルスタック Web アプリケーションを開発する方法を紹介しています。

## Awesome FastAPI
FastAPI を使う方は是非見ていただきたい GitHub のレポジトリです。

https://github.com/mjhea0/awesome-fastapi

FastAPI に関連する 
+ サードパーティ
+ 記事、ポッドキャスト、動画などのリソース
+ ボイラープレート
+ Docker イメージ
+ オープンソースプロジェクト

が厳選されてまとめられています。

## 記事
FastAPI に関する記事が掲載されているサイトを紹介します。
2023 年 10 月現在、記事数が一番多いサイトは **Medium** です。

### 日本語の記事
+ **[Qiita](https://qiita.com/tags/fastapi)**：エンジニア向けの技術ブログサービス
+ **[Zenn](https://zenn.dev/topics/fastapi)**：エンジニア向けの技術ブログサービス
    + [FastAPI入門](https://zenn.dev/sh0nk/books/537bb028709ab9)
        環境構築に `Docker` や `poetry` が使われており、また、データベースに `MySQL` を利用し、非同期で接続しているなど、モダンで実践的な入門書です。

### 英語の記事
+ **[Medium](https://medium.com/tag/fastapi)**：ブログサービス
 FastAPI の作成者である [Sebastián Ramírez](https://tiangolo.medium.com/) さんの記事もあります。
 記事の数が多く、公式ドキュメントでドキュメント化されていない最新の情報（[Pydantic バージョン 2 への対応](https://medium.com/python-in-plain-english/%EF%B8%8F-fastapi-pydantic-2-4-sqlalchemy-2-0-more-1d50d9948330) など）も手に入れやすいです。
+ **[DEV Community](https://dev.to/t/FastAPI)**：エンジニア向けの技術情報共有サービス
 FastAPI の作成者である [Sebastián Ramírez](https://dev.to/tiangolo) さんの記事もあります。
+ **[freeCodeCamp](https://www.freecodecamp.org/news/tag/fastapi/)**：プログラミング学習サイト
記事の数は少ないものの JWT 認証やテスト方法、マイクロサービスを作成する方法など充実した内容の記事があります。

## SNS
+ **X（旧 Twitter）**：公式のアカウント [＠FastAPI](https://twitter.com/FastAPI) もあります。公式のアカウントで、最新バージョンのリリース情報や公式ドキュメントの更新情報などを知ることができます。
+ **[Reddit](https://www.reddit.com/search/?q=FastAPI)**：活発な議論が行われる SNS です。
+ **[YouTube](https://www.youtube.com/results?search_query=FastAPI)**：初学者向けの動画が充実しています。
    + [Python API Development - Comprehensive Course for Beginners](https://youtu.be/0sOvCWFmrtA)
    2021 年の動画ですが、FastAPI の基本的な使い方だけでなく、データベースアクセスやユーザ認証、NGINX との連携など幅広く、充実した内容です。

## 有料の教材

+ **[Udemy](https://www.udemy.com/courses/search/?src=ukw&q=FastAPI)**
オンライン学習サイトです。初学者向けのチュートリアルから応用的な使い方まで、幅広いコースが用意されています。

+ **書籍**
2023 年 10 月現在、FastAPI について書かれた書籍は下記の書籍を含めて、数冊しかありません。
  + **「動かして学ぶ！Python FastAPI開発入門」**
Zennで公開されている[FastAPI入門](https://zenn.dev/sh0nk/books/537bb028709ab9)を元にした書籍です。AWS や GCP へのデプロイ方法も扱っています。

# FastAPI を学習・実装する上での注意点
+ FastAPI の学習をする前に、Python のモダンな機能である**型ヒント**と**非同期処理**について理解を深めることが重要です。
+ FastAPI は、Python のモダンな（発展途上とも言える）機能を活かした Web フレームワークであり、Python 自体でも変更や拡充がなされています。そのため、**Python のバージョンによりコードの書き方が異なる**ことがあります。
+ FastAPI のバージョンが 0.x.x であるため、**破壊的な変更がなされる可能性がある**ことに留意してください。
+ 高頻度で新機能が追加されており、公式ドキュメントの日本語訳のページが更新されておらず最新のバージョンに対応していない可能性があります。
+ 推奨されるコードの書き方がバージョンにより変わるので、ブログ記事や Youtube動画 などのチュートリアルよりもまずは（英文の）公式ドキュメントを読むことをおすすめします。

これらの注意点の補足等を以下で説明します。

## 型ヒント
型ヒント（Type Hints）は、コード内で変数や関数の引数、返り値などに対して型情報を追加するための仕組みです。これにより、Python のコードの可読性を向上させ、エラーの早期発見にも役立ちます。Python 3.5 で導入され、その後も機能が着実に拡充されています。以下は、型ヒントの簡単な使用例です。
```python
# 型ヒントなし
def greet(name):
    return f"Hello, {name}!"
    
my_name = "Mike"
print(greet(my_name))
# > Hello, Mike!


# 型ヒントあり
def greet(name: str) -> str:
    return f"Hello, {name}!"

my_name: str = "Mike"
print(greet(my_name))
# > Hello, Mike!

# 型ヒントを使用しても、インタプリタは実行時に型チェックを強制しない。
number: int = 100
print(greet(number))
# > Hello, 100!
```
関数 `greet` は、引数 `name` の型は **str** (文字列) であり、返り値の型は **str** であることが期待されます。しかし、型ヒントを使用しても、Python インタプリタは実行時に型チェックを強制しない（ランタイム時には型ヒントが無視され、通常の動的型付けが維持される）ため、`greet(100)`と引数に **int** (整数型) を代入しても実行時にエラーが発生しないことに注意してください。

Python の型ヒントの構文について詳しくは、[typing --- 型ヒントのサポート — Python ドキュメント](https://docs.python.org/ja/3/library/typing.html)や [PEP 484 – Type Hints](https://peps.python.org/pep-0484/) を参考にしてください。
[Python Types Intro - FastAPI](https://fastapi.tiangolo.com/python-types/) では、Pythonのバージョンの違いによる型ヒントの書き方の違いが簡単にまとまっています。

### オートコンプリート
型ヒントは、エディターのオートコンプリート機能を向上させます。先ほどの `greet` 関数では、引数 name の型は str (文字列) と記述しているため、関数内で引数を使用する場合、`name` は文字列のメソッドが自動補完されます。

[Visual Studio Code](https://code.visualstudio.com/) の場合：
![typehint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2801269/2e62bc85-6054-dec0-3ca8-a0bd26ee9e8a.png)

### 型チェッカー
また、型ヒントは型チェッカーによって活用され、開発者に対して型エラーやコードの意図しない利用を早期に検知する手段となります。
例えば、[mypy](https://github.com/python/mypy) などの型チェッカーを用いて、型ヒントに基づくコードの解析やチェックが行えます。これにより、より安全で保守しやすいコードを書くのに寄与します。

### FastAPI における型ヒント
FastAPI は型ヒントの機能を広範囲で使用しています。型ヒント機能を拡張するライブラリである **Pydantic** を利用し、パスパラメータやクエリパラメータ、ボディなどのバリデーション や API ドキュメンテーション機能を提供しています。FastAPI を利用するには、Python の型ヒントの知識が必須です。

:::note
FastAPI で推奨されている型ヒントの書き方がたびたび変更されます。
2023 年 11 月現在、以下のように `Annotated` の使用が推奨されていますが、公式ドキュメントの日本語訳のページのコードは、古いバージョンの型ヒントの書き方が使用されたままです。
> FastAPI added support for Annotated (and started recommending it) in version 0.95.0.
> 
> If you have an older version, you would get errors when trying to use Annotated.
> 
> Make sure you Upgrade the FastAPI version to at least 0.95.1 before using Annotate
> [FastAPI Query Parameters and String Validations - FastAPI](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/)

英文の公式ドキュメントを読むことをおすすめします。
:::
 
## 非同期処理
非同期処理（Asynchronous processing）とは、**あるタスクが終わるのを待たずに、別のタスクを実行する方法**で、処理時間を短縮することができます。

処理時間を短縮する方法は他にも **並列処理(Concurrency)** や **並行処理(Parallelism)** がありますが、それぞれ得意な処理・不得意な処理があります。それぞれの処理方法を組み合わせて使うこともできます。

非同期処理は、ネットワーク通信やデータベースアクセスなどの **I/O バウンド**な処理を行う場合に特に有効です。
例えば、ネットワーク通信では、データの送受信が外部のサーバーとの通信を含むため、ネットワークの遅延などの影響を受けることがあります。
+ 従来の同期処理では、通信が完了するまで待機するため、待ち時間の間に別の処理は実行できません。
+ これに対して非同期処理を使用すると、待ち時間の間に別の処理を進行させることができるため、全体の処理速度の向上や効率的なリソース利用が期待できます。

:::note
**I/O バウンド**な処理とは、外部のリソース（例: ファイル、データベース、ネットワーク）との対話が発生する処理を指します。

数値計算や画像処理などの、**CPU バウンド**な処理（処理の実行時間が CPU の計算速度に依存する処理）とよく比較されます。非同期処理は、CPU バウンドな処理に有効だとは考えられていません。
:::

非同期処理は一般的にイベントループとタスクの組み合わせによって実現されます。
+ イベントループは、非同期処理の処理順序を管理する役割を担います。
+ タスクは、非同期処理の基本的な単位です。

タスクをイベントループに登録することで、イベントループがタスクの実行を制御し、非同期処理が実現されます。

### Python における非同期処理の実装
Pythonでは、標準ライブラリの [`asyncio`](https://docs.python.org/ja/3.12/library/asyncio.html) やサードパーティの非同期処理ライブラリ（例: [`Trio`](https://trio.readthedocs.io/en/stable/)）を利用することで非同期処理を実現できます。これらのライブラリは、イベントループやタスクの管理を提供します。

Python で非同期処理を実装するには、コルーチンを理解する必要があります。

#### コルーチン
[コルーチン(Coroutine)](https://docs.python.org/ja/3/glossary.html#term-coroutine)は、実行を任意の箇所で一時停止し、後で再開できる性質や振る舞いのことを指す概念です。コルーチンは、中断・再開により他のコルーチンと連携して実行されることから、その名前が付けられています（接頭辞の、Co は共同、協調を意味する）。

コルーチンを実装するための手段として、Python では、コルーチン関数とコルーチンオブジェクトがあります。
+ コルーチン関数は、コルーチンの処理内容を定義します。通常、 [`async def`](https://docs.python.org/ja/3/reference/compound_stmts.html#async-def) で定義され、[`await`](https://docs.python.org/ja/3/reference/expressions.html#await) 式で呼び出されます。
+ コルーチンオブジェクトは、コルーチン関数を呼び出すと返されるオブジェクトを指します。実行、一時停止、または再開できる機能をもちます。

:::note
文脈によっては、**コルーチン**が**コルーチン関数**もしくは**コルーチンオブジェクト**のことを指す場合があります。

参考：[コルーチンと Task — Python 3.12.0 ドキュメント](https://docs.python.org/ja/3/library/asyncio-task.html#awaitables)
:::

以下は、コルーチン関数の簡単な例です。
```python
import asyncio

# コルーチン関数
async def main():
    print(f"Started at {time.strftime('%X')}")
    # asyncio.sleep もコルーチン関数
    await asyncio.sleep(1) # 1 秒待機
    print('Between')
    await asyncio.sleep(3) # 3 秒待機
    print(f"Finished at {time.strftime('%X')}")
```
+ コルーチン関数は `async def` で定義します。
+ コルーチン関数は `await asyncio.sleep(1)`のように `await` 式の中で使うことができます（コルーチン関数内でコルーチン関数を利用できます）。
+ `await` はコルーチンの実行を一時停止します。
+ `await` は `async def`で定義された関数内でのみ使用可能です。
+ エントリーポイントである `main` 関数を `asyncio.run` 関数 で実行します。（`main()` のように、単にコルーチン関数を呼び出しただけでは実行できず、コルーチンオブジェクトが返されるだけ）

```python
# main()
# <coroutine object main at 0x7fdfa03969c0>

asyncio.run(main())
# Started at 9:12:24
# Between
# Finished at 9:12:28
```
実行すると、`Started at ...` を出力し、そこから 1 秒待って `Between` を出力し、さらに 3 秒待って `Finished at ...` を出力します。

おそらく、`asyncio.sleep(1)`で待機している間に、`asyncio.sleep(3)`が実行され、処理時間の合計 3 秒であること期待したかもしれませんが、4 秒かかっています。つまり、以上のコードだけでは非同期処理が実装できません。

`asyncio` で非同期処理を実装するには、エントリーポイント（上記の例では `main` 関数）内のコルーチンオブジェクトを `Task` オブジェクトでラップする必要があります。

:::note
async/await 構文を使用したコルーチンは、**ネイティブコルーチン**と呼ばれています。以前は**ジェネレーターベースのコルーチン**もありましたが、Python 3.8 で非推奨になり、Python 3.11 以降は削除されています。

参考：[Coroutines and Tasks — Python 3.10.13 documentation](https://docs.python.org/3.10/library/asyncio-task.html#generator-based-coroutines)
:::

#### Task
**Task オブジェクト**は、コルーチンをラップして、その進行状況を追跡し、管理するために使用されます。[`asyncio.create_task()`](https://docs.python.org/ja/3/library/asyncio-task.html#asyncio.create_task) 関数、あるいは loop.create_task() 関数や ensure_future() 関数を使用して作成されます。手作業での Task オブジェクトの実装は推奨されません。

```python
import asyncio

async def main():
    print(f"Started at {time.strftime('%X')}")
    
    # Task オブジェクトを生成
    task1 = asyncio.create_task(asyncio.sleep(1))
    task2 = asyncio.create_task(asyncio.sleep(3))
    
    # await asyncio.sleep(1)
    await task1
    print('Between')
    # await asyncio.sleep(3)
    await task2
    
    print(f"Finished at {time.strftime('%X')}")


asyncio.run(main())
# Started at 9:18:34
# Between
# Finished at 9:18:37
```
+ `asyncio.create_task` はコルーチンオブジェクトを引数に取り、`Task` オブジェクト を返します。

実行すると、`Started at ...` を出力し、そこから 1 秒待って `Between` を出力し、さらに 2 秒待って `Finished at ...` を出力します。**前回よりも 1 秒早くなっています**。

非同期処理を用いることで、並行して複数の処理（`task1` と `task2`）を進行させ、待ち時間が発生しても別の処理が進行できることが確認できます。

:::note
今回のコードでは await 式の中で **Task オブジェクト**を、前回のコードでは**コルーチンオブジェクト**を使用しました。

このように await 式の中で使うことができるオブジェクトを [awaitable](https://docs.python.org/ja/3/glossary.html#term-awaitable) オブジェクトと言います。

参考：[コルーチンと Task — Python 3.12.0 ドキュメント](https://docs.python.org/ja/3.12/library/asyncio-task.html#awaitables)

:::

#### イベントループ
イベントループは、非同期処理の中でタスク間の切り替えを担当します。
`asyncio.run` 関数は、この関数内で新しいイベントループを作成します。

:::note
`asyncio.run` 関数は通常、プログラムのメインエントリポイントとして使用され、呼び出し毎に新しいイベントループが作成され、最終的にはそのイベントループがクローズされます。したがって、この関数は理想的にはプログラム内で一度だけ呼び出されるべきです。
:::


### Web 開発と非同期処理
近年の Web 開発において、非同期処理が注目を浴び、**[ASGI（Asynchronous Server Gateway Interface）](https://asgi.readthedocs.io/en/latest/)** 仕様に基づいた新しいフレームワークが登場しています。ASGI は、Python の Web アプリケーションやサーバーが非同期処理をサポートするための仕様で、非同期なリクエスト処理や `WebSocket` などのプロトコルをサポートし、高い性能と柔軟性を提供します。

`asyncio` は、FastAPI のような ASGI 仕様に準拠した Web アプリケーションフレームワークの基盤として機能します。

一方で、Django や Flask などの既存の Web フレームワークは、非同期処理の機能が導入される前に開発されました。これらのフレームワークは、従来の同期的なリクエスト処理に適した **[WSGI（Web Server Gateway Interface）](https://wsgi.readthedocs.io/en/latest/)** 仕様を実装しています。

:::note
現在は、**Django** や **Flask** でも **ASGI** 仕様をサポートしています。
+ [How to deploy with ASGI - Django](https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/)
+ [ASGI - Flask](https://flask.palletsprojects.com/en/latest/deploying/asgi/)
:::

### FastAPI における非同期処理
FastAPI では、[非同期処理（async / await）](https://fastapi.tiangolo.com/async/)を使用せずに Web API の開発を行うことも可能です。しかし、FastAPIの強みである高いパフォーマンスを最大限に活かすためには、非同期処理の使用を検討することをおすすめします。

ただし、非同期処理はパフォーマンスの向上をもたらす一方で、誤用すると逆にパフォーマンスが低下する可能性があり、また、非同期処理を過度に使用すると、コードの複雑さが増す可能性があることに注意してください。

非同期処理の難しい部分は、FastAPI 側が裏側で処理してくれるため、非同期処理を使うべきかどうかの判断と `async/await` の使い方の理解が FastAPI を使用する上で重要です。

公式ドキュメントでは、[Concurrency and async / await](https://fastapi.tiangolo.com/async/) のページで、非同期(`async def`) か同期(`def`) かどちらで関数を定義すべきかの判断基準が書かれています。簡単に以下でまとめます。
+ サードパーティライブラリで、`await` を使用して呼び出すように指示されている場合は、`async def`（非同期）を使う。
    > If you are using third party libraries that tell you to call them with await
+ データベースアクセスやファイル操作など何かと通信するサードパーティライブラリで `await`を使用するサポートがない場合は、`def`（同期）を使う。
    > If you are using a third party library that communicates with something (a database, an API, the file system, etc.) and doesn't have support for using await, (this is currently the case for most database libraries), then declare your path operation functions as normally, with just def
+ 他の何かと通信したり、応答を待つ必要がないのであれば、`async def`（非同期）を使う。
    > If your application (somehow) doesn't have to communicate with anything else and wait for it to respond, use async def.
+ よくわからない場合は、`def`（同期）を使う。
    > If you just don't know, use normal def.

## その他機能
FastAPIは、型ヒントや非同期処理に加えて、**依存性注入(Dependency injection)** や **OpenAPI スキーマの自動生成**、**ミドルウェア**、**バックグラウンドタスク**などの機能を提供しています。これらの機能を活用することで、Web API 開発をより効率的に進めることができます。最初は難しく感じるかもしれませんが、これらの機能を理解し導入することで、コードの可読性や保守性を向上させることができます。

**参考:**
[Dependencies - FastAPI](https://fastapi.tiangolo.com/tutorial/dependencies/)
[First Steps: Interactive API docs - FastAPI](https://fastapi.tiangolo.com/tutorial/first-steps/#interactive-api-docs)
[Background Tasks - FastAPI](https://fastapi.tiangolo.com/tutorial/background-tasks/)
[Middleware - FastAPI](https://fastapi.tiangolo.com/tutorial/middleware/)
## バージョンが 0.x.x
> 高頻度で新機能が追加され、定期的にバグが修正され、実装は継続的に改善されています。
> これが現在のバージョンがいまだに 0.x.x な理由であり、それぞれのバージョンは**破壊的な変更がなされる可能性**があります。
> 
> 参考: [FastAPIのバージョンについて - FastAPI](https://fastapi.tiangolo.com/ja/deployment/versions/)

公式ドキュメントにも記述されていますが、メジャーバージョン が 0 で、初期開発用のため、いつでも変更される可能性があります。1.0.0 未満の全てのバージョンは破壊的な変更が加わる可能性があり、注意が必要です。
参考: [Semantic Versioning 2.0.0 | Semantic Versioning](https://semver.org/)

# まとめ
FastAPI は比較的新しい Web フレームワークであるため、 Django や Flask に比べて情報量は少ないです。しかし、公式ドキュメントや Medium などのサイト、GitHub のプロジェクトを活用することで、（ほとんど英語ですが）十分に学習することができます。

FastAPI を学習・実装する上での注意点として、
+ FastAPI を使用する前に、**型ヒント**や**非同期処理**の基礎を抑えること
+ 他の資料よりもまずは公式ドキュメントの**英文**のページを読むこと
+ 外部的な要因でドキュメント化が追いついていない内容（依存ライブラリのバージョン変更への対応など）は Medium などのブログ記事が役に立つこと

が挙げられます。

すでにご存知のサイトも多いと思いますが、参考になりましたら幸いです。