FastAPI

モチベ
- FastAPIに入門する
FastAPIとは
- APIを構築する為のモダンで高速なPython Webフレームワーク
- パフォーマンスが高速
- 開発が高速
- 2018年リリースPythonWebフレームワークの中では比較的新しい
- 型ヒントを用いて開発を行う
FastAPIとは
- シンプルで直感的な記述で、簡単にAPIが作成可能
- 型ヒントを使用した安全な開発
- 自動ドキュメント生成(OpenAPI標準に従ったドキュメント)
- 後発ながらコミュニティが非常に活発

型ヒント
- Pythonは動的型付き言語なので、型を利用しない開発が基本だった
- 型ヒントを活用することで可読性の向上やバグの早期発見などのメリット
- Python3.5から公式サポート
int - 整数
float - 少数
str - 文字列
bool - 論理型
list[int] - リスト型 [1, 2, 3]の様なやつ
dict[str, bool] - 辞書型 {"flag": true} # Python3.9から導入された
optional
from typing import Optional
g: Optional[int] = 1 # or None
Annotated(型に注釈をつけることが出来る)
from typing import Annotated
Weight = Annotated[int, 'kg'] # 型, 説明
w: Weight = 20
一般的なAnnotatedは型の説明でしかないが、FastAPIの場合、DIに使用することが出来る。
関数・メソッドの型ヒント
def sum(a: int, b: int) -> int:
return a + b

環境構築
仮想環境作成
python -m venv fleamarket
環境のアクティベート
source fleamarket/bin/activate
モジュールインストール
pip install fastapi
pip install "uvicorn[standard]"
モジュール確認
pip list

開発
ディレクトリ直下にmain.pyを作成
FastAPIを利用した記述を行っていく
main.py
from fastapi import FastAPI
app = FastAPI()
# デコレータをつけることでAPIにすることが出来る app.httpメソッド
@app.get('/')
async def example():
return {"message": "Hello World"}
uvicornを利用してサーバを立てる
uvicorn main:app --reload
curl localhost:8000

ドキュメント
自動的にドキュメントページが用意されている
uvicornでサーバを起動している限り、ソースコードの更新内容が自動的にdocsに反映される
OpenAPIの話
Try it outをクリック→Executeをクリック
レスポンスを確認できる

CRUD - READ
curds/item.py
from enum import Enum
from typing import Optional
class ItemStatus(Enum):
ON_SALE = "ON_SALE"
SOLD_OUT = "SOLD_OUT"
class Item:
def __init__(
self,
id: int,
name: str,
price: int,
description: Optional[str],
status: ItemStatus
):
self.id = id
self.name = name
self.price = price
self.description = description
self.status = status
items = [
Item(1, "pc", 100000, "備品です", ItemStatus.ON_SALE),
Item(2, "スマートフォン", 50000, None, ItemStatus.ON_SALE),
Item(3, "Python本", 1000, "使用感あり", ItemStatus.SOLD_OUT),
]
def find_all():
return items
def find_by_id(id: int):
for item in items:
if item.id == id:
return item
return None
main.py
from fastapi import FastAPI
from cruds import item as item_cruds
app = FastAPI()
@app.get('/items')
async def find_all():
return item_cruds.find_all()
@app.get('/items/{id}')
async def find_by_id(id: int):
return item_cruds.find_by_id(id)

クエリパラメータ
?name=fastapi
cruds/item.py
def find_by_name(name: str):
filtered_items = []
for item in items:
if item.name == name:
filtered_items.append(item)
return filtered_items
main.py
@app.get('/items/') # 衝突すると上書きが発生する
async def find_by_name(name: str): # クエリパラメータを引数にするだけ
return item_cruds.find_by_name(name)

CRUD - Create
item.py
def create(item_create):
new_item = Item(
len(items) + 1,
item_create.get('name'),
item_create.get('price'),
item_create.get('description'),
ItemStatus.ON_SALE,
)
items.append(new_item)
return new_item
main.py(fastapiからBodyをimportし、引数の初期値に定義)
from fastapi import FastAPI, Body
@app.post('/items')
async def create(item_create=Body()):
return item_cruds.create(item_create)

CRUD - UPDATE
item.py
def update(id: int, item_update):
for item in items:
if item.id == id:
item.name = item_update.get('name', item.name)
item.price = item_update.get('price', item.price)
item.description = item_update.get('description', item.description)
item.status = item_update.get('status', item.status)
return item
main.py
@app.put('/items/{id}')
async def update(id: int, item_update=Body()):
return item_cruds.update(id, item_update)

CRUD - Delete
item.py
def delete(id: int):
for i in range(len(items)):
if items[i].id == id:
delete_item = items.pop(i)
return delete_item
return None
main.py
@app.delete('/items/{id}')
async def delete(id: int):
return item_cruds.delete(id)

router
from fast import APIRouter を利用
@appを@routerに変更
routers/item.py
from fastapi import APIRouter, Body
from cruds import item as item_cruds
router = APIRouter()
@router.get('/items')
async def find_all():
return item_cruds.find_all()
@router.get('/items/{id}')
async def find_by_id(id: int):
return item_cruds.find_by_id(id)
@router.get('/items/')
async def find_by_name(name: str):
return item_cruds.find_by_name(name)
@router.post('/items')
async def create(item_create=Body()):
return item_cruds.create(item_create)
@router.put('/items/{id}')
async def update(id: int, item_update=Body()):
return item_cruds.update(id, item_update)
@router.delete('/items/{id}')
async def delete(id: int):
return item_cruds.delete(id)
main.pyではroutersをimportし、app.include_router()にわたす
main.py
from fastapi import FastAPI
from routers import item
app = FastAPI()
app.include_router(item.router)
これでルーティングの設定完了
APIRouterにprefixを追加することで、エンドポイントの共通パス部分をまとめることができる。
router = APIRouter(prefix="/items")
tagsを追加することで、OpenAPIで表示されているタイトル部分を指定でき、ドキュメントをまとめることが出来る。
router = APIRouter(prefix="/items", tags=["Items"])

バリデーション
Pydantic
- 型ヒントを使用するデータ検証ライブラリ
- FastAPIと併せてインストールされる
schemas.py
from pydantic import BaseModel, Field
from typing import Optional
class ItemCreate(BaseModel):
name: str = Field(min_length=2, max_length=20, examples=['pc'])
price: int = Field(gt=0, examples=[10000])
description: Optional[str] = Field(None, examples=['備品です'])
routers/item.py
from schemas import ItemCreate
@router.post('')
async def create(item_create: ItemCreate):
return item_cruds.create(item_create)
cruds/item.py
from schemas import ItemCreate
def create(item_create: ItemCreate):
new_item = Item(
len(items) + 1,
item_create.name,
item_create.price,
item_create.description,
ItemStatus.ON_SALE,
)
items.append(new_item)
return new_item