序
この記事を書いた半年ほど前(2024/6)の時点では、「検証用のちょっとしたWebアプリをVite
とReact
の組み合わせで作ることが多」かったのですが、最近は猫も杓子もAIであり、そうすると検証用のアプリといえばそれも大体AIであり、となれば必然的にバックエンドをPythonにしたくなるわけです。1
そういうわけで、いろいろ開発環境を整備したり模索したりしていたのですが、ある程度「快適」と言えるところまで来たので、記事にしました。
本格的で大規模な開発用ではなく、ちょっとしたツールや小規模なWebアプリの構築を目的としているので、そういうのを求めている人には先にごめんなさいしておきます。
2024年11月時点の情報です。プラットフォームは問わないと思いますが、私が個人的にLinux、Windows環境で試した限りにおいて問題なく動作している、ということに留意ください。
最終的なツールスタック
こうなります。
開発用ツール
- uv - Rust製のPythonバージョン管理・パッケージマネージャ
- Bun - 高速なJavaScriptランタイム兼パッケージマネージャ
-
Task - Go製のタスクランナー
- uvにタスクランナーがついたらこれはなくてもいいかも
バックエンド
-
FastAPI - StarletteとPydanticをベースとしたPython製Webアプリケーションフレームワーク
- Flaskでもいいと思います(未検証)
- 個人的にはFastAPIのほうが"薄い"感じで好みです
- Uvicorn - Python用ASGI Webサーバー
フロントエンド
静的サイトにビルドできるならフレームワークは別になんでもいいと思いますが、開発の快適さから言うとViteが使えるとよいです。今回はなんだかんだで結構手に馴染んできたRemix v2の後継であるReact Routerのv7(プレリリース) を使っています。
-
React Router (v7) - フルスタック(になった)Webアプリケーションフレームワーク
Routerとは- 2024年11月現在、まだプレリリースなので、気になる場合はRemix v2のSPAモードでもよいです。ほとんどいっしょ
- 今回はSSR無しで使います
ビルダー(optional)
-
Nuitka - Pythonスクリプトをスタンドアロンアプリにビルドするツール
- 身近な人に試してもらうとき用
- 非開発者のPCにPythonは入っていないことが多い2
- 先の記事にも書きましたが、いちいちWebアプリとしてホストするの面倒で…
- 先に有名どころでPyInstallerを試したのですが、exeの起動にえらい時間がかかったのでこちらにしてみました
- NuitkaはNuitkaで、ビルドにかなり時間かかるという問題があります…痛し痒しです
あとはお好みでblackやBiomeあたりのLinter/Formatterを入れましょう。
環境構築
uv
とBun
のインストール
この2つが起点になります。公式のインストール手順に沿ってインストール。
# uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Bun
curl -fsSL https://bun.sh/install | bash
プロジェクトフォルダを作る
# `template-python-spa`の部分は任意
uv init template-python-spa
cd template-python-spa
Pythonのインストール
# バージョン指定する場合は`uv python install 3.12`などにする
uv python install
各種Python系フレームワーク・ツールのインストール
pip install xxx
ではなくuv add xxx
でインストールします
# FastAPI
uv add fastapi
# Uvicorn
uv add "uvicorn[standard]"
# Task
# pipのレジストリにあるのがありがたい
uv add --dev go-task-bin
# Nuitka
uv add --dev nuitka
Pythonのエントリーポイントづくり
uv init
するとディレクトリ内にhello.py
というファイルができるのですが、削除して新たにmain.py
というファイルを作ります。
rm hello.py
touch main.py
import os
import sys
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 開発用CORS
origins = [
"http://127.0.0.1:8000",
"http://localhost:5173",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/hello")
def read_root():
return {"Hello": "World"}
def is_nuitka():
# Nuitkaでコンパイルされているかどうかを確認
return "__compiled__" in globals()
def get_dir():
# ディレクトリパスの取得
if is_nuitka():
return os.path.dirname(os.path.abspath(sys.argv[0]))
else:
return "."
# フロントエンドの配信
frontend_dir = directory = Path(get_dir()) / "frontend/build/client"
# フロントエンドのビルド前にuv run task devしたときにコケないように
if frontend_dir.exists():
app.mount(
"/",
StaticFiles(directory=frontend_dir, html=True),
name="rr",
)
if __name__ == "__main__":
import uvicorn
if is_nuitka():
# Nuitkaで実行時はブラウザを開く
import webbrowser
webbrowser.open("http://127.0.0.1:8000/", new=2, autoraise=True)
uvicorn.run(app, host="127.0.0.1", port=8000)
- ファイルパスを相対パスで指定してたらうまく動作しなかったので、Nuitkaで実行中は絶対パスを指定するようにしています
フロントエンド側の準備
React Router公式のテンプレートがあるので、frontend
フォルダにそれを配置します。
# v7が正式リリースされたら`@pre`は消えると思います
bunx create-react-router@pre frontend
バックエンドはPythonに一任するので、SSRは無効化します。
import { reactRouter } from "@react-router/dev/vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
reactRouter({
- ssr: true,
+ ssr: false, // SPAモードにする
}),
tsconfigPaths(),
],
});
Vite環境変数
開発環境の時はフロントエンドがhttp://localhost:5173
で動くので、APIを叩く処理は開発時のみオリジンをhttp://127.0.0.1:8000
へ置き換える必要があります。
VITE_API_BASE=http://127.0.0.1:8000
こんな感じで設定しておき、fetch
するときに、
const response = await fetch(
`${import.meta.env.VITE_API_BASE}/hello`
);
のように呼び出せばよいです。
Taskfile.yaml
の準備
タスクを設定していきます
version: '3'
tasks:
run:
cmds:
- uv run task build
- uv run main.py
build:
cmds:
- cd frontend && bun install && bun run build
dev:
cmds:
- uv run uvicorn main:app --reload & (cd frontend && bun install && bun run dev)
make:
cmds:
- uv run task build
- nuitka ./main.py --standalone --include-data-dir=./frontend/build/client=./frontend/build/client
uv run task xxx
でタスクが実行されます。開発サーバーの起動はuv run task dev
で、バックエンド・フロントエンドともに、ホットリロードが効きます。
通常のWebアプリとしてホストするときはuv run task run
、Nuitkaでビルドする場合はuv run task make
です。
- Nuitkaには
--onefile
という、必要なファイルを一つの*.exe
に全部入れてくれるオプションがあるのですが、これを使うとどうも一部のマルウェア検知ツールにマルウェア扱いされてしまいます- 社内のシステム管理者に逮捕されたくない場合は、やめておくのが無難かと思います(1敗)
- Nuitkaは現在のところ、クロスコンパイルができません
- Windows用のexeを作りたい場合は、Windows環境でやる必要があります
運用
これでテンプレートとしては完成です。GitHubにでも置いておきましょう。
開発端末上にuv
とBun
が必要になるのがネックかもですが、それさえ乗り越えられれば、git clone
からのuv sync
ですぐ作り始められます。
- 試してはいないですが、おそらくBunを使ってるところは全部
npm
に置き換えられると思うので、Bunはまだちょっと…という方はNodeに差し替えましょう
終わりに
uv
に早くタスクランナーがついてほしいのと、バックエンドがPythonであるElectronの登場が待たれます。3