概要
FastAPIは、PythonでAPIを構築するための非常に人気のあるフレームワークである。pydanticと組み合わせることで型安全性を提供し、開発者が迅速に高品質なAPIを構築できるように設計されている。本記事では、FastAPIを使用してラーメンのオーダーを管理するAPIサーバーを構築する方法を示す。
環境構築
FastAPIとその依存関係をインストールする。以下のコマンドを実行する。
pip install fastapi uvicorn pydantic
コード全体
from fastapi import FastAPI
from pydantic import BaseModel
from enum import Enum
from typing import List
import uuid
app = FastAPI()
@app.get("/healthz")
def check_healthy():
return {"message": "This api is healty"}
# スキーマ定義
class Base(Enum):
sio = "sio" # 800
miso = "miso" # 850
class Size(Enum):
namimori = "namimori"
oomori = "oomori"
tokumori = "tokumori"
class Topping(Enum):
menma = "menma"
nori = "nori"
nitamago = "nitamago"
tyashu = "tyashu"
# order
class OrderRequest(BaseModel):
base: Base
size: Size
toppings: List[Topping]
class OrderResponse(OrderRequest):
total_price: int
# 料金
BASE_PRICES = {
Base.sio: 800,
Base.miso: 850,
}
SIZE_PRICES = {
Size.namimori: 0,
Size.oomori: 50,
Size.tokumori: 100,
}
TOPPING_PRICES = {
Topping.menma: 30,
Topping.nori: 30,
Topping.nitamago: 50,
Topping.tyashu: 80,
}
# ローカルのメモリ(テスト用)
ORDER_MEMORY = []
# api
@app.post("/orders")
def create_order(order: OrderRequest):
# オーダーから合計金額を計算
total = (
BASE_PRICES[order.base]
+ SIZE_PRICES[order.size]
+ sum([TOPPING_PRICES[topping] for topping in order.toppings])
)
# オブジェクトを作成
id = uuid.uuid4()
order_response = OrderResponse(
**order.dict(),
uuid=id,
total_price=total,
)
# メモリに追加
ORDER_MEMORY.append(order_response)
return {
"message": "Order created successfully",
"order": order_response,
}
@app.get("/orders")
def get_order():
return ORDER_MEMORY
解説
データモデルの定義
Pydanticを使用して、オーダーのリクエストとレスポンスのスキーマを定義する。
Enumを用いることで、定義されていないメンバ変数に対してErrorを発生させることができる。
from pydantic import BaseModel
from typing import List
from enum import Enum
class Base(Enum):
sio = "sio"
miso = "miso"
class Size(Enum):
namimori = "namimori"
oomori = "oomori"
tokumori = "tokumori"
class Topping(Enum):
menma = "menma"
nori = "nori"
nitamago = "nitamago"
tyashu = "tyashu"
class OrderRequest(BaseModel):
base: Base
size: Size
toppings: List[Topping]
class OrderResponse(OrderRequest):
total_price: int
エンドポイントの定義
ヘルスチェックエンドポイント
APIの健康状態を確認するためのエンドポイントを作成する。
@app.get("/healthz")
def check_healthy():
return {"message": "This api is healthy"}
オーダーエンドポイント
オーダーを作成するためのPOSTリクエストと、全オーダーを取得するためのGETリクエストを定義する。
今回は簡易的に、ORDER_MEORY
という空のリストを定義して、メモリ上にデータを一時保管している。本番開発ではDBを使うのが良い。
# オーダー保存空間(テスト用)
ORDER_MEMORY = []
@app.post("/orders")
def create_order(order: OrderRequest):
# オーダーから合計金額を計算するロジック(後述)
# ...
ORDER_MEMORY.append(order_response)
return {
"message": "Order created successfully",
"order": order_response,
}
@app.get("/orders")
def get_order():
return ORDER_MEMORY
合計金額計算ロジック
合計金額を計算するロジックを実装する。オーダーが与えられると、ベース、サイズ、トッピング料金の辞書をもとに合計を計算する。
# 料金
BASE_PRICES = {
Base.sio: 800,
Base.miso: 850,
}
SIZE_PRICES = {
Size.namimori: 0,
Size.oomori: 50,
Size.tokumori: 100,
}
TOPPING_PRICES = {
Topping.menma: 30,
Topping.nori: 30,
Topping.nitamago: 50,
Topping.tyashu: 80,
}
# api
@app.post("/orders")
def create_order(order: OrderRequest):
# オーダーから合計金額を計算
total = (
BASE_PRICES[order.base]
+ SIZE_PRICES[order.size]
+ sum([TOPPING_PRICES[topping] for topping in order.toppings])
)
# ...
実行
以下のコマンドでサーバーを起動する。
uvicorn main:app --reload
コマンドラインから通信テスト
curlコマンドで APIと通信できる。
# ヘルスチェック
curl localhost:8000/healthz
# レスポンス
# {"message":"This api is healty"}
# オーダー登録
curl -X 'POST' \
'http://localhost:8000/orders' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"base": "miso",
"size": "tokumori",
"toppings": [
"menma", "nitamago"
]
}' | jq
# レスポンス
# {
# "message": "Order created successfully",
# "order": {
# "base": "miso",
# "size": "tokumori",
# "toppings": [
# "menma",
# "nitamago"
# ],
# "total_price": 1030 # ←合計料金が計算されている!
# }
# }
試しに、pydanticで定義した型ルールに反するリクエストを投げてみる。base
が醤油、toppings
にネギが足されており、これらはメニュー表にない。
以下のように、エラーに加えてどの定義が間違っているか教えてくれる。
curl -X 'POST' \
'http://localhost:8000/orders' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"base": "shouyu",
"size": "tokumori",
"toppings": [
"menma", "nitamago", "negi"
]
}' | jq
# レスポンス
# {
# "detail": [
# {
# "type": "enum",
# "loc": [
# "body",
# "base"
# ],
# "msg": "Input should be 'sio' or 'miso'",
# "input": "shouyu",
# "ctx": {
# "expected": "'sio' or 'miso'"
# }
# },
# {
# "type": "enum",
# "loc": [
# "body",
# "toppings",
# 2
# ],
# "msg": "Input should be 'menma', 'nori', 'nitamago' or 'tyashu'",
# "input": "negi",
# "ctx": {
# "expected": "'menma', 'nori', 'nitamago' or 'tyashu'"
# }
# }
# ]
# }
Swagger UIでの通信テスト
Fast APIにはSwagger UI
というAPIのドキュメント化機能が組み込まれており、定義したエンドポイントの仕様書を表示し、APIをテストすることできる。
ブラウザでlocalhost:8000/docs
アクセスする以下の画面が表示される。
Try it out
のボタンから、適当にリクエストボディを編集してExecute
を実行すると、curlコマンドと同様の動作確認が行える。
まとめ
FastAPIを使用することで、型安全で高性能なAPIを簡単に構築することができた。