こんにちは。 DSOC R&D グループの高橋寛治です。
私は Python で WebAPI を開発する際には、Flask をよく利用しています。 最近は、FastAPI がいいらしいという話を同僚から聞くようになりました。 少し公式ドキュメントを覗いてみると、Flask をより便利に高機能にしたものに感じました。
これは使わねばと思い、開発環境の構築からテストまでやってみたため、ここで紹介します。 (しっかり理解したい場合は、充実した公式のチュートリアルがおすすめです。ドキュメントがしっかり整備されています。)
FastAPI
FastAPI は、Python の Webフレームワークです。 特徴の詳細については、公式ドキュメントに列挙されています。
以下に私が触った感想を列挙します。
- 記法が簡単かつ直感的
- Flask と似ている箇所もあり、扱いやすいです
- API ドキュメントの自動生成が便利
- 管理が煩雑になりがちな API ドキュメントをコードから自動生成します
- FastAPI のドキュメントがしっかりしている
- かなり詳細に書かれており親切です
一言でまとめると、Flask から FastAPI への移行を決意させる魅力的な機能ばかりです。
開発環境の構築
今回は、次を満たす環境とします。
- Python パッケージ化する
- pytest でテストを行う
次のようにディレクトリを構築します。
. ├── requirements.txt ├── setup.py ├── src │ └── sample │ ├── __init__.py │ └── api.py └── tests ├── __init__.py └── test_api.py
あとは API とテストを書くだけという状況にするために、 requirements.txt
, setup.py
を次のように書いて、Python パッケージ化します。
requirements.txt
fastapi[all]==0.63 uvicorn[standard]==0.13 pydantic==1.7.3 pytest==6.2.2
setup.py
from glob import glob from os.path import basename from os.path import splitext from setuptools import setup from setuptools import find_packages def _requires_from_file(filename): return open(filename).read().splitlines() setup( name="sample", version="0.1.0", license="", description="", author="Sansan, Inc.", url="", packages=find_packages("src"), package_dir={"": "src"}, py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], include_package_data=True, zip_safe=False, install_requires=_requires_from_file('requirements.txt'), setup_requires=["pytest-runner"], tests_require=["pytest", "pytest-cov"] )
そしてプロジェクトのルートで、 pip install -e .
で開発用に Python ライブラリとしてインストールして、環境構築は終了です。
エンドポイントを作成する
api.py
に簡単なエンドポイントを定義します。
from fastapi import FastAPI app = FastAPI( title="Sample API", description="This is a simple sample api.", version="1.0.0" ) @app.get("/") def read_root(): return {"message": "Sample API"}
Flask とほとんど同じです。
流れとしては、 FastAPI モジュールをインポートし、 app という変数にインスタンスを代入します*1。
その後、app
に対して、ルートへの GET アクセスを read_root
メソッドに紐付けます。
紐付けはデコレータによる視覚的にわかりやすい記法となっています。
さて、次のコマンドで動かします。
ここで sample
は Python パッケージの src/sample
の sample
です。
$ uvicorn sample.api:app --reload --port 8000
ブラウザもしくは curl で、期待したレスポンスが返ってくるか確認しましょう。
$ curl localhost:8000 {"message":"Sample API"}
意図したメッセージが返ってきました。
テストを行う
test_api.py に次のように記述しテストを行います。
from fastapi.testclient import TestClient from sample.api import app client = TestClient(app) def test_read_root(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"message": "Sample API"}
流れとしては、テスト用のクライアントを作成し、ルートへの GET アクセスを行い、そのステータスコードやレスポンスの中身を確認します。
テストの実行は、プロジェクトのルートで pytest
コマンドを実行し、行います。
API ドキュメントを見る
GET リクエストでパラメータ を送り、その結果を取得するというエンドポイントを定義します。 また、定義したエンドポイントについてAPI ドキュメントが自動生成されていることを確認します。
test_api.py
に次のテストを追加します。
def test_get_document(): response = client.get("/document/1") assert response.status_code == 200 assert response.json() == { "id": "1", "title": "Sample Title", "date": "2021-01-01", }
document
というリソースの1を取得すると、Document
が取得できるというものです。
これを api.py
に実際に定義します。なお、 Document
の内容をここでは固定値とします。(実際は DB などから取得します)
### api.py に以下を追記 import datetime from pydantic import BaseModel, Field class Document(BaseModel): id: str = Field(..., title="文書のID", example="1") title: str = Field(..., title="文書のタイトル", example="Sample Title") date: datetime.date = Field(..., title="日付", example="2021-01-01") @app.get( "/document/{document_id}", response_model=Document, description="Read a document", ) def read_document(document_id: str): document = Document( id=document_id, title="Sample Title", date=datetime.date(2021, 1, 1) ) return document
pydantic と呼ばれるライブラリで Document
を定義します。
それぞれの変数について、値の例や説明、期待する初期値など柔軟に記述できます。
ここで変数に対して記述した内容がAPI ドキュメントに表示されます。
定義した Document
とエンドポイントの紐付けは、デコレータで行います。
ここだと、response_model
という引数によりエンドポイントの戻り値が Document
型であることを示します。
さて、pytest
でテストが通ることを確認してから、API ドキュメントを閲覧します。
ブラウザで、 localhost:8000/docs
を閲覧します。
そして、 GET
の /document/{document_id}
となっているところを展開してみましょう。
このように、先ほど Python コードで定義したエンドポイントとエンドポイントのパラメータが表示されます。 コードと API ドキュメントの管理が、コードから生成されることはいいですね*2。
「Try it out」を押すと、この画面から API リクエストを送り、結果を確認することができます。
document_id
に 2 を設定し、実行してみました。
Response body も期待通りの値となっています。
おわりに
FastAPI は非常に簡単かつ簡潔に API エンドポイントの定義および実装が行えます。 また、公式ドキュメントが非常に充実しており、理解や導入がしやすいです。
私なりに例を考えてみましたが、テストまで一連で行うという点以外は、公式ドキュメントでも同様の流れで記述されています。
リクエストとレスポンスの型を Python コードとして簡単に定義できる点は、モデル作成者が OpenAPI 記法や使い方を覚えなくて済むため、実務でも運用負荷が減りそうなイメージです*3。
簡単・便利・手厚いドキュメントということで、今後利用がますます増えていくであろう FastAPI について、環境構築からテストまでを紹介しました。
執筆者プロフィール
高橋寛治 Sansan株式会社 DSOC (Data Strategy & Operation Center) 研究開発部 研究員
阿南工業高等専門学校卒業後に、長岡技術科学大学に編入学。同大学大学院電気電子情報工学専攻修了。在学中は、自然言語処理の研究に取り組み、解析ツールの開発や機械翻訳に関連する研究を行う。大学院を卒業後、2017年にSansan株式会社に入社。キーワード抽出など自然言語処理を生かした研究開発に取り組む。
▼本連載のほかの記事はこちら