ハンズオンで学ぶ、初心者向けのFastAPI
はじめに
今回は、React から離れて Python のフレームワークであるFastAPI
に触れてみました。
色々と分かった事を自分の備忘録として、まとめていこうと思います。
参考にした記事
触った理由
フロントの知識も少しずつ深まり、バックエンドの知識や技術を知る必要が今後必要だと感じて、色々ネットを漁った結果FastAPI
にたどり着いたという感じです。
初心者が学ぶには丁度いいとのことで、今回学習してみました。まだまだFlask
の利用者が多いですが今後流行ればいいなと思います。
FastAPI の構造
今回使用する、FastAPI の仕組みについてです。FastAPI は、データの受け渡しに特化したフレームワークです。勘違いしてはいけないこととして、FastAPI
はあくまでデータの受け渡しをしているだけであって、DB 側とのやり取り(SQL を投げる)はやっていないということです。
図で表すと、下のような感じです。
何度も言いますが、FastAPI はデータの受け渡しを行うだけで、DB とのやり取りに関しては他のフレームワークで補完しています。
ここでは、FastAPI
が推奨しているsqlalchemyが DB とのやり取りの部分を補完しています。この部分に関しては、sqlalchemy
でなくても恐らく問題ないと思います。
実践
環境
- vscode(1.74.0)
- python(3.10.8)
- fastapi(0.87.0)
導入
とりあえず今回は、DB との接続部分は置いといて、色々触ってみます。
DB と接続して色々やるのは、次回に持ちこそうと思います。
まずは、インストールしましょう。
pip install "fastapi[all]"
インストールが完了したら、テキトーなフォルダに移動してmain.py
と__init__.py
の 2 つを作成しましょう。自分の場合は、以下のような感じです。
fastapi-sample
├─ main.py
└─ __init__.py
では、main.py
に以下をコピペしてください。
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
async def get():
return {"Message":"Hello World"}
次に、コマンドラインで以下を実行してみましょう。(実行する際は、必ず今回作成したmain.py
と同じ階層で実行してください。)
uvicorn main:app --reload
その後、コマンドに表示されるリンク(http://127.0.0.1:8000/)に飛んでみましょう。
以下のような画面が表示されるかと思います。
現在、この path にデータを返していないため、データがないと言われているだけですね。@app.get("/hello")
この部分でデータを返す path を決めており、今回は(http://127.0.0.1:8000/hello)にデータを返しています。次に、(http://127.0.0.1:8000/docs)に移ってみてください。FastAPIが標準で搭載しているSwaggerUIに移るかと思います。以下の画面が表示されていれば問題ありません。
これを使って、簡単にデータのやり取りを行うことができます。いちいち、ブラウザの path を変えずに確認できるので、非常に便利です。使い方は、調べてみてください。ここまで、できたら実行してみましょう。以下が、データとして返ってきたら成功です。
{
"Message": "Hello World"
}
次に、パスパラメータとクエリパラメータについてです。ここでは、実装方法のみを紹介するので、中身を知りたい方は以下の記事を参考にすると良いかと思います。
パスパラメータ
パスパラメータについてです。以下が、パスパラメータのサンプルコードです。
@app.get("/countrys/{country}")
async def get(country:str):
return {"Message": country}
まず、1 行目の/countrys/{country}
の部分で URL の path を記述しています。path の{country}
の部分がパスパラメータで、これが 2 行目で宣言している関数の引数として渡ります。パスパラメータは、URL の中で使える変数みたいなものと思って頂ければ問題ないかと思います。では、実行してみましょう。今回は、入力エリアにJapan
と入力して、実行します。以下がデータとして、返ってくれば成功です。
{
"Message": "Japan"
}
問題なく、入力した値がデータとして返ってきていることを確認できました。もちろん、path も{country}
になっている部分は、Japan
になっています。以上、パスパラメータの確認でした。
クエリパラメータ
クエリパラメータについてです。以下がサンプルコードになります。
@app.get("/countrys")
async def read_item(country_name:str, city_name:str):
return {
"country_name":country_name,
"city_name":city_name
}
パスパラメータとの時との違いは、path を記述する部分には変数を与えることなく、関数の引数に変数を渡している部分です。実際に、実行してみましょう。今回は、country_name
にはJapan
、city_name
にはtokyo
を与えてみます。以下の内容が返ってくれば、成功です。
{
"country_name": "japan",
"city_name": "tokyo"
}
ちなみに、URL もhttp://127.0.0.1:8000/countrys?country_name=japan&city_name=tokyo
と、クエリパラメータになっていることを確認できました。以上、クエリパラメータの確認でした。
モックを使ってデータを処理
ここでは、モックを扱ってfastapi
の基本動作を確認します。main.py
と同じ配下に、以下のsample.json
をコピペしてください。
{
"data": [
{ "Message": "Hello" },
{ "Message": "Hello2" },
{ "Message": "Hello3" }
]
}
ディレクトリ内の構造はこのようになっていれば大丈夫です。
fastapi-sample
├─ sample.json
├─ main.py
└─ __init__.py
では、次にモックを読み込みたいと思います。以下がデータを読み込むためのサンプルコードです。
app = FastAPI()
temp = open('./sample.json','r')
sample_data = json.load(temp)
インスタンスを生成している次の行に書いて頂ければ結構です。このデータを使用して、CRUD 処理を行います。
データの取得
ここでは、データの取得を行います。全てのデータを取得する場合と、指定データの取得を行います。最初に、全データの取得を行います。
以下が、サンプルコードです。
@app.get("/items")
async def get_messages():
return sample_data
解説です。まず、1 行目でパスを指定しています。今回は、/items
でget
のリクエストを送信を行います。2 行目で関数を宣言して、処理内容を記述します。辞書型の形式にしたデータsample_data
を返すだけですね。これで、実行してみましょう。以下がデータとして返ってくれば、成功です。
{
"data": [
{
"Message": "Hello"
},
{
"Message": "Hello2"
},
{
"Message": "Hello3"
}
]
}
これで、終了と言いたいところですが、fastapi
には型定義を行うことができます。これを行うことで、よりエラーが出にくいコードへと仕上げることができます。main.py
と同階層にschema.py
を宣言しましょう。以下のような構造になれば大丈夫です。
fastapi-sample
├─ sample.json
├─ main.py
├─ schema.py
└─ __init__.py
schema.py
に以下の内容を記述してください。
from pydantic import BaseModel
from typing import List
class Item(BaseModel):
Message: str
class Items(BaseModel):
data:List[Item]
ここでは、class を作成しています。これらを利用することで先ほどのレスポンス結果に型を決めることができます。(class が分からない方は調べましょう。)
では、先ほど作成したデータの取得のコードに移ります。今回は、Items
という型を返すことで型を合わせることができそうです。ということで、記述します。記述すると以下のようになります。
@app.get("/items",response_model=Items)
async def get_messages():
return sample_data
変わった部分は、1 行目です。response_model=Items
が追加されています。response_model
はレスポンスで返ってきたデータの型をチェックしています。もし、この型通りの結果が返ってこない場合は、エラーを出します。特にここは、書かずとも処理は動くみたいですが、エラーを極力減らすためにも書くべきですね。(思わぬデータが返って来た時が面倒ですね。。。)
ここまで、理解ができれば特定のデータの取得も特に難しくないです。可能であれば、一度挑戦してみましょう。今まで、やってきた内容を使えば簡単に実装ができます。以下に、答えのコードを貼っておきます。
答え合わせ
@app.get("/items/{id}",response_model=Item)
async def get_message(id:int)
return sample_data["data"][id]
解説です。まず、今回は配列の中にある一つのデータのみを取得するためパスパラメータでid
を受け取ります。あとは取得したid
を使用して、配列の中身を参照した結果をデータとして返すだけです。また、今回は単一のデータを返すため方はItem
になっています。
データの挿入
ここでは、データの挿入を行います。以下が、サンプルコードです。
@app.post("/items",response_model=Item)
async def create(item:Item):
sample_data["data"].append({"Message":item.Message})
return {"Message":item.Message}
見た目、ほとんどget
の処理と変わりませんね。ちなみにですが、データの更新と削除もほとんど変わりません。注目すべきは 1 行目が先ほどと異なりpost
になっていることですね。データの挿入を行う際はpost
メソッドを使用します。あとは、post
クエリパラメータで受け取った値を、配列に追加して、追加したデータの内容をレスポンスとして返しているだけです。レスポンスで返す値は、任意で決めてもらって構いません。個人的に、入力した値を返すのがベストだと思い、ここではレスポンスとして入力したデータを返しています。
データの更新
ここでは、データの更新を行います。以下が、サンプルコードです。
@app.patch("/items/{id}",response_model=Item)
async def update(id:int, item: Item):
sample_data["data"][id] = {"Message": item.Message}
return sample_data["data"][id]
まず、データの更新の為patch
メソッドを選択します。その後、パスパラメータで変更を行いたい箇所をid
で受け取ります。そして、クエリパラメータで変更内容を受け取り、辞書型に変換したsample_data
データの指定した index に更新を処理を行っています。その後、更新内容をレスポンスとして返しています。
データの削除
ここでは、データの削除を行います。以下が、サンプルコードです。
@app.delete("/items/{id}",response_model=Items)
async def delete(id:int):
sample_data["data"].pop(id)
return sample_data
まず、データの削除の為delete
メソッドを選択します。その後、パスパラメータで削除したい Index をid
で受け取り、配列の処理と同様に、pop
メソッドに今回削除したい Index の id を引数に渡して削除は完了です。今回は、返り値として、全データを返すようにしています。
最後に
今回は、fastapi
の触り部分をやってみました。学習方法についてですが、いきなり DB に接続してやるのも良いと思いますが、個人的には初心者の方は、まずfastapi
役割をしっかりと抑えた上で DB 接続へと移った方が処理内容をより理解できるのではないかと思います。どれがどの役割だったか、中身の理解があやふやになってしまう可能性があります。以上、備忘録でした。
Discussion