早引き FastAPI
Python で API サーバー立てるのに FastAPI が良さそうなので、ドキュメントをナナメ読みした。
チュートリアルだけでもボリューミーだったので、辞書的に知りたい情報にアクセスできるようにまとめる。
記事執筆時の 2021-03-30 時点で FastAPI のメジャーバージョンは 0 なので、いずれこの記事は陳腐化する可能性が高い。あくまで執筆時点での整理なのでご了承を。
FastAPI の特徴
ここにまとまっているが、
- Starlette という軽量 Web フレームワーク
- Pydantic という型アノテーションによるバリデーション/シリアライゼーションのライブラリ
すばやく FastAPI を使っていく意味では、上記の2つのライブラリに大きく依存しているのがポイント。
とりあえず使う
$ pip install fastapi
$ pip install uvicorn[standard]
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
$ uvicorn main:app --reload
パラメータ
パスオペレーション関数(path operation function)の引数に指定できるパラメータのこと。
ここではどのパラメータがどの関数なのか理解やすくするために Path(...)
のように、追加の引数を何も指定せずにパラメータを指定している。
追加の引数が不要な場合は Path
などの関数は省略できるので、 FastAPI のドキュメントのように省略したスタイルで記載したほうが実際はわかりやすい。
自分が見た限りだと各パラメータの追加の引数が一覧で整理されたページはなかったので、現状はソースコードを確認するか、コントリビュートしよう。
Path
パスパラメータを設定するには Path
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
追加の引数が不要な場合は Path
を使わずに直接初期値を渡すことができる。
from typing import Optional
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}/comments/{comment_id}")
async def read_comment(
item_id: int = Path(...),
comment_id: Optional[int] = None,
):
if comment_id is None:
return {"item_id": item_id}
return {"item_id": item_id, "comment_id": comment_id}
追加の引数の一覧
パスパラメータの概要
パスパラメータの追加の引数の概要
Query
クエリパラメータを設定するには Query
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
追加の引数が不要な場合は Query
を使わずに直接初期値を渡すことができる。
from typing import Optional
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
async def read_items(
name: str = Query(...),
title: Optional[int] = None,
):
if title is None:
return {"name": name}
return {"name": name, "title": title}
追加の引数の一覧
クエリパラメータの概要
クエリパラメータの追加の引数の概要
Body
ボディパラメータを設定するには Body
を使用する。
ボディの型定義とバリデーションするために BaseModel
を継承したクラスを定義する。
BaseModel
を継承したクラスで各フィールドを定義するために Field
を使用する。
BaseModel
と Filed
は pydantic
から import する点に注意する。
BaseModel
ボディの型定義とバリデーションするために BaseModel
を継承したクラスを定義する。
設定は内部クラス Config
として定義できる。不要な場合は省略できる。
from typing import Optional
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
class Config:
title = "Item"
BaseModel
の概要
BaseModel
の設定項目
Field
BaseModel
を継承したクラスにフィールドを設定するには Field
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
追加の引数が不要な場合は Field
を使わずに直接初期値を渡すことができる。
from typing import Optional
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(...)
description: Optional[str] = None
Field
の概要
Field
の対応する型
Field
の追加の引数
Body
ボディパラメータを設定するには Body
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
追加の引数が不要な場合は Body
を使わずに直接初期値を渡すことができる。
複数の Body
を指定する場合は、受け取るボディの形式が変わるので注意する。
from typing import Optional
from fastapi import FastAPI, Body
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str]
class User(BaseModel):
name: str
description: Optional[str]
@app.post("/items")
async def create_item(item: Item = Body(...), user: Optional[User] = None):
if user is None:
return {"item": item}
return {"item": item, "user": user}
追加の引数の一覧
ボディパラメータの概要
ネストしたボディパラメータの概要
複数のボディパラメータを指定するときの概要
ボディパラメータにメタデータを付与するときの概要
Cookie
クッキーパラメータを設定するには Cookie
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
クエリパラメータとして認識されてしまうため、追加の引数が不要な場合でも Cookie
を使わなければならない。
from typing import Optional
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items")
async def read_items(
name: str = Cookie(...),
title: Optional[int] = Cookie(None),
):
if title is None:
return {"name": name}
return {"name": name, "title": title}
追加の引数の一覧
クッキーパラメータの概要
Header
ヘッダーパラメータを設定するには Header
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
クエリパラメータとして認識されてしまうため、追加の引数が不要な場合でも Header
を使わなければならない。
重複したヘッダーは型アノテーションとして List
を指定することで、配列で受け取ることができる。
from typing import Optional
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items")
async def read_items(
x_tokens: List[str] = Header(...),
user_agent: Optional[str] = Header(None),
):
if user_agent is None:
return {"x_tokens": x_tokens}
return {"x_tokens": x_tokens, "user_agent": user_agent}
追加の引数の一覧
ヘッダーパラメータの概要
Form
フォームパラメータを設定するには Form
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
クエリパラメータもしくはボディパラメータとして認識されてしまうため、追加の引数が不要な場合でも Form
を使わなければならない。
フォームパラメータを使用するには python-multipart
をインストールする必要がある。
$ pip install python-multipart
from typing import Optional
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
追加の引数の一覧
フォームパラメータの概要
File
ファイルアップロードに対応するには File
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
クエリパラメータもしくはボディパラメータとして認識されてしまうため、追加の引数が不要な場合でも File
を使わなければならない。
型アノテーションとして bytes
もしくは UploadFile
を指定できる。
bytes
はそのまま bytes
として値を受け取り、 UploadFile
を指定すると UploadFile
として値を受け取る。
UploadFile
だとファイル名といったメタ情報にアクセスできるなどの利点があるので、一般的には UploadFile
の使用が推奨される。
File
を使用するには python-multipart
をインストールする必要がある。
$ pip install python-multipart
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/file/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
追加の引数の一覧
ファイルパラメータの概要
Depends
Dependency Injection を導入するには Depends
を使用する。
引数に呼び出し可能なオブジェクト(関数やクラス)を指定する。
引数に指定する呼び出し可能なオブジェクトは、パスパラメータ関数の引数指定できるものを引数として取る。
Depends
は上書き可能なので、テスト時だけクラスを差し替えるといったことができる。
from abc import ABC, abstractmethod
from fastapi import Depends, FastAPI
app = FastAPI()
class SlackClientInterface(ABC):
@abstractmethod
def send_message(self, message: str) -> None:
raise NotImplementedError
class SlackClient():
def send_message(self, message: str) -> None
print(f"send message: {message}")
async def get_slack_client():
return SlackClient()
@app.post("/send")
async def send_message(message: str, slack_client: SlackClientInterface = Depends(get_slack_client)):
slack_client.send_message(message)
return {"message": message}
@app.post("/login")
async def login(
username: str = Form(...),
password: str = Form(...),
slack_client: SlackClientInterface = Depends(get_slack_client),
):
slack_client(f"Welcome {username}!")
return {"username": username}
from fastapi import Depends, FastAPI
from main import app, SlackClientInterface
class SlackClientMock():
def send_message(self, message: str) -> None
print(f"send mock message: {message}")
async def get_slack_client_for_test():
return SlackClientMock()
app.dependency_overrides[get_slack_client] = get_slack_client_for_test
client = TestClient(app)
def test_send_message():
response = client.get("/send?message=hello")
assert response.status_code == 200
assert response.json()["message"] == "hello"
追加の引数の一覧
Dependency Injection の概要
BackgroundTasks
レスポンスを返したあとに実行するバックグラウンドタスクを定義するには BackgroundTasks
を使用する。
第一引数には初期値を入れる。
オプションの場合は None
を、必須かつ初期値を設定しない場合は ...
を指定する。
クエリパラメータもしくはボディパラメータとして認識されてしまうため、追加の引数が不要な場合でも File
を使わなければならない。
File
を使用するには python-multipart
をインストールする必要がある。
$ pip install python-multipart
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/file/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
バックグラウンドタスクの概要
パスオペレーション設定
体力切れのため、項目だけ羅列する。
response_model
status_code
tags
dependencies
Discussion