はじめに
私は普段レガシーなWISAスタック1で開発をしていますが、訳あってPythonに興味がありFastAPIでアプリを作ってみたいと思いました。
ちょうどその頃社内で雑談チャットが運用開始になり、そこに毎日お題を出せたらと思って大喜利用お題箱アプリを作れたらと思ってプライベートで開発をはじめた状態です。
作っているアプリ
「お題くん」という大喜利用お題投稿アプリです。
https://github.com/huideyeren/odaikun
このアプリに投稿したお題を、決められた時間にチャットに投稿するというものです。
基本的には、様々なチャットに対してWebhookを投げるだけですが。
なお、現状未完成の上フロントエンド側については全く手を付けられていません。
なぜFastAPIを選んだのか
ざっと、以下のような理由があります。
- Pythonコミュニティとのご縁があった2のでPythonで開発したかった
- 今携わっている業務がレガシーな感じなので、モダンなスタイルで開発したかった
- FastAPIはAPIドキュメント生成機能があると聞いたので、ドキュメントが減らせると思った
よかったこと
さて、ここからはFastAPIすごいなと思ったところを書いていきます。
APIドキュメント標準添付で「ドキュメント書きたくないでござる系開発」に近づけた
FastAPIにはOpenAPIドキュメントを生成する機能があります。これによって、APIのドキュメンテーションだけではなくAPIの手動テストもできるようになります。
もっともC#の世界でも.NET 5.0以降のASP.NET Core WebAPIでOpenAPIドキュメントが標準添付になりましたが、私が開発をはじめたときはプレビュー版しかリリースされていなかったので使いませんでした。
また、PythonエコシステムにはSphinxというドキュメンテーションツールがあります。この使い勝手がなかなかよく、これを使うことによって苦手なExcel方眼紙を書かずに済むという狙いもありました。実際に、日本語ではありますがdocstringを書いてちゃんとドキュメントにできる体制は整えています。
これでPythonの世界の中はドキュメントを書きやすい状態になったわけです。
何よりモダン
FastAPIはDockerを使うことを前提にしたプロジェクトテンプレートが用意されています。
ちなみに、私はこのテンプレートを使いました。
https://github.com/Buuntu/fastapi-react
これの使い方としては以下の通りです。なお、Dockerは事前にインストールしておくことが必要です。
# cookiecutterをインストールする
$ pip3 install cookiecutter
# cookiecutterでテンプレートからプロジェクトを生成する
$ cookiecutter gh:Buuntu/fastapi-react
# ビルドするスクリプトに実行権限を与える
$ chmod +x scripts/build.sh
# コンテナをビルドする
$ ./scripts/build.sh
# コンテナを起動する
$ docker-compose up -d
これだけでFastAPIとReactを使ったアプリ開発が行えてしまうのです。しかもユーザー認証機能付きで。
なお、業務で携わっているシステムではまだ以下のようなものが無い状態です。
- CI/CD
- ユニットテスト
- データベースのマイグレーション
- フロントエンドとバックエンドの分離
- Dockerによるコンテナ開発
- その他「手作業」を必要とするトイル
これらの課題を乗り越えること前提のモダンな開発をできてよかったと思っています。もっとも、ユニットテストは現状全部用意できませんでしたが。
FastAPIで困ったこと
ここからは、困ったことを上げていこうと思います。
フルスタックなフレームワークと勝手が違う
私はASP.NET MVCやDjangoのようなフルスタックなフレームワークは触ったことがあるのですが、FastAPIのようなマイクロフレームワークが初めてだったので最初は相当苦労しました。
何せ、MVCアーキテクチャでいうところのビューがなく、ビューに当たる部分はReactなどのNode.jsを使ったJavaScriptエコシステムで構築する必要があるのです。
うまく作るにはバックエンドとフロントエンドの両方の技術をバランスよく覚える必要がありますが、まだReactには手が出せず……ということでしばらく放置になってしまっているのが現状です。
とりわけ、苦心したのがファイルの分割です。MVCアーキテクチャーをベースとしたアプリケーションではだいたい画面ごとにModelとViewとControllerに分けますが、FastAPIでは規模が大きくなった場合どのように分けるべきか迷うところが少々ありました。結局、以下のようなディレクトリ構成になったのですが、元々テンプレートでの構成をほぼ守っています。
.
└── app
├── alembic
│ └── versions
├── api
│ ├── api_v1
│ │ └── routers
│ │ └── tests
│ └── dependencies
├── core
├── db
│ ├── crud
│ └── schemas
└── tests
Pythonで型のあるプログラミングが厳密にできなかったこと
私が普段書いているC#では静的型付けなので、dynamic型3を使わない限りは型付けに矛盾があるとビルドで失敗します。
Pythonもmypyなどで型チェックを行いながら書けるのですが、とりわけPydanticとSQLAlchemyのあたりでこれはDBのモデルかAPIのモデルか混乱することがたまにありました。
以下のコードはデータベースからお題情報を取得するときのコードです。
def get_topic(db: Session, topic_id: int):
"""
get_topic 指定されたIDのお題を取得する
Args:
db (Session): DB接続
topic_id (int): お題のID
Raises:
HTTPException: お題が見つからない旨のHTTP 404 エラー
Returns:
Topic: 指定されたIDのお題
"""
topic = db.query(models.Topic).filter(models.Topic.id == topic_id).first()
if not topic:
raise HTTPException(status_code=404, detail="Topic not found")
return topic
このtopicってSQLAlchemyの型なのかPydanticの型なのかがわからず、結果としてAny型のまま進んでしまっています。これでは型を厳格にした意味が無くなってしまうのです。
ドキュメントを書かずに済むわけではなかった
いざドキュメントを生成してみると、Sphinxで生成されるドキュメントはバックエンドの開発者向けドキュメントが中心でした。フロントエンド側のドキュメントも必要だし、エンドユーザー向けのドキュメントも必要です。そうなると、Sphinxで全て書いてしまってよいのかという問題になります。
ただ、現状この問題に対しては解決策を持っていないのが現状です。開発が進めば解決策も浮かんでくると思います。
まとめ
FastAPIは書いていて楽しいフレームワークなので、社内の技術縛りが無ければ採用の候補に入れられたらよいと思っています。
というか、まだまだ開発途中なのでアドベントカレンダー書きが一段落したら開発を進めたいと思います。