Python の Web Framework！ FastAPI について調べてみた！

![fastapi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659561/0cbb026f-2d06-50ab-7307-5006954bed60.png)

# はじめに

Python の Web Framework には、Django や Flask などいくつかあります。

Django は、フロントエンドのコードもバックエンドのコードも１つで管理出来たり
Flask は、簡単にコードを書くことができるなどそれぞれ一長一短の特徴があります。（ここにあげたものだけではありませんが、、）

今回は、まだ比較的新しい FastAPI を使ってみます。

# FastAPI とは

FastAPI はその名の通り高速でモダンな Web フレームワークであると謳っています。

特徴としては、

- 高速（NodeJS や Go 並みのとても高いパフォーマンス）
- 高速なコーディング
- 直感的
- 簡単
- 短い
- 堅牢性

とされています。

また、サポートしているのは、Python 3.6 以降になります。

# 簡単な使い方

## 環境

今回、紹介するにあたって、私の方で試した環境は以下になります。

- Python: 3.6.8
- fastapi: 0.63.0
- uvicorn: 0.13.3

## インストール

```bash
$ pip install fastapi uvicorn
```

## Get エントリポイント

```python:main.py
from fastapi import FastAPI


app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}
```

### 動作確認

実行コマンド

```bash
$ uvicorn main:app --reload
```

ブラウザから http://127.0.0.1:8000/items/5?q=somequery にアクセスすると

```json
{ "item_id": 5, "q": "somequery" }
```

このような値が返ってくるかと思います。

また、http://127.0.0.1:8000/docs にアクセスすると API ドキュメントも確認できます。

## Post エントリポイント

```python:main.py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel


app = FastAPI()

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

@app.post("/items/")
async def create_item(item: Item):
    return item
```

### 動作確認

POST リクエストは、http://127.0.0.1:8000/docs にアクセスし、そこからリクエストを行います。

![post_request.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659561/c031067e-cf61-7ed6-6e73-d69be3bb71b6.png)

リクエストした値がそのままレスポンスとして返ってくることが分かると思います。

# よく使う機能

## クエリパラメータの検証

以下のようにすることでクエリパラメータが 3 文字以上 50 文字以下で `^fixedquery$` にマッチするものだけという
検証を行えます。

また、Query の第一引数は、デフォルトパラメータとなります。

```python
from typing import Optional

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Optional[str] = Query(None, min_length=3, max_length=50, regex="^fixedquery$")
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
```

## クッキーの取得

クッキーは、以下のようにして取得できます。Cookie の引数は、デフォルト値です。

```python
from typing import Optional

from fastapi import Cookie, FastAPI

app = FastAPI()


@app.get("/items/")
async def read_items(ads_id: Optional[str] = Cookie(None)):
    return {"ads_id": ads_id}
```

## リクエストヘッダの取得

リクエストヘッダは、以下のようにして取得できます。Header の引数は、デフォルト値です。

```python
from typing import Optional

from fastapi import FastAPI, Header

app = FastAPI()


@app.get("/items/")
async def read_items(user_agent: Optional[str] = Header(None)):
    return {"User-Agent": user_agent}
```

## エラーハンドリング

エラーを発火させるのは、`raise` で `HTTPException` を渡すことで実現できます。

```python
from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}


@app.get("/items/{item_id}")
async def read_item(item_id: str):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item": items[item_id]}
```

## 任意のステータスコードで返す

いくつか分岐させて、任意のステータスコードを返したい時もあると思います。

以下のようにすることで任意のステータスコードでレスポンスを返せます。

```python
from typing import Optional

from fastapi import Body, FastAPI, status
from fastapi.responses import JSONResponse

app = FastAPI()

items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}


@app.put("/items/{item_id}")
async def upsert_item(
    item_id: str, name: Optional[str] = Body(None), size: Optional[int] = Body(None)
):
    if item_id in items:
        item = items[item_id]
        item["name"] = name
        item["size"] = size
        return item
    else:
        item = {"name": name, "size": size}
        items[item_id] = item
        return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)
```

## バックグラウンドでの処理

重たい処理など API のレスポンスを返した後に処理を行いたいこともあるかと思います。

以下のようにすることでログファイルへの書き出しはバックグラウンドで行えます。

```python
from fastapi import BackgroundTasks, FastAPI

app = FastAPI()


def write_notification(email: str, message=""):
    with open("log.txt", mode="w") as email_file:
        content = f"notification for {email}: {message}"
        email_file.write(content)


@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_notification, email, message="some notification")
    return {"message": "Notification sent in the background"}
```

# おわりに

いかがでしたか。

まだ、新しいのでドキュメントの日本語翻訳も途中のようですが、
FastAPI のドキュメントはとてもわかりやすく書かれていて、学習も容易なので、詳細はそちらをご確認ください。

バックエンドでは、アカウント管理もよくやることの一つかと思いますが、
そこについても書いてしまうととても長くなりそうだったので、そのうち書こうと思います。

https://note.com/ryoka_in
こちらのサイトで一部の記事を先行して有料公開しています。ぜひご覧ください。

# 参考

https://fastapi.tiangolo.com/ja/

https://github.com/tiangolo/fastapi