PythonとDockerで作るAPI　FastAPIを最速で理解する

# はじめに
本記事はFastAPIでバックエンドを開発する方法について記載しています。

[**FastAPI**](https://en.wikipedia.org/wiki/FastAPI)は、PythonでAPIを開発するためのモダンで高速(高性能)なWebフレームワークです。

Pydanticによる型ヒントを使用したデータの検証や、OpenAPIドキュメントを自動的に生成することができます。
また非同期プログラミングをサポートし、SQLAlchemyやTortoise-ORMと互換性、[依存性の注入（DI）](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5)を考慮した実装も可能です。

Pythonを用いたマイクロサービスのバックエンド開発などには、最適なソリューションです。

https://github.com/tiangolo/fastapi

## FastAPIを使用する理由
なぜFastAPIを使用するかについて、FastAPIの主な特徴について以下に記載しています。

### シンプル
後述するチュートリアルの通りに数行でAPIを開発することができます。

デフォルトの状態で適切に最適化されていますが、オプションを使用してパラメータを調整することもできます。

### 高速
Webフレームワークの性能評価の指標を算出している**Web Framework Benchmarks**より、[2022-07-19 Round 21](https://www.techempower.com/benchmarks/#section=data-r21)をご参照ください。（本記事執筆時点で最新のRound）

性能を基準に評価する際は、上記サイトは参考になると思います。

### 標準化
FastAPIは、[OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification)を標準として、[JSON Schema](https://json-schema.org/)と完全互換性があります。

**OpenAPI Specification**（以下、OAS）は、[Linux Foundation](https://ja.wikipedia.org/wiki/Linux_Foundation)共同プロジェクトである[OpenAPI Initiative](https://www.openapis.org/)内のコミュニティ主導のオープン仕様です。またOpenAPI自体がJSONスキーマに基づいています。

APIの標準およびプログラミング言語に依存しないインターフェイスによって、HTTPベースのコミュニケーションを簡素化します。

## APIについて学ぶ
システム開発におけるソフトウェアの結合は、RPC、SOAP Webサービス、Web APIなどテクノロジーの進化によって、抽象化が進みました。

FastAPIもAPIを実現するための手段の一つですが、正しく使いこなすためには、[Representational State Transfer（以下、REST）](https://ja.wikipedia.org/wiki/Representational_State_Transfer)の概念や、API設計の考え方について理解していることが重要です。

本記事はFastAPIを題材にしているので深くは掘り下げませんが、APIの概念につて以下に記載しています。

### APIとは
**API**とは、[アプリケーションプログラミングインタフェース](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9)の略称です。

APIには、以下のような種類があり、それぞれ用途が異なります。

- スマートフォンでカメラを起動して、写真を撮影するなどのハードウェアAPI
- Windows OS内部などで使用されるOS API
- 外部に公開していない内部利用を目的とした、インターナルAPI
- 外部から利用可能なパブリックAPI（Open APIとも呼ばれ、YAMLまたはJSONで記述）

APIを設計するということは、利用するユーザーとコンポーネント間のインターフェースを定義するということです。APIはシステムにおける内部の実装を**抽象化**したものすぎません。

:::note
現実世界で例えると、ATMでお金を下ろす場合、どのような仕組みで動いているかについて気にする人は少ないと思います。
興味があるのは、どこにカードを入れて、どのような操作をしたらお金を預けたり、引きだしができるなど、インターフェースの仕様ではないでしょうか。
:::

### API設計の重要性
API設計がなぜ重要なのか考えてみましょう。

例えば、APIが適当に設計されている場合は、以下のような問題が発生する可能性があります。

- APIが正しく使わなれないため、十分に活用されない
- APIが分かりにくいことによるユーザーからの問い合わせや、クレームが増えることによる運用コストの増加
- 設計の欠陥によって、データ漏洩などセキュリティに関わる脆弱性に繋がる

:::note
API設計は、原理原則に基づいたテクノロジーに対する理解や、ユーザー側からの視点など多岐にわたります。
:::

### APIは誰のためのものか
APIは**目標を達成するための手段**として、ユーザーが欲しいものを提供することが求められます。

例えば、リモコンはシンプルです。
テレビだろうがオーディオだろうが、大体は手に取ることができるサイズで、同じようなボタンの配置をしていて、何を目的としているかがはっきりと分かります。

API設計では、APIを使用するユーザーから見て、以下に示すような配慮が必要不可欠です。

- そのAPIで何ができて、どのようにして使用するのか
- APIをリクエストするための方法
- APIが返すレスポンスの期待値、例外発生時のエラーメッセージ

### REST API
**REST API**（またはRESTful API）は、RESTアーキテクチャスタイルの制約に従って策定されたAPIのことです。

2000年に[Roy Fielding](https://en.wikipedia.org/wiki/Roy_Fielding)によって発表された、以下論文によってその設計思想が広まりました。

- [Architectural Styles and the Design of Network-based Software Architectures](https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm)

RESTfulの原則は以下の通りです。

- クライアント、サーバー、リソースからなる**クライアント/サーバー**アーキテクチャで、要求はHTTP経由で処理される
- **ステートレス**な通信は各要求を独立し、分離する
- クライアントは、サーバーのレスポンスを**キャッシュ**することができる
- **統一インターフェース**は以下の要件を満たすこと
    - 要求されるリソースは識別可能
    - クライアントは表現を用いてリソースを操作できる
    - 自己記述的メッセージによって、クライアントが情報をどのように処理すべきかを記述する十分な情報が含まれている
    - [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS)
- 要求された情報は**階層化システム**である
- **コードオンデマンド**はサーバーからクライアントに実行可能コードを送信することで、クライアントは機能を拡張できる

### OpenAPI Specification
**OAS**は、REST API記述フォーマットです。

以前は**Swagger Specification**と呼ばれていたもので、2015年11月にOAIに寄贈され、2016年1月に現在の運用になっています。OpenAPIの詳細については、[What Is OpenAPI?](https://swagger.io/docs/specification/about/)をご参照ください。

OASドキュメントはテキストファイルで、リソース、アクション、パラメータ、レスポンスを記述します。
[Swagger Editor](https://editor.swagger.io/)を使用して編集することができます。

OASを使用することの最大のメリットは、単純に開発を効率よく進めることができるの一言に尽きると思います。

例えば、フロントエンドはバックエンド側の実装を待たなくても、OASを使用してモックサーバを起動してフロントエンドの開発スピードを上げることができます。

:::note
OAS自体がドキュメントになるので、エクセルなどによるドキュメント管理が不要です。
またOASに準じて宣言的に書くことは、曖昧な記述がなくなるため、仕様が明確になります。
:::

## FastAPI開発
APIの概念がある程度理解できた段階で、FastAPIの開発を行ないましょう。

前提として、本記事の環境は以下の通りです。

| 項目 | バージョン等 |
|:-----------|:------------|
| OS         | macOS       | 
| Python     | 3.9.4       | 
| fastapi    | 0.88.0      |
| Docker     | 20.10.6     | 

### チュートリアル
チュートリアルの[最初のステップ](https://fastapi.tiangolo.com/ja/tutorial/first-steps/)では、FastAPIの基本的な機能について学ぶことができます。

早速、チュートリアルを行うために、Pythonの仮想環境で試してみましょう。
Pythonの仮想環境はvenvを使用して作成します。

まずは仮想環境を作成するために、ターミナルなどを起動して、以下のコマンドを実行します。
`＜Dir＞`には任意のディレクトリ名を指定します。

`$ python3 -m venv ＜Dir＞`
`$ source ＜Dir＞/bin/activate`

仮想環境構築後、以下のコマンドを実行してすべてのオプションの依存関係と機能をインストールします。

`$ pip3 install "fastapi[all]"`

pipでインストール完了後、以下のコマンドを実行すると、必要なパッケージについてインストールされていることが確認できます。

`$ pip3 list`

<details><summary>出力例</summary>

```console
Package           Version
----------------- ---------
anyio             3.6.2
certifi           2022.12.7
click             8.1.3
dnspython         2.2.1
email-validator   1.3.0
fastapi           0.88.0
h11               0.14.0
httpcore          0.16.3
httptools         0.5.0
httpx             0.23.1
idna              3.4
itsdangerous      2.1.2
Jinja2            3.1.2
MarkupSafe        2.1.1
orjson            3.8.3
pip               20.2.3
pydantic          1.10.2
python-dotenv     0.21.0
python-multipart  0.0.5
PyYAML            6.0
rfc3986           1.5.0
setuptools        49.2.1
six               1.16.0
sniffio           1.3.0
starlette         0.22.0
typing-extensions 4.4.0
ujson             5.6.0
uvicorn           0.20.0
uvloop            0.17.0
watchfiles        0.18.1
websockets        10.4
```
</details>

次に`main.py`を作成した後に、以下のコマンドを実行して`uvicorn`で起動します。

- main.py

```python
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}
```

`$ vi main.py`
`$ uvicorn main:app --reload`

```console
INFO:     Will watch for changes in these directories: ['/Users/python/Desktop']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [72817] using WatchFiles
INFO:     Started server process [72819]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

```

上記のような出力を確認後、Chrome ブラウザを起動します。
`http://127.0.0.1:8000/`にアクセスすると、`{"message":"Hello World"}`のJSON形式のメッセージが確認できます。
またデベロッパー ツールを使用すると、レスポンスヘッダからuvicornなどが確認できます。

![スクリーンショット 2022-12-21 0.19.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/5f88f231-9125-24cd-3173-107e064fe442.png)

`http://127.0.0.1:8000/docs`にアクセスすると、自動生成されたSwagger UIで提供されているインタラクティブなドキュメントが確認できます。

![スクリーンショット 2022-12-21 0.37.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/87b65941-a1b6-eb55-f46d-ff13ca2aae85.png)

ここまで5分もかからないと思いますが、たったの数行でAPIの基礎となる土台を作ることができました。
引き続き、チュートリアルのユーザーガイドを参照することで、基本的なAPI開発の実装に関して学ぶことができます。

### Dockerを使用したAPI開発
Dockerを使用してAPI開発を行う例について以下に記載します。

ソースをマウントしている状態になるため、コードの変更は自動的に行われることで、柔軟に開発を行うことができます。

以下ではAPIの動きを理解するために、ルールティングの設定と、セキュリティ対策としてAPIキーでバックエンドを保護する実装を行っています。

#### 環境構築
ソースを格納しているカレントディレクトリ（.）を起点にした、本記事のサンプルコードは以下の構成です。

```text
.
├── backend
│   ├── Dockerfile
│   └── api
│       ├── main.py
│       ├── routers
│       │   └── test.py
│       └── util
│           └── util.py
└── docker-compose.yaml

```
- docker-compose.yaml

```yaml
version: '3'

services:
  
  demo-api:
    build:
      context: "./backend"
      dockerfile: "Dockerfile"
    ports:
      - 8000:8000 
    volumes:
      - ./backend/api:/src
    environment:
      - ENVIRON

```


- Dockerfile

```dockerfile
FROM python:3.9-buster
ENV PYTHONUNBUFFERED=1

WORKDIR /src
COPY api ./

RUN pip3 install --upgrade pip
RUN pip3 install fastapi
RUN pip3 install "uvicorn[standard]"
RUN pip3 install requests

ENTRYPOINT ["uvicorn", "main:app", "--host", "0.0.0.0", "--reload"]
```

:::note
FROMで指定している`python:3.9-buster`は、Debian 10を基にしたイメージです。
またdockerhubに格納されているpythonのイメージは[Official Imagesのpython](https://hub.docker.com/_/python)から確認できます。名前に`slim`が付いているイメージは軽量です。
:::

- main.py

```python
from fastapi import Depends, FastAPI, HTTPException, Security
from fastapi.security.api_key import APIKeyHeader, APIKey
from starlette.status import HTTP_403_FORBIDDEN

from routers import test
from util import util

correct_key: str = util.get_apikey()
api_key_header = APIKeyHeader(name='Authorization', auto_error=False)

async def get_api_key(
    api_key_header: str = Security(api_key_header),
    ):
    if api_key_header == correct_key:
        return api_key_header
    else:
        raise HTTPException(
            status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
        )

app = FastAPI()
app.include_router(test.router, dependencies=[Depends(get_api_key)], tags=["Test"])

@app.get("/")
def read_root():
    return {"status": "ok"}
```

- test.py

```python
import os
from typing import Dict, List, TypedDict

from fastapi import APIRouter, Depends, HTTPException

ENVIRON = os.environ['ENVIRON']

router = APIRouter()


@router.get("/v1/list/fruit")
async def get_list_fruit() -> Dict:

    if ENVIRON == 'dev':
        response = {
                    'name': 'apple',
                    'price': '100',
                   }
    elif ENVIRON == 'prd':
        response = {
                    'name': 'orange',
                    'price': '100',
                   }                   
        
    return response

```

- util.py

```python
def get_apikey() -> str:
    response = "dummy"

    return response

```

:::note warn
本記事ではサンプルになるので便宜上、上記のような表現にしています。
実際に実装する場合、APIキーの値などはハードコーディングせずに、シークレット管理システムなどから取得するようにしましょう。
:::

上記コードを配備後、以下のコマンドを実行してデプロイします。

`$ export ENVIRON='dev'`
`$ docker-compose up -d`

<details><summary>出力例</summary>

```console
Docker Compose is now in the Docker CLI, try `docker compose up`

Building demo-api
[+] Building 40.3s (13/13) FINISHED                                                                                                 
 => [internal] load build definition from Dockerfile                                                                           0.0s
 => => transferring dockerfile: 306B                                                                                           0.0s
 => [internal] load .dockerignore                                                                                              0.0s
 => => transferring context: 2B                                                                                                0.0s
 => [internal] load metadata for docker.io/library/python:3.9-buster                                                           2.5s
 => [auth] library/python:pull token for registry-1.docker.io                                                                  0.0s
 => [internal] load build context                                                                                              0.0s
 => => transferring context: 3.81kB                                                                                            0.0s
 => [1/7] FROM docker.io/library/python:3.9-buster@sha256:5e28891402c02291f65c6652a8abddedcb5af15933e923c07c2670f836243833    23.9s
 => => resolve docker.io/library/python:3.9-buster@sha256:5e28891402c02291f65c6652a8abddedcb5af15933e923c07c2670f836243833     0.0s
 => => sha256:5e28891402c02291f65c6652a8abddedcb5af15933e923c07c2670f836243833 988B / 988B                                     0.0s
 => => sha256:c7c50787e2da71205402343dd1233b3ca6ebe70c7e790f6ba7d856b81b893200 50.45MB / 50.45MB                               2.7s
 => => sha256:821dc261e045ba09851c52ed28be3b9ecc9fe8c923fba05854ab0fd2fa38ceca 10.00MB / 10.00MB                               1.9s
 => => sha256:a21bc1f5190fa4f5d13dbdec6adc8d65ae2d3025411ecab367f616831dd570cb 2.22kB / 2.22kB                                 0.0s
 => => sha256:509942a964375108ec41c5663ed58a9d5f52f49336a113f29688ce599b9b0bee 8.51kB / 8.51kB                                 0.0s
 => => sha256:aff359114acb7d843de09375f87669fffb0abd1125c16f96431bc3c4173b48f8 7.86MB / 7.86MB                                 1.4s
 => => sha256:3790459d63d5fdf148a092f1857010796dfa217329491b66a015a40de92f3db9 51.87MB / 51.87MB                               4.8s
 => => sha256:c09b016fff62bb8002f99d7e639c0a2c0ce5a774796aae1e446e99deeb1ac01d 191.89MB / 191.89MB                            10.5s
 => => sha256:6aa81cf5366ed8cc72bf3e9865b2a33a023c602226b7c83b584b33fa1c443246 6.15MB / 6.15MB                                 3.4s
 => => extracting sha256:c7c50787e2da71205402343dd1233b3ca6ebe70c7e790f6ba7d856b81b893200                                      3.6s
 => => sha256:4c2f5a834e3b5c8b7a0195ef5d22bc8480e9879cd4c38b93dcd0df9bbe559ad6 19.92MB / 19.92MB                               5.0s
 => => sha256:1d79b8cadecb9bf336caef07c858791fef8942063d9da716cee84e03aa9f5e00 233B / 233B                                     5.1s
 => => sha256:146e10e712ef0ac2ba6baf3895242f97e3dd52e72f02bbdbbb21700f8362391c 2.88MB / 2.88MB                                 5.7s
 => => extracting sha256:aff359114acb7d843de09375f87669fffb0abd1125c16f96431bc3c4173b48f8                                      0.6s
 => => extracting sha256:821dc261e045ba09851c52ed28be3b9ecc9fe8c923fba05854ab0fd2fa38ceca                                      0.5s
 => => extracting sha256:3790459d63d5fdf148a092f1857010796dfa217329491b66a015a40de92f3db9                                      3.3s
 => => extracting sha256:c09b016fff62bb8002f99d7e639c0a2c0ce5a774796aae1e446e99deeb1ac01d                                      9.3s
 => => extracting sha256:6aa81cf5366ed8cc72bf3e9865b2a33a023c602226b7c83b584b33fa1c443246                                      0.4s
 => => extracting sha256:4c2f5a834e3b5c8b7a0195ef5d22bc8480e9879cd4c38b93dcd0df9bbe559ad6                                      0.9s
 => => extracting sha256:1d79b8cadecb9bf336caef07c858791fef8942063d9da716cee84e03aa9f5e00                                      0.0s
 => => extracting sha256:146e10e712ef0ac2ba6baf3895242f97e3dd52e72f02bbdbbb21700f8362391c                                      0.3s
 => [2/7] WORKDIR /src                                                                                                         0.3s
 => [3/7] COPY api ./                                                                                                          0.1s
 => [4/7] RUN pip3 install --upgrade pip                                                                                       4.7s
 => [5/7] RUN pip3 install fastapi                                                                                             3.6s
 => [6/7] RUN pip3 install "uvicorn[standard]"                                                                                 2.9s
 => [7/7] RUN pip3 install requests                                                                                            1.8s
 => exporting to image                                                                                                         0.4s
 => => exporting layers                                                                                                        0.4s
 => => writing image sha256:5f62e853a8f8569a6c75bc7b4a59f9912e855a890022b3c2e9d647808d840626                                   0.0s
 => => naming to docker.io/library/sample_demo-api                                                                             0.0s
WARNING: Image for service demo-api was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating sample_demo-api_1 ... done
```

</details>

以下のコマンドを実行し、バックエンドのコンテナが正常に起動できていることを確認します。

`$ docker logs -f sample_demo-api_1`

```console
INFO:     Will watch for changes in these directories: ['/src']
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [1] using WatchFiles
INFO:     Started server process [8]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
```

#### ルーティング確認
デプロイ完了後、Chrome ブラウザから`http://localhost:8000/`にアクセスします。

レスポンスのメッセージ及び`Status Code`は`200 OK`が返ってきていることが確認できます。

![スクリーンショット 2022-12-23 0.49.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/39c23375-90a2-5e14-8071-299bae8f914f.png)

次にルーティング設定している、`http://localhost:8000/v1/list/fruit`にアクセスします。
レスポンスのメッセージ及び`Status Code`は`403 Forbidden `が返ってきていることが確認できます。

![スクリーンショット 2022-12-23 0.49.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/8d6b5302-3534-67dc-07fb-3b7ea7eed32d.png)

APIキーで保護しているため、このままではアクセスすることはできません。
HTTPリクエストのヘッダに、APIキーを付与してリクエストを送る必要があります。

:::note
`app.include_router()`関数を使用することで、APIのエンドポイントが追加できることが分かります。
ルーティングの仕様など、他のWebフレームワークからインスピレーションを受けていることが[代替ツールから受けたインスピレーションと比較](https://fastapi.tiangolo.com/ja/alternatives/)に記載されています。:point_up:
:::

#### APIキーによるバックエンドの保護
`http://127.0.0.1:8000/docs`にアクセスし、Swaggerの画面からPIキーを設定してみましょう。

認証を行うために、下三角の箇所を押します。

![スクリーンショット 2022-12-23 0.59.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/99403c0d-48a0-9a3a-2db0-ff05fdfa8168.png)

「Try it out」を押します。

![スクリーンショット 2022-12-23 0.59.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/06c6b9c5-d892-df86-7365-79ceb0a3a823.png)

「Excute」を押すことで、リクエストを送ることができますが、この状態では先ほど同じ結果になるので、右上の鍵マークのアイコンを押します。

![スクリーンショット 2022-12-23 1.00.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/ad7aa9e7-346e-a970-b131-05d4d63ba76c.png)

「Available authorizations」の画面が表示されるので、「Value」にはAPIキーの値を入力して、「Authorize」を押します。

![スクリーンショット 2022-12-23 1.00.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/313fa06a-bdf3-7345-ae57-abf7c0bd295b.png)

「Close」を押します。

![スクリーンショット 2022-12-23 1.00.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/3da9f0fc-41d6-b9eb-b492-7d6bc534bf27.png)

再度、「Excute」を押します。
今度は、APIのレスポンスが確認できます。

![スクリーンショット 2022-12-23 1.00.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/17cbd607-dc88-312e-0678-1396c8be399f.png)

:::note
APIキーを保護する仕組みを実現しているのは、`dependencies=[Depends(get_api_key)]`による箇所です。
依存性の注入の詳細については、[Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/)をご参照ください。
:::

#### 環境変数を変えてデプロイする
`docker-compose.yaml`の`environment`で環境変数を設定しています。

環境変数を変えてデプロイしてみましょう。
以下のコマンドを実行して、一度コンテナを削除します。

`$ docker rmi -f  sample_demo-api`

```console
Untagged: sample_demo-api:latest
```

環境変数`ENVIRON`を`prd`に設定してデプロイします。

`$ export ENVIRON='prd'`
`$ docker-compose  up -d`

dockerの仕組みとして`CACHED`と表示されている通りに、2回目以降はキャッシュを利用してビルドしていることが確認できます。

<details><summary>出力例</summary>

```console
Docker Compose is now in the Docker CLI, try `docker compose up`

Building demo-api
[+] Building 2.1s (13/13) FINISHED                                                                                                  
 => [internal] load build definition from Dockerfile                                                                           0.0s
 => => transferring dockerfile: 37B                                                                                            0.0s
 => [internal] load .dockerignore                                                                                              0.0s
 => => transferring context: 2B                                                                                                0.0s
 => [internal] load metadata for docker.io/library/python:3.9-buster                                                           1.8s
 => [auth] library/python:pull token for registry-1.docker.io                                                                  0.0s
 => [1/7] FROM docker.io/library/python:3.9-buster@sha256:5e28891402c02291f65c6652a8abddedcb5af15933e923c07c2670f836243833     0.0s
 => [internal] load build context                                                                                              0.0s
 => => transferring context: 485B                                                                                              0.0s
 => CACHED [2/7] WORKDIR /src                                                                                                  0.0s
 => CACHED [3/7] COPY api ./                                                                                                   0.0s
 => CACHED [4/7] RUN pip3 install --upgrade pip                                                                                0.0s
 => CACHED [5/7] RUN pip3 install fastapi                                                                                      0.0s
 => CACHED [6/7] RUN pip3 install "uvicorn[standard]"                                                                          0.0s
 => CACHED [7/7] RUN pip3 install requests                                                                                     0.0s
 => exporting to image                                                                                                         0.0s
 => => exporting layers                                                                                                        0.0s
 => => writing image sha256:5f62e853a8f8569a6c75bc7b4a59f9912e855a890022b3c2e9d647808d840626                                   0.0s
 => => naming to docker.io/library/sample_demo-api                                                                             0.0s
```

</details>

今回は`ENVIRON`が`prd`になるため、responseは先ほどとは別の結果が返ってきているのが確認できます。

![スクリーンショット 2022-12-23 1.02.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/c85f1490-1a96-10a7-aa48-2bf9e504554e.png)

:::note
ビルド時のキャッシュを削除する場合は、`docker builder prune`コマンドを実行します。
なお、ビルド時にキャッシュを利用しないようにするためには、`docker-compose build --no-cache`を実行します。
:::

### 開発に役立つツール
**Postman**はAPI開発の必需品です。

いくつかの機能がありますが、基本機能であるCollections機能は無料で利用できるので個人開発に重宝します。
複数人のチームでシナリオを共有する場合などは、有料プランの検討が必要になってくると思います。

[Postman](https://www.postman.com/)の公式サイトからデスクトップアプリがダウンロードできます。

https://github.com/postmanlabs

- 実行例

![スクリーンショット 2022-12-23 1.26.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/fb0a5a50-7f46-dd60-947b-1a9922afe95f.png)

# おわりに
[歴史、設計、そしてこれから](https://fastapi.tiangolo.com/ja/history-design-future/)の通りに、FastAPIの歴史はまだ旅路の途中です。

2018年12月にリリースされているので他のWebフレームワークと比べると、まだ歴史が浅く、不足している機能もあると思いますが、Netflixなどでも使用されています。

[Stack Overflow 2021 Developer Survey](https://insights.stackoverflow.com/survey/2021/#most-loved-dreaded-and-wanted-webframe-love-dread)では、3番目に人気のあるWebフレームワークとなっています。

## 参考
### RESTful API
- [RESTful API とは](https://aws.amazon.com/jp/what-is/restful-api/)
- [RESTful Web API の設計](https://learn.microsoft.com/ja-jp/azure/architecture/best-practices/api-design)

### FastAPI
- [FastAPI](https://fastapi.tiangolo.com/)
- [Netflixが危機管理オーケストレーションツールをオープンソースとして公開](https://www.infoq.com/jp/news/2020/05/netflix-open-source-dispatch/)
- [Introducing Dispatch](https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072)