・M2 Mac
・macOS Monterey
・shell:zsh
FastAPI
「FastAPI」は、Python 3.6以降でWeb APIを開発するための「Webフレームワーク」(Webアプリケーションを開発するために必要な機能をパッケージ化したもの。)の一つです。
FastAPIのインストール
FastAPI及び必要なライブラリをインストールします。
% pip3 install "fastapi[all]"
または、
# メインのライブラリをインストール。
% pip3 install fastapi
# コードを実行して起動するサーバーのインストール。
% pip3 install "uvicorn[standard]"
シンプルなAPIの作成
ローカルサーバー(ここでは自身のPC内に構築したサーバーをいいます。)でGETメソッドを実行するとJSON形式のデータが返される最もシンプルなAPIを作成します。
まず、「main.py」ファイルを作成し、以下のプログラムを入力します。コメントにて各プログラムの内容を説明していきます。
from fastapi import FastAPI # FastAPIクラスをインポートします。
app = FastAPI() # FastAPIクラスからインスタンスを生成します。
# @の行はデコレータです。
# インスタンス化したappに対し、HTTPメソッド(「オペレーション」ともいいます。)のGETでルート(/)にアクセスがあった場合、直下の関数の処理を行います。
@app.get("/") # ()内を「パス」といいます。
async def root(): # asyncは非同期処理を可能とし、ある処理が実行中でも他の処理を実行することができます。
return {"message": "Hello World"}
@の行は、パスに対するオペレーションが定義されたデコレータですので、「パスオペレーションデコレータ」といいます。また、デコレータ直下の関数を「パスオペレーション関数」といいます。
次に、uvicornを使用しローカルサーバーを起動します。
% uvicorn main:app --reload # mainはファイル名、appはインスタンスを指します。
# 実行結果
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
「main.py」のappインスタンスを起動しました。「--reload」は開発用のオプションで、コード変更時にサーバーの再起動を自動的に行なってくれます。
実行結果の1行目にある、「http://127.0.0.1:8000」(このURLはルート「/」と同一です。つまり「http://127.0.0.1:8000/」と同じアドレスを意味します。)にWebブラウザでアクセス(GETメソッドを実行)すると、ブラウザにJSON形式のデータ「{"message": "Hello World"}」が表示されます。
仮に、パス部分を「"/home”」とした場合は、「http://127.0.0.1:8000/home」で 「{"message": "Hello World”}」が表示されます。
パスパラメータ
「パスパラメータ」を使用することで、パスの一部を変数として受け取り、パスオペレーション関数の引数として使用することができます。
先ほど、作成した「main.py」に以下のプログラムを追記します。
# パスのうち{}で囲われた部分を「パスパラメータ」といいます。パスの一部を変数として受け取ります。
# パスパラメータはパスオペレーション関数の引数として使用できます。
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
ここで、「http://127.0.0.1:8000/items/1」にアクセスするとブラウザに「{"item_id": 1}」が表示されます。
なお、パスオペレーション関数の引数に「アノテーション」を設定することで、設定した型以外の型がパスで指定された場合、エラーが出力されます。通常の関数のアノテーションではエラーは出力されません。FastAPIが、データの「バリデーション(検証)」を行なっているのです。
@app.get("/items/{item_id}")
async def read_item(item_id: int): # パスパラメータにint型以外が与えられた場合、エラーが表示されます。
return {"item_id": item_id}
以下は、コンテナにアノテーションを施す方法です。
li: list[int] = [1, 2, 3] # int型を要素とするリスト。
tu: tuple[str, int] = ('a', 1) # str型とint型を要素とするタプル。
se: set[str] = {'a', 'b', 'c'} # str型を要素とする集合。
di: dict[str, int] = {'a': 1, 'b': 2, 'c': 3} # str型のキーとint型の値を持つ辞書。
クエリパラメータ
URL内で「?」以降に付与されるパラメータを「クエリパラメータ」といいます。なお、検索キーワードのことを「クエリ」ともいいます。
パスパラメータではない変数をパスオペレーション関数の引数に設定すると自動的にクエリパラメータとして認識されます。
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] # クエリパラメータを使ったスライスでfake_items_dbから要素を抽出します。
クエリパラメータは、URL内において「?クエリパラメータ1=クエリパラメータ1の値&クエリパラメータ2=クエリパラメータ2の値&...」として表記することができます。
また、上記プログラムのクエリパラメータはデフォルト値を持っているため、「http://127.0.0.1:8000/items/」は「http://127.0.0.1:8000/items/?skip=0&limit=10」へのアクセスと同等となります。
仮に、「http://127.0.0.1:8000/items/?skip=1&limit=1」にアクセスした場合、「fake_items_db[1: 2]」の値が返され、ブラウザには「[{"item_name": "Bar"}]」が表示されます。
オプションパラメータ
APIを叩く際に、必須ではないパラメータを「オプションパラメータ」といいます。
「Union型」を使いオプションパラメータを設定していきます。Union型は「typingモジュール」で定義された型で、複数のデータ型を宣言することができます。例えば、文字列型または整数型を宣言したい場合は「Union[str, int]」と表記します。
from typing import Union
from fastapi import FastAPI
@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}
Union[str, None]によって、文字列型かNone型を宣言しています。None型は値「None」のみを持ち、Noneは値が存在しないことを表します。
オプショナル(必須ではない)なクエリパラメータであるqに値を指定しなかった場合、qはデフォルト値であるNoneを持ちます。
リクエストボディ
POSTメソッド(クライアントからAPIにデータを送信するメソッド。)を用いてリクエストを送る際、データ本体を「リクエストボディ」として送信します(逆にAPIは、クライアントにレスポンスを返す際、データ本体を「レスポンスボディ」として送信します。)。
POSTメソッドでは、基本的にJSON形式のリクエストボディを扱うことになります。
リクエストボディは、「pydanticモジュール」の「BaseModelクラス」を継承したクラスで、その構造(モデル)を定義していきます。
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel # pydanticモジュールのBaseModelクラスをインポートします。
# BaseModelクラスを継承したクラスで、データモデルを定義します。
# データモデルの内容は変数とアノテーションで構成します。
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はItem型である必要があります。item.name等とすることで各パラメータにアクセスすることもできます。
return item # リクエストボディをレスポンスボディとして返します。
上記プログラムで作成したAPIを実際に叩いて見ます。以下のプログラムを別のターミナルで実行してください。
import requests
url = 'http://127.0.0.1:8000/items/'
body = {
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
response = requests.post(url, json=body)
print(response.json())
# 実行結果
{'name': 'Foo', 'description': 'An optional description', 'price': 45.2, 'tax': 3.5}