Fast APIのチュートリアルをやってみた

# はじめに
Fast APIの公式チュートリアルをやってみた

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

やった範囲
チュートリアルユーザーガイド ~ クエリパラメーターと文字列の検証まで
ここまででFast APIの特徴やGET POST path paramerter query paramerterなど基本的なことを学べます
チュートリアルはこの先も続くのですが、概要を把握するだけならここまででも把握できます。 FastAPIの楽しさや便利さを味わえます

## チュートリアルをまとめてみる

## 基本
```python
# base
from fastapi import FastAPI # Starletteを継承している

app = FastAPI() # instance

@app.get("/") # path operation(HTTP method) decorate
async def root(): # path operation function
    return {"message": "Hello World"} # jsonに変換
```

## path parameter

```python
# path parameter
@app.get("/items/{id}")
async def read_item(id: int): # 型を指定する
    return {"item_id": item_id}
# 型をintにした時 idはint型にサニタライズされる
# 型をintにしていない時 デフォルトの文字列で受け取る
```

```python
# Enum 
# 型と固定値(選択肢)を定義
from enum import Enum

class ModelName(str, Enum):
  tee = "tee"
  baru = "baru"

@app.get("/models/{name}"):
async def greet(name: ModelName)
  if name is ModelName.tee:
    return { "message": "おはようtee" }
  return { "message": "こんにちはbaru" }
```
```python
# file pathなど PATH(/)を含むpath parameterを受け取る
@app.get("/files/{file_path:path}"): # :path
async def read_file(file_path: str): # str
    return {"file_path": file_path}
# PATHの最初に/を含ませたい場合 URLは/を重ねる (//)
```

## query parameter
*query parameterはデフォルト値やオプショナルも受け取れる*
*query paramerterを配列で受け取る場合はQueryを使用する必要がある*

```python
# query parameter
# path parameterで定義されていなければ自動でqueryと解釈
# query parameterはOptionalにしたり、default値を持てる
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

# デフォルト値あり
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]
```
```python
# オプショナル
from typing import Union

# デフォルト値を Noneにすればオプションになる
# FastAPIでUnionは使用していない。 エディターのサポートで有効
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: Union[str, None] = None):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}
```
```python
# bool型もsanitization(変換)する
@app.get("/bool")
async def check_bool(q: bool = False):
    if q:
        return {"message": "true"}
    return {"message": "false"}
```
```python
# query paramsのvalidation
from fastapi import FastAPI, Query
from typing import Union

# オプション && 存在する時は5文字以下
# 必須 && validationをする場合 第1引数に...を指定する 
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=5)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
```
```python
# query pramerterを配列で受けとる
# queryを使わずにList[]を使うとrequest bodyとして解釈される
@app.get("/items/")
async def read_items(q: Union[List[str], None] = Query(default=None)):
    query_items = {"q": q}
    return query_items
```
```
https://fastapi.tiangolo.com/ja/tutorial/query-params-str-validations/
```

## *POST*
```python
from fastapi import FastAPI
from typing import Union
from pydantic import BaseModel

# Pydanticを使用して、受け取るrequest bodyを定義する
class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
# この時点のitem typeはclass instance
# 処理する場合dict()で辞書に変換してから処理する
    return items

# pathから受け取るのか、bodyから受け取るのかは自動で判断する
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}
```


## 説明を省いて載っける
*超基礎的な部分のみを綺麗に抽出*
```python
from fastapi import FastAPI

app = FastAPI()

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

```python
# path paramerter
@app.get("/path/{id}")
async def path(id: int):
    return {"id": id}

# query paramerter
@app.get("/query")
async def query(q: Union[str, None] = None):
  if q:
    return {"query": q}
  return {"message": "queryは渡されませんでした"}
```

```python
# request body
class User(BaseModel):
  name: st
  age: int
  email: str
  password: str

@app.post("/signup")
async def create_user(user: User):
  return user
```

## Fast APIの何が良いのか
Fast APIは巨人の方に乗っています。
Paydanticによる恩恵は素晴らしいです

Paydanticはvalidation機能を提供しますが、同時にsanitizeもします

```python
# 型なし
@app.get("/path/{id}")
async def path(id):
    return {"id": id} # url paramerterから取得した値はstringになる
```


```python
# 型あり
@app.get("/path/{id}")
async def path(id: int):
    return {"id": id} # int型
```

*通常はintに変換する作業を自前で書かないといけなのですが、pydanticの力でsanitize(変換)してくれています*
*もちろん変換できない値を入れた場合適切なエラーを表示します*

---
pathなのかrequestなのかqueryなのかを自動で判断する
```python
class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: Union[str, None] = None):
    if q:
        return {"item_id": item_id, **item.dict(), "query": q}
    return {"item_id": item_id, **item.dict()}
```

*expressと比較してみる*
```js
// どこから取るのかハンドリングする必要がある
app.get("/:path", async (req, res) => {
  const { path } = req.params; // path params
  const { query } = req.query; // query params
  const user = req.body; // request body

  const obj = {
    ...user,
    path,
    query,
  };

  res.json(obj);
});
```

自動ドキュメント生成
/docsや/redocにアクセスすると、作成したAPIに紐づいたドキュメントを見ることができます。
Postmanを使わずにAPIの検証を行うことができる + ドキュメントを自前で用意する必要がない

APIを作る専用のフレームワークであることもメリットだと感じました。

## 終わりに
UdemyをFastAPIを学習する前に公式ドキュメントで触ってみました。
公式ドキュメントにはFastAPIの仕組みや、ここに載せていない細かい情報まで大変参考になりました。
FastAPIはシンプルなので結構好きです
2018年からのスタートなので、まだまだ伸びていくと思います。
公式ドキュメントは一部日本語化もされていて非常に読みやすいのでぜひ読んでみてください

下記にDockerの環境構築方法も載せています!! よかったら使ってください

https://qiita.com/kandalog/items/d936d6985c7abb925adf