FastAPI は、Python 3.6以降を使用したモダンなアプリケーションプログラミングインターフェースの構築に便利な、高速かつ軽量のウェブフレームワークです。今回の記事では、そんなFastAPI を使ったアプリ構築の基本を説明し、これが2021年ベストオープンソースフレームワークの1つとしてノミネートされた理由の一端をご紹介します。
FastAPIアプリを開発する準備ができたら、サーバー選びが必要になります。Kinstaのアプリケーションホスティングとデータベースホスティングサービスは、Pythonに強いPaaS(Platform as a Service)です。是非ともあわせてご確認ください。
まずは基本から学びましょう。
FastAPIの強み
はじめに、FastAPIフレームワークが各種開発プロジェクトにもたらす利点をご紹介します。
- スピード:FastAPIは、その名の通り、非常に高速なフレームワークです。その速さは、一般にAPI構築の中でも最速の選択肢と称されるGoやNode.jsに匹敵します。
- 学習とプログラミングが容易:FastAPIでは、APIを世に送り出すために必要なほぼすべての要素が網羅されています。いちいちゼロからすべてを記述する必要はありません。わずか数行のコードで、デプロイ可能なRESTful APIを用意することができます。
- 包括的なドキュメント:FastAPIはOpenAPIというドキュメントの仕様を採用しており、ドキュメントを動的に生成することができます。このドキュメントには、FastAPIのエンドポイント、応答、パラメータ、リターンコードといった詳しい情報が記載されます。
- バグの少ないAPI:FastAPIはカスタムデータ検証をサポート。バグの少ないAPIを構築することができます。FastAPIの採用により人為的なバグが40%減少した例も見られるほどです。
- 型ヒント:Python 3.5で導入された型ヒントにより、変数の
type
が宣言可能になりました。変数の型が宣言されていると、IDEにおけるサポート面での相性が高まり、より正確にエラーを予測することができます。
FastAPI利用の下準備
今回の記事の説明に沿ってFastAPIを利用するには、少しだけ準備をする必要があります。
Visual Studio Codeなどのプログラミング向けテキストエディタ/IDEをご用意ください。他には、Sublime TextやEspressoなどもおすすめです。
Pythonアプリケーションとそのインスタンスの実行場所として仮想環境を構築するのが一般的です。仮想環境では、複数のパッケージセットや構成を同時に実行しながら、互換性のないパッケージのバージョンによる干渉を回避することができます。
仮想環境を作成するには、ターミナルを開いて次のコマンドを実行します。
$ python3 -m venv env
また、仮想環境を有効にする必要があります。これを行うコマンドは、使用しているオペレーティングシステムやシェルにより異なります。各環境でのコマンドは以下の通りです。
# UnixまたはMacOS(bashシェルスクリプト):
/path/to/venv/bin/activate
# UnixまたはMacOS(cshスクリプト):
/path/to/venv/bin/activate.csh
# UnixまたはMacOS(fishシェル):
/path/to/venv/bin/activate.fish
# Windows(コマンドプロンプト):
pathtovenvScriptsactivate.bat
# Windows(PowerShell):
pathtovenvScriptsActivate.ps1
Python対応IDEの中には、仮想環境有効化の設定ができるものもあります。
続いては、FastAPIをインストールします。
$ pip3 install fastapi
FastAPIはAPI構築のフレームワークですが、APIをテストするためにはローカルウェブサーバーが必要になります。開発に便利な選択肢として、Uvicornをおすすめします。Uvicornをインストールするには、次のコマンドを実行します。
$ pip3 install "uvicorn[standard]"
インストールに成功したら、プロジェクトの作業ディレクトリにmain.pyという名前のファイルを作成します。このファイルがアプリケーションのエントリポイントになります。
FastAPIの簡単な例
エンドポイントの例をセットアップし、FastAPIをテストしてみましょう。main.pyファイルに以下のコードを貼り付けて、ファイルを保存します。
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"greeting":"Hello world"}
上記コードにより、基本的なFastAPIエンドポイントが作成できます。説明は以下の通りです。
from fastapi import FastAPI
:APIの機能がFastAPIというPythonクラスで提供されるapp = FastAPI()
:FastAPIインスタンスを作成@app.get("/")
:Pythonのデコレータであり、FastAPIに対して、その下の関数がリクエスト処理を担当することを指定@app.get("/")
:ルートを指定するデコレータ(サイトのルートでGET
メソッドを作成し、ラップされた関数により結果が返される)- このほかにも、
@app.post()
、@app.put()
、@app.delete()
、@app.options()
、@app.head()
、@app.patch()
、@app.trace()
のような操作を行うことが可能
ファイルのあるディレクトリで、ターミナルのコマンドを実行しAPIサーバーを起動します。
$ uvicorn main:app --reload
このコマンドにあるmain
は、モジュールの名前です。app
オブジェクトはアプリケーションのインスタンスであり、ASGIサーバーにインポートされます。--reload
は、何か変更を加えたときに自動での再読み込みをサーバーに指示するものです。
ここまでで、ターミナルには以下が表示されるはずです。
$ uvicorn main:app --reload
INFO: Will watch for changes in these directories: ['D:\WEB DEV\Eunit\Tests\fast-api']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [26888] using WatchFiles
INFO: Started server process [14956]
INFO: Waiting for application startup.
INFO: Application startup complete.
続いてブラウザで、http://localhost:8000
に移動して、APIが動作していることを確認してください。JSONオブジェクトとして「“Hello”: “World”」と表示されます。この段階ですでに、FastAPIを使ったAPIの作成がいかに簡単かをご理解いただけたはずです。上記コードの6行目にあるように、ルートを定義し、Pythonの辞書を返すという操作を行うだけで完了しています。
型ヒントを使う
Pythonでのコード記述の経験があれば、変数にint
、str
、float
、bool
のような基本的なデータ型を付けることには慣れているはずです。しかし、Pythonバージョン3.9からは、高度なデータ構造が導入されており、dictionaries
、tuples
、lists
などのデータ構造での作業が可能になっています。FastAPIの型ヒントを使用すると、pydanticモデルを使用してデータのスキーマを構成し、データ検証の恩恵を受けることができます。
以下の例で、シンプルな食事の価格計算機calculate_meal_fee
を使って、Pythonでの型ヒントの使い方をご紹介します。
def calculate_meal_fee(beef_price: int, meal_price: int) -> int:
total_price: int = beef_price + meal_price
return total_price
print("Calculated meal fee", calculate_meal_fee(75, 19))
ちなみに、型ヒントによってコードの挙動そのものは変更されません。
FastAPIのインタラクティブAPIドキュメント
FastAPIでは、Swaggerを使用した自動での対話型APIドキュメント生成が可能です。http://localhost:8000/docs
に移動すると、すべてのエンドポイント、メソッド、スキーマを確認できます。
このブラウザベースのAPIドキュメントはFastAPIで自動生成されるので、これの用意として特別な操作をする必要はありません。
ブラウザベースのAPIドキュメントには、Redocという選択肢もあります。これを利用するには、http://localhost:8000/redoc
に移動してください。すると、エンドポイント、メソッド、およびそれぞれの応答が一覧で表示されます。
FastAPIでのルートの設定
@app
デコレータでは、ルートのメソッドを@app.get
や@app.post
のように指定できます。また、GET
、POST
、PUT
、DELETE
や、あまり一般的ではない選択肢であるHEAD
、PATCH
、TRACE
もサポートしています。
FastAPIでアプリを構築する
続いては、FastAPIを使用してCRUDアプリケーションを構築する方法をご紹介します。このアプリケーションでは以下のことができます。
- ユーザーを作成する
- ユーザーのデータベースレコードを読み込む
- 既存のユーザーの情報を変更する
- 既存のユーザーを削除する
CRUDの各操作を実行するために、APIエンドポイントを公開するメソッドを作成します。その結果として、ユーザーのリストを保存できるインメモリデータベースが出来上がります。
Pythonの型注釈を使ってデータの検証や設定管理を行うために、pydanticライブラリを使用します。この例では、データの形状を属性を持つクラスとして宣言します。
インメモリデータベースを使用しましょう。これにより、FastAPIを使いながらAPIを素早く構築することができます。とは言え、実際の開発環境では、PostgreSQL、MySQL、SQLite、あるいはOracleなど、好みのデータベースを利用していただいてかまいません。
アプリの構築
まず、ユーザーモデルを作成します。ユーザーモデルには、次のような属性があります。
id
:UUID(汎用一意識別子)first_name
:ユーザーの名前last_name
:ユーザーの苗字gender
:ユーザーの性別roles
:admin
とuser
という役割がこれに含まれる
まず、作業ディレクトリにmodels.pyという名前のファイルを作成し、次のコードをmodels.pyに貼り付けてモデルを作成します。
# models.py
from typing import List, Optional
from uuid import UUID, uuid4
from pydantic import BaseModel
from enum import Enum
from pydantic import BaseModel
class Gender(str, Enum):
male = "male"
female = "female"
class Role(str, Enum):
admin = "admin"
user = "user"
class User(BaseModel):
id: Optional[UUID] = uuid4()
first_name: str
last_name: str
gender: Gender
roles: List[Role]
上のコードを簡単に説明すると以下の通りです。
User
クラスがBaseModel
を継承し、これがpydantic
からインポートされる- 上述のように、ユーザーの属性を定義
次のステップは、データベースの作成です。main.pyファイルの中身を以下のコードに置き換えてください。
# main.py
from typing import List
from uuid import uuid4
from fastapi import FastAPI
from models import Gender, Role, User
app = FastAPI()
db: List[User] = [
User(
id=uuid4(),
first_name="John",
last_name="Doe",
gender=Gender.male,
roles=[Role.user],
),
User(
id=uuid4(),
first_name="Jane",
last_name="Doe",
gender=Gender.female,
roles=[Role.user],
),
User(
id=uuid4(),
first_name="James",
last_name="Gabriel",
gender=Gender.male,
roles=[Role.user],
),
User(
id=uuid4(),
first_name="Eunit",
last_name="Eunit",
gender=Gender.male,
roles=[Role.admin, Role.user],
),
]
main.pyで以下の操作を行っています。
db
をList
の型で初期化し、User
のモデルを渡す- それぞれ
first_name
、last_name
、gender
、roles
のような必要とされる属性を持つ4人のユーザーを格納するインメモリデータベースを作成(ユーザーEunit
にはadmin
とuser
の役割が割り当てられ、他の3人のユーザーにはuser
のみ割り当てられる)
データベースレコードの読み出し
ここまでで、インメモリデータベースのセットアップとユーザーの登録が完了しました。次のステップで、すべてのユーザーの一覧を返すエンドポイントをセットアップします。FastAPIの腕の見せどころです。
main.pyファイルで、Hello World
エンドポイントのすぐ下に次のコードを貼り付けます。
# main.py
@app.get("/api/v1/users")
async def get_users():
return db
このコードによりエンドポイント/api/v1/users
が定義され、また、データベースdb
のすべての内容を返す非同期関数get_users
が作成されます。
ファイルを保存すると、ユーザーエンドポイントのテストが可能です。ターミナルで次のコマンドを実行し、APIサーバーを起動してみましょう。
$ uvicorn main:app --reload
ブラウザで、http://localhost:8000/api/v1/users
にアクセスします。以下のように、すべてのユーザーの一覧が表示されるはずです。
この段階で、main.pyファイルは以下のようになります。
# main.py
from typing import List
from uuid import uuid4
from fastapi import FastAPI
from models import Gender, Role, User
app = FastAPI()
db: List[User] = [
User(
id=uuid4(),
first_name="John",
last_name="Doe",
gender=Gender.male,
roles=[Role.user],
),
User(
id=uuid4(),
first_name="Jane",
last_name="Doe",
gender=Gender.female,
roles=[Role.user],
),
User(
id=uuid4(),
first_name="James",
last_name="Gabriel",
gender=Gender.male,
roles=[Role.user],
),
User(
id=uuid4(),
first_name="Eunit",
last_name="Eunit",
gender=Gender.male,
roles=[Role.admin, Role.user],
),
]
@app.get("/")
async def root():
return {"Hello": "World",}
@app.get("/api/v1/users")
async def get_users():
return db
データベースレコードの作成
次のステップは、データベースでの新規ユーザー作成のためのエンドポイント作成です。次のコードをmain.pyファイルに貼り付けてください。
# main.py
@app.post("/api/v1/users")
async def create_user(user: User):
db.append(user)
return {"id": user.id}
このコードでは、新規ユーザー登録用のエンドポイントを定義し、@app.post
デコレータを使用しPOST
メソッドを作成しています。
また、User
モデルのuser
を受け取る関数create_user
を作成し、新規作成したuser
をデータベースdb
に追加しています。最後に、このエンドポイントからは、作成したユーザーのid
のJSONオブジェクトが返されます。
上述のように、エンドポイントをテストするには、FastAPIの自動APIドキュメントを使用します。これは、ウェブブラウザを使用しPOSTを実行できないためです。http://localhost:8000/docs
に移動することで、Swaggerを利用したドキュメントを確認できます。
データベースレコードの削除
今回の例では、CRUDアプリケーションの構築を行っているため、アプリケーションには指定したリソースを削除する機能が必要です。それでは、ユーザー削除のエンドポイントを作成しましょう。
以下のコードをmain.pyファイルに貼り付けます。
# main.py
from uuid import UUID
from fastapi HTTPException
@app.delete("/api/v1/users/{id}")
async def delete_user(id: UUID):
for user in db:
if user.id == id:
db.remove(user)
return
raise HTTPException(
status_code=404, detail=f"Delete user failed, id {id} not found."
)
このコードで行っていることは以下の通りです。
@app.delete("/api/v1/users/{id}")
:@app.delete()
デコレータを使用してdeleteエンドポイントを作成(パスは/api/v1/users/{id}
のままですが、その後、ユーザーのidに対応するパス変数であるid
を取得)async def delete_user(id: UUID)
:URLからid
を取得するdelete_user
関数を作成for user in db:
:データベース内のユーザーをループし、渡されたid
がデータベース内のユーザーと一致するかどうかをチェックdb.remove(user)
:id
がユーザーと一致した場合、そのユーザーを削除する(そうでない場合、ステータスコード404のHTTPException
が発生)
データベースレコードの更新
続いては、ユーザーの情報を更新するエンドポイントを作成します。更新対象のパラメータはfirst_name
、last_name
、roles
です。
models.pyファイルで、次のコードをUser
モデルの下に、つまりUser(BaseModel):
クラスの後に貼り付けます。
# models.py
class UpdateUser(BaseModel):
first_name: Optional[str]
last_name: Optional[str]
roles: Optional[List[Role]]
このコードで、クラスUpdateUser
はBaseModel
を継承しています。そして、first_name
、last_name
、roles
のような編集対象となるユーザーパラメータをオプションに設定します。
次に、特定のユーザー情報の更新に使用するエンドポイントを作成します。main.pyファイル内、@app.delete
デコレータの後に、次のコードを貼り付けます。
# main.py
@app.put("/api/v1/users/{id}")
async def update_user(user_update: UpdateUser, id: UUID):
for user in db:
if user.id == id:
if user_update.first_name is not None:
user.first_name = user_update.first_name
if user_update.last_name is not None:
user.last_name = user_update.last_name
if user_update.roles is not None:
user.roles = user_update.roles
return user.id
raise HTTPException(status_code=404, detail=f"{id}に該当するユーザーが見つかりませんでした")
上のコードでは、以下のことを行っています。
- 情報編集のエンドポイントである
@app.put("/api/v1/users/{id}")
を作成(ユーザーのIDに対応する変数パラメータid
あり) update_user
というメソッドを作成(UpdateUser
クラスとid
を取得)for
ループを使用して、渡されたid
に紐付けられたユーザーがデータベースに存在するかどうかをチェック- ユーザーのパラメータのいずれかが
is not None
(nullでない) かどうかをチェック(first_name
、last_name
、roles
など、いずれかのパラメータがnullでない場合そのパラメータが変更される) - 操作に成功すると、ユーザーIDが返される
- ユーザーが見つからなかった場合、ステータスコード404(そしてメッセージ
{id}に該当するユーザーが見つかりませんでした
)のHTTPException
が表示される
このエンドポイントのテストの際には、Uvicornサーバーが稼働していることを確認しましょう。起動していない場合には、次のコマンドを入力してください。
uvicorn main:app --reload
テスト結果のスクリーンショットが以下の通りです。
まとめ
今回の記事では、PythonのFastAPIフレームワークの基礎、そして、FastAPI を使用したアプリケーションの素早い立ち上げの例をご紹介しました。具体的には、フレームワークを使用した、データベースレコードの作成、読み出し、更新、削除といったCRUD APIエンドポイントの構築を扱いました。
ウェブアプリ開発を次のレベルに引き上げるには、アプリケーションホスティングとデータベースホスティングをはじめとする、Kinstaのプラットフォームをご活用ください。FastAPIと同様に、シンプルかつ高性能です。
コメントを残す