Pythonライブラリ(API開発):FastAPI
1.概要
FastAPIはAPI開発用ライブラリであり特徴は下記の通りです。
2.APIとは/Fast APIで何をやるの?
APIとは特定サービスにアクセスできる仕組みであり参考のサービスおよび事例記事は下記で紹介しています。
個人的なFast APIの用途は2つあります。
1. Pythonで作成したAIモデルにExcelからアクセスすることでUIをExcelだけで完結できる。
2.「Pythonの環境構築済みで|高スペック⦅GPU付き⦆のPC・マイコンにAIモデルを入れておき、他の低スペック・細かい環境構築無しPCからでもAIモデルを使用可能」な状態を作るためです。
これによりAIモデルを簡単に社内でも共有できるようになります。
3.用語説明
必要な用語の説明を記載します。※一応調べてますが自信なし
4.Fast API基礎1:全般
Fast APIは公式ドキュメントが分かりやすいためそちらに従います。ライブラリは"fastapi"と"uvicorn"をインストールします。これらをインストールすると後で使用する"pydantic"も自動でインストールされます。
[Terminal]
pip install fastapi
pip install uvicorn
4-1.アプリの起動:uvicorn
FastAPIの起動はターミナルで"uvicorn filename:FastAPI()インスタンス名 --reload"を実行します。"--reload"を記載すると|ファイル更新時に自動で再起動します⦅更新ごとにファイルの立ち下げ不要⦆。なお製品向けに使用すると不要なリソースを食うため開発用として使用します(参考)。
参考までにasyncは非同期処理(複数の処理をこなす際、ある処理は別の処理の終了を待たないような処理)を意味しています(詳細は下記記事参照)。
[In] ※note_fastapi.pyに作成
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return {"Hello": "World"}
@app.get("/KIYO")
async def KIYO():
return {"Hello": "KIYO"}
@app.get("/users/{user_id}")
async def username(user_id):
return {"Hello": user_id}
#POSTメソッド
@app.post("/user") #app.postメソッドでルーティングを設定
async def info_user(items): #async: 非同期処理を行う
return {items}
[Terminal]
uvicorn note_fastapi:app --reload
[Out]
ブラウザで”http://127.0.0.1:8000”にアクセスするとroot関数の戻り値を確認できる。
出力結果より下記が確認できました。
4-2.起動時のhost・PORT設定
FastAPI立ち上げ時のhostやportの設定は下記で実施できます。
[Terminal]
uvicorn note_fastapi:app --reload --host 0.0.0.0 --port 8000
4-3.Fast APIの機能:データ型設定
型ヒントとは変数・戻り値のデータ型を示す機能です。Pythonの型ヒントは強制力がなく別の型を入力してもエラーは出ません。
しかしFast APIではデータ型を設定することで指定した型以外の入力値は受け付けないようにすることができます。留意点は下記のとおりです。
5.Fast API基礎2:APIドキュメント
FastAPIは起動と同時にAPIドキュメントが自動で2種類作成されます。またブラウザ上で動作チェックをすることも可能です。
[URL]
http://127.0.0.1:8000/docs
http://127.0.0.1:8000/redoc
5-1.APIドキュメント:Swagger UI
Swagger UIベースのAPIドキュメントは下記の通りであり、HTTPメソッド、パス、関数名が一覧で表示されております。
「タブボタン」->「Try it out」->「Excecute」を実行することで、ブラウザ上で指定したメソッドでの関数の戻り値が確認できます。
また入力値が必要な関数でも指定個所に入力すれば値に応じた戻り値を確認することができます。
5-2.APIドキュメント:ReDoc
ReDoc形式は下記の通りです。基本的な表示内容はSwagger UIと同じです。ただ戻り値の確認方法がわかりませんでした・・・
6.Fast API基礎3:GETメソッド
本章ではGETメソッドを説明します。GETメソッドの仕様よりパスパラメータ・クエリパラメータはGETメソッドのみで使用します。
6-1.ルーティング:@app.get("url")
Getメソッドのルーティング要領は下記の通りです。
[In]
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return {"Hello": "World"}
@app.get("/KIYO")
async def hello():
return {"Hello": "KIYO"}
6-2.パラメータ設定1:パスパラメータ
URLに記載した値を変数として使用する場合、その変数をパスパラメータと呼びます。設定はルーティング時のurlに{パスパラメータ}と記載します。
[In]
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return {"Hello": "World"}
@app.get("/{name}") #パスパラメータ:nameを設定
async def hello(name):
return {"Hello": name}
[Out]
URLの値に応じて出力が変化
6-3.パラメータ設定2:クエリパラメータ
パスパラメータ以外で関数の引数に渡した値はクエリパラメータになります(記法は3章参照)。なおエラーが出るため初期値はNoneを渡してます。
[In]
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return {"Hello": "World"}
@app.get("/{name}") #パスパラメータ:nameを設定
async def hello(name, sex='man', age=None): #クエリパラメータage, sexを設定
return {"Hello": [name, sex, age]}
[Out]
クエリパラメータに応じて戻り値が変化
6-4.データ型設定:型ヒント
パラメータの型を制限する場合はGETメソッドの関数に型ヒントを付けます。これにより指定型以外の入力があるとエラーを発生させられます。
[In]
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return {"Hello": "World"}
@app.get("/{name}") #パスパラメータ:nameを設定
async def hello(name:str, sex:str ='man', age: int=None): #クエリパラメータage, sexを設定
return {"Hello": [name, sex, age]}
[Out]
ageは整数以外は受け付けない。sexは数値・少数は文字列として認識される
型指定が不要なら型ヒントを設定しなくても問題ないですがtypingモジュールのOptionalでも任意指定ができます。
[In]
from fastapi import FastAPI
from typing import Optional
app = FastAPI() #FastAPIインスタンスを生成
@app.get("/{name}") #パスパラメータ:nameを設定
async def hello(name:str, sex: Optional[str] ='man', age: int=None): #クエリパラメータage, sexを設定
return {"Hello": [name, sex, age]}
7.Fast APIの基礎4:POSTメソッド
7-1.ルーティング:@app.post("url")
POSTメソッドのルーティングはGETメソッドのget->postに変更するだけです。3章で説明の通りPOSTメソッドはURLだけで通信できません。戻り値を取得するためにはHTTPクライアントでPOSTメソッドを送る必要があるためrequestsライブラリを使用します。
[In] Fast API
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#POSTメソッド
@app.post("/")
async def helloWorld():
return 'Hello world'
[In] APIを叩くスクリプト
import requests
url = 'http://127.0.0.1:8000'
res = requests.post(url) #パラメータをPOST
res.json()
[Out]
'Hello world'
7-2.パラメータ設定1:個別設定
POSTメソッドはパスパラメータやクエリパラメータはないため変数は通常通り関数に記載します。パラメータをPOSTメソッドで渡すときはDict型で{'変数名1':値1, '変数名2':値2・・・}とします。
[In] Fast API
from fastapi import FastAPI
app = FastAPI() #FastAPIインスタンスを生成
#POSTメソッド
@app.post("/users")
async def hellousers(username: str, age: int):
return f'ユーザー:{username}、年齢:{age}'
[In]
import requests
url = 'http://127.0.0.1:8000/users'
params = {'username': 'KIYO',
'age': 100}
res = requests.post(url, params=params) #パラメータをPOST
res.json()
[Out]
[Out]
・POSTメソッドでparamsの値を受け取って戻り値で出力
・ageは(文字列でも)整数なら受け付けるが小数点ならエラー
APIドキュメントよりパラメータが個別入力であることが確認できます。
7-3.パラメータ設定2:リクエストボディ(BaseModel)
パラメータが多くなると個別ではコードが見にくくなります。POSTメソッドで渡すパラメータ専用の型(各変数の名前・型・初期値)を作成することができ、作成にはBasemodelを使用します。
[In]
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, List
app = FastAPI() #FastAPIインスタンスを生成
class UserInfo(BaseModel):
username: str
age: int
sex: Optional[str] #型は任意で変更可能
#POSTメソッド
@app.post("/users")
async def hellousers(info: UserInfo):
return f'ユーザー情報:{info}'
[In]
import requests
import json
url = 'http://127.0.0.1:8000/users'
data = {'username': 'KIYO',
'age': '100',
'sex': 10.0}
res = requests.post(url, data=json.dumps(data)) #パラメータをPOST
res.json()
[Out]
出力は下記参照
<class 'dict'> <class 'str'> # print(type(data), type(json.dumps(data)))
APIドキュメントよりパラメータの入力形式が変更されています。
7-4.バリデーション(条件設定):Field()
入力値に制限をかける場合はpydanticのField()を使用します。下記では最小文字数制限だけかけました。
[In]
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Optional, List
app = FastAPI() #FastAPIインスタンスを生成
class UserInfo(BaseModel):
username: str = Field(min_length=2) #2文字未満はエラー
age: int
sex: Optional[str] #型は任意で変更可能
#POSTメソッド
@app.post("/users")
async def hellousers(info: UserInfo):
return f'ユーザー情報:{info}'
[Out]
import requests
import json
url = 'http://127.0.0.1:8000/users'
data = {'username': 'A',
'age': '100',
'sex': 10.0}
res = requests.post(url, data=json.dumps(data)) #パラメータをPOST
res.json()
[Out]※usernameが1文字のためエラー発生
{'detail': [{'loc': ['body', 'username'],
'msg': 'ensure this value has at least 2 characters',
'type': 'value_error.any_str.min_length',
'ctx': {'limit_value': 2}}]}
7-5.参考_クラス設定(ORマッパー):orm_mode=True
FastAPIは通常JSON形式データしか受け付けませんが、SQLのO/RマッパーであるSQLAlchemyなどと連動させたい場合はormモード設定ができます。
[In]※下記では使用しないため参考用
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Optional, List
app = FastAPI() #FastAPIインスタンスを生成
class UserInfo(BaseModel):
username: str = Field(min_length=2) #2文字未満はエラー
age: int
sex: Optional[str] #型は任意で変更可能
class Config:
orm_mode = True
8.特殊ルーティング(Event)
FastAPIではアプリの起動時/停止時にコード処理させることも可能です。
8-1.起動時の実行@app.on_event("startup")
起動処理はルーティング「@app.on_event("startup")」を使用します。
[IN]
from fastapi import FastAPI
app = FastAPI()
items = {}
@app.on_event("startup")
async def startup_event():
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
[OUT]
起動時にitems = {'foo': {'name': 'Fighters'}, 'bar': {'name': 'Tenders'}}となる
8-2.起動時の実行@app.on_event("shutdown")
停止処理はルーティング「@app.on_event("shutdown")」を使用します
[IN]
from fastapi import FastAPI
app = FastAPI()
@app.on_event("shutdown")
def shutdown_event():
with open("log.txt", mode="a") as log:
log.write("Application shutdown")
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
[OUT]
停止時にlog.txtに"Application shutdown"と記載する。
9.その他応用
9-1.戻り値の型ヒント:response_model
APIを叩いた時の戻り値の型ヒントはapp.method("root", response_model)となります。なくても動きますがご参考までに。
[In]
from fastapi import FastAPI
from typing import Dict
app = FastAPI() # FastAPIインスタンスを生成
#GETメソッド
@app.get("/", response_model=Dict) # "/"(ルート)にアクセスしたときに実行
async def read_root():
return {"message": "Hello World"}
10.他PCへ接続時の注意点
専門の人は当たり前でも情報系出身でない私がはまった部分のメモです。
10-1.アクセス時のIPアドレス
開発時で接続に使用した"http://127.0.0.1:8000"はlocalhostと同じであり自分のPCを意味します。
よって他PCに接続する場合は"http://接続PCのIPアドレス:port"と入力する必要があります。Windowsはコマンドプロンプトで"ipconfig"と入力すればipアドレスは確認できます。
10-2.uvicorn立ち上げ時のhostIP設定
Fast API起動時にhost IPの設定として"--host 0.0.0.0"のフラグを付ける必要があります。意味は1mmも理解できてないですがこれをつけないとwin10061エラーが解消されませんでした。
[Terminal]
uvicorn note_fastapi:app --reload --host 0.0.0.0 --port 8000
コピペ用テンプレコード
[In]
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import Optional, List, Dict
class Info(BaseModel):
inA: str
inB: Optional[str] #型は任意で変更可能
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return {"Hello": "World"}
#POSTメソッド
@app.post("/info") #app.postメソッドでルーティングを設定
async def info_post(info: Info): #async: 非同期処理を行う
return {info}
参考資料
あとがき
とりあえず書きたい部分だけかけたので先出しします。やってみたシリーズや業務と合わせながら更新していきます。
2022年2月追記:他PCへの接続でエラーが消えなかったので休日がつぶれた(すべて8章に記載)・・・・
この記事が気に入ったらサポートをしてみませんか?