Python: DockerでFastAPIによるRESTAPI環境を起動するサンプル一式

https://github.com/yagrush/myfastapi

FastAPIをDockerで実行するテンプレート一式です。
PythonとFastAPIを使ったRESTAPIサーバがすぐに試せます。
また、pytestを使ってAPIのテストコードをDockerで実行するサンプルも付いています。


## 必要環境

Windows10, Mac, Linux などに Docker + docker-composeとGitをインストールしてください。

--------

### Windows10

WSL2でUbuntuなどのLinux仮想環境を作って、そこにDocker + docker-composeをインストールするのがおすすめです。

* マイクロソフト WSLの公式マニュアル 
https://docs.microsoft.com/ja-jp/windows/wsl/

* WSL2 Ubuntu 20.04 にDocker, docker-composeを入れる
https://qiita.com/yagrush/items/f12563eef6a1dd77cd4d

--------

### Mac

Docker Desktopなどをインストールしてください。

https://www.docker.com/

<br />

## FastAPI Dockerコンテナの起動

### WindowsでWSL2を使用する場合

まず、スタートメニューなどから「Ubuntu 20.04 on Windows」などをクリックしてWSL2を起動してください。
WSL2内のLinuxシェル画面が起動したら、それを使ってこの後の作業を続けてください。

### この一式を、Gitを使ってダウンロードします

```bash
git clone https://github.com/yagrush/myfastapi.git
```


### ディレクトリの中に入ります

```bash
cd myfastapi
```


### FastAPIコンテナを起動しましょう


```bash
docker-compose up --build -d
```


### APIを叩く前にまずは、APIドキュメントにアクセスしてみます

ブラウザで↓を開きます。
http://localhost:8000/docs<br />

* FastAPIは自動的にAPIドキュメントを生成してくれます。
FastAPIを起動中に利用することができます。
ここからAPIを実際に実行することも可能です。

![api_docs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/8650e2cd-7fdc-d7e8-356a-5f11b91fbbf2.png)



### APIをためしに実行してみます

* root
GET `/`
を実行してみましょう。
これはURLでいうところの http://localhost:8000/ です。

![api_test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/af6c4068-c7e6-2247-e685-2272d9fff1a2.png)
 ↓
![api_test2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/23a1417d-eeb4-58b3-747f-e107bca4c07e.png)
 ↓
![api_test3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/e00844fa-1885-f20c-5826-157243668fa0.png)

このAPIは、固定のJSON
```json
{
  "message": "Hello World"
}
```
を返すAPIですが、その通りちゃんと返ってきたようです。

### ためしに、ブラウザからもアクセスしてみます

↓をブラウザで開きます。
http://localhost:8000/
↓
![api_test4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/d16eaadb-8685-db13-ccef-02465fdafdf3.png)

無事、

```json
{
  "message": "Hello World"
}
```
が返ってきました。

## FastAPI Dockerコンテナの終了

終了したいときは、`docker-compose.yml`があるディレクトリで↓のコマンドを実行します。

```
docker-compose down
```

## pytestをDockerで実行する

pytestは、ソースコードの健全性をテストするソースコードを実行するプログラムです。

これもDockerコンテナとして実行してみましょう。
そうすれば、手元のMacやWindowsやWSLにPythonやpytestをインストールする必要が無くなります。
今回は、Dockerとpytest設定ファイルをご用意してありますので、

* pytest-docker-compose.yml
* pytest.Dockerfile
* src/pytest.ini

早速実行してみましょう。
実行すると、`src/pytest.ini` に書いてある通りにテストコードを探して自動的に実行してくれます。

今回のプロジェクトには、テストコード `src/tests/api/v1/test_user.py` があります。

内容は、API `/v1/user/abcde` を実行して、期待通りのレスポンス

```json
{
  "user_name": "abcde 一郎さん",
  "age": 12
}
```

が返ってくるかどうかをチェックするテストコードです。

```python
from fastapi.testclient import TestClient
from prog import app

client = TestClient(app, base_url="http://localhost:8100")


def test_get():
    user_id = "abcde"
    res = client.get(f"/v1/user/{user_id}")
    assert res.status_code == 200
    assert res.json()["user_name"] == f"{user_id} 一郎さん"
    assert res.json()["age"] == 12
```

`pytest`Dockerコンテナを起動します。

```bash
$ docker-compose -f  pytest-docker-compose.yml up
```
（このコンテナは、pytestが完了次第終了します。）

↓ このように表示されればOKです。

```bash
WARNING: Found orphan containers (myfastapi) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
Starting myfastapi-pytest ... done
Attaching to myfastapi-pytest
myfastapi-pytest | ============================= test session starts ==============================
myfastapi-pytest | platform linux -- Python 3.10.4, pytest-7.1.1, pluggy-1.0.0 -- /usr/local/bin/python
myfastapi-pytest | cachedir: .pytest_cache
myfastapi-pytest | rootdir: /src, configfile: pytest.ini, testpaths: ./tests
myfastapi-pytest | plugins: anyio-3.5.0
myfastapi-pytest | collecting ... collected 1 item
myfastapi-pytest |
myfastapi-pytest | tests/api/v1/test_user.py::test_get API:v1/user/abcde 所要時間：0.0003070831298828125秒
myfastapi-pytest | PASSED
myfastapi-pytest |
myfastapi-pytest | ============================== 1 passed in 0.15s ===============================
myfastapi-pytest exited with code 0
```