FastAPI サンプル - you1025/my_something_flagments GitHub Wiki
インストール
アプリケーションの実行
参考: 最初のステップ
main.app
下記を参考に GET メソッドを受け取る main.py
を作成する。
python の dict を返すようにすると JSON で結果が返却される。
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello():
return { "message": "Hello, World!" }
Uvicorn
uvicorn
を起動する。
--reload
オプションを指定すると再起動なしでコードの変更が反映されるようになる。
カレントディレクトリに main.py
が存在する場合は下記でよいが、
カレントディレクトリの hoge
ディレクトリ配下に main.py
が存在する場合は hoge.main:app
を指定する必要がある事に注意。
$ uvicorn main:app --reload
INFO: Will watch for changes in these directories: ['/Users/shimajiro/prog/python/fastapi_sample']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2414] using statreload
INFO: Started server process [2416]
INFO: Waiting for application startup.
INFO: Application startup complete.
API の呼び出し
$ curl -X GET localhost:8000/hello
{"message":"Hello, World!"}
パラメータおよびデータの指定
パスパラメータ
参考: パスパラメータ
URL の一部をパラメータとして取得する。
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello/{name}")
def hello(name: str):
return { "message": f"Hello, {name}!" }
$ curl localhost:8000/hello/shimajiro
{"message":"Hello, shimajiro!"}
パラメータ未指定の場合
単純に存在しない URL を指定した事になる。
$ curl localhost:8000/hello/
{"detail":"Not Found"}
クエリパラメータ
参考: クエリパラメータ
パスパラメータでない引数は自動的にクエリパラメータとして解釈される。
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello(name: str):
return { "message": f"Hello, {name}!" }
$ curl "http://localhost:8000/hello?name=shimajiro"
{"message":"Hello, shimajiro!"}
パラメータ未指定の場合
必須項目が未指定というエラーが返る。
$ curl "http://localhost:8000/hello"
{"detail":[{"loc":["query","name"],"msg":"field required","type":"value_error.missing"}]}
パラメータ未指定への対応
typing::Optional
を用いて未指定を可能とする。
未指定のままで良い場合は下記のようにデフォルト値として None
を指定するが、これ以外の文字列を設定する事も可能。
# main.py
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
@app.get("/hello")
def hello(name: Optional[str] = None):
return { "message": f"Hello, {name}!" }
未指定の場合でもエラーにならず None
が指定されている。
文字列として "Hello, None!"
と表記されているのは python の仕様で f"{None}"
により "None"
文字列が生成される事による。
$ curl "http://localhost:8000/hello"
{"message":"Hello, None!"}
リクエストボディ
参考: リクエストボディ
下記に記載するように Pydantic::BaseModel
と FastAPI::Body
を用いた 2 通りの方法が存在する。
@app.post
により POST メソッドを指定している事に注意。
Pydantic::BaseModel
リクエストボディに特定の型を指定する。
必須項目の指定や細かい指定が可能でこちらの方が高機能。
# main.app
from fastapi import FastAPI
from pydantic import BaseModel
class Data(BaseModel):
message: str
app = FastAPI()
@app.post("/send")
def receive(data: Data):
return data.message
$ curl -X POST -H "Content-Type: application/json" --data '{"message": "Hello, World!"}' localhost:8000/send
"Hello, World!"
Field による制約の付与
参考: Body - Fields
pydantic::Field
を用いてリクエストボディの項目ごとに制約を追加する事ができる。
下記は Data::message
項目に最小文字数の制約を追加し、空文字の送付を弾いている。
from fastapi import FastAPI
from pydantic import BaseModel, Field
class Data(BaseModel):
message: str = Field(min_length=1)
app = FastAPI()
@app.post("/send")
def receive(data: Data):
return data.message
message
に空文字を指定するとちゃんとエラーになる。
$ curl -X POST -H "Content-Type: application/json" --data '{"message": ""}' localhost:8000/send
{"detail":[{"loc":["body","message"],"msg":"ensure this value has at least 1 characters","type":"value_error.any_str.min_length","ctx":{"limit_value":1}}]}
FastAPI::Body
参考: Body - Multiple Parameters
リクエストボディに特定の型を指定しない。
# main.py
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/send")
def receive(body: dict = Body(...)):
return body["message"]
$ curl -X POST -H "Content-Type: application/json" --data '{"message": "Hello, World!"}' localhost:8000/send
"Hello, World!"
データ未送信の場合
おこられる。
$ curl -X POST -H "Content-Type: application/json" localhost:8000/send
{"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]
バックグラウンド処理
参考: Background Tasks
API として呼び出される関数の引数に FastAPI::BackgroundTasks
のインスタンスを含め、
実行したいバックグラウンド処理を記載した関数を add_task
で追加する。
# main.py
import time
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def heavy_task(message):
print("start heavy task.")
time.sleep(10)
with open("./messages.txt", mode="a") as f:
f.write(f"message: {message}\n")
print("heavy task has done.")
@app.post("/kick_heavy_process")
async def receive_heavy_process(background_tasks: BackgroundTasks):
background_tasks.add_task(heavy_task, message="がんばれ")
return { "message": "receive your heavy task." }
API を呼び出すと即座にタスク受け入れのメッセージが返却され、約 10 秒後にカレントディレクトリの messages.txt
にメッセージが追加される。
$ curl -X POST localhost:8000/kick_heavy_process
複数回の同時呼び出しでも即座に受け入れメッセージが返却される。
$ curl -X POST localhost:8000/kick_heavy_process; curl -X POST localhost:8000/kick_heavy_process; curl -X POST localhost:8000/kick_heavy_process
{"message":"receive your heavy task."}{"message":"receive your heavy task."}{"message":"receive your heavy task."}
uvicorn
のログ。
まず 3 回呼び出しを受け入れてその後に順次処理を実行している事が分かる。
INFO: 127.0.0.1:56506 - "POST /kick_heavy_process HTTP/1.1" 200 OK
start heavy task.
INFO: 127.0.0.1:56508 - "POST /kick_heavy_process HTTP/1.1" 200 OK
start heavy task.
INFO: 127.0.0.1:56510 - "POST /kick_heavy_process HTTP/1.1" 200 OK
start heavy task.
heavy task has done.
heavy task has done.
heavy task has done.
Cloud Run での API 作成例
ビルド用のソースコード
ソースコード用のディレクトリとして src
を作成。名前は何でも良い。
$ mkdir src
src
以下に下記 4 ファイルを作成
- requirements.txt
- main.py
- Dockerfile
- .dockerignore
requirements.txt
fastapi
uvicorn
main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
def hello():
return { "message": "Hello, World!" }
Dockerfile
# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.9-slim
# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True
# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./
# Install production dependencies.
RUN pip install --no-cache-dir -r requirements.txt
# Run the web service on container startup.
ENV PORT 8080
CMD exec uvicorn --host 0.0.0.0 --port $PORT main:app
.dockerignore
Dockerfile
README.md
*.pyc
*.pyo
*.pyd
__pycache__
.pytest_cache
Docker Image の作成
src
ディレクトリを指定して Cloud Build でイメージを作成する。
[PROJECT-ID]
には GCP のプロジェクト ID を指定する[IMAGE-NAME]
は(恐らく対象プロジェクト内で一意であれば)何でも良いはずなので適当な名前(ex. helloworld)を付ける
$ gcloud builds submit ./src --tag gcr.io/[PROJECT-ID]/[IMAGE-NAME]
詳細なオプションは gcloud builds submit を参照。
Cloud Run へのデプロイ
上記で作成したイメージを用いて Cloud Run 上にコンテナを追加する。
[サービス名]
に指定した名前で API 呼び出しの URL が決まる(適当で良い。今回はhelloservice
とする)--image
には上記ビルドで指定した--tag
の値を指定する
リージョンと未認証での呼び出しを聞かれるので適当に回答する。
$ gcloud run deploy [サービス名] --image gcr.io/[PROJECT-ID]/[IMAGE-NAME]
詳細なオプションは gcloud run deploy を参照。
API の呼び出し
URL の helloservice
がデプロイ時に指定したサービス名となっている。
$ curl https://helloservice-qxtt2lxm3x-an.a.run.app/hello
{"message":"Hello, World!"}