フロントエンド配信(app.frontend)¶
FastAPI 0.138.0 で app.frontend() が追加された。ビルド済みのフロントエンド(HTML / CSS / JS)を 1 行で配信するためのメソッド。StaticFiles と違って追加の import が要らないし、 API のルートと同じアプリ・同じポートに同居させても API が隠れない。
ファイル構成¶
main.py と、フロントエンド用の dist/ を用意する。 dist/ の中に HTML と JS を置くだけにして試してみる。
main.py¶
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist")
@app.get("/inc")
def inc(x: int):
return {"result": x + 1}
app.frontend(配信パス, directory=配信するディレクトリ) で、 dist/ の中身を / 以下で配信する。
FastAPI は path operation(@app.get などで定義したルート)を先にチェックして、どれにもマッチしなかったリクエストだけを dist/ にフォールバックさせる。だから /inc や /docs は今まで通り API・Swagger UI として動くし、それ以外のパスがフロントエンドになる。
dist/ は先に作っておく
app.frontend() は起動時に directory の存在を確認する(check_dir=True がデフォルト)。 dist/ が無いと起動に失敗するので、先に下の HTML と JS を作っておく。存在チェックを後回しにしたいときは check_dir=False を渡す。
dist/index.html¶
数字を入れて +1 ボタンを押すと結果を表示するだけのページ。 /app.js を読み込む。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>+1 calculator</title>
</head>
<body>
<h1>+1 calculator</h1>
<input id="x" type="number" value="0">
<button id="run">+1</button>
<p id="result"></p>
<script src="/app.js"></script>
</body>
</html>
dist/app.js¶
ボタンが押されたら /inc?x=... を叩いて、返ってきた result を画面に出す。
const input = document.getElementById("x");
const button = document.getElementById("run");
const result = document.getElementById("result");
button.addEventListener("click", async () => {
const response = await fetch(`/inc?x=${input.value}`);
const data = await response.json();
result.textContent = `result: ${data.result}`;
});
フロントエンドと API が同じオリジン(同じホスト・ポート)なので、 CORS の設定は要らない。
動かす¶
ブラウザで http://localhost:8000/ を開くと index.html が表示される。数字を入れて +1 を押すと、 /inc のレスポンスがそのまま画面に出る。

それぞれのパスがどこに向くかは下のとおり。
| パス | 中身 |
|---|---|
/ |
dist/index.html |
/app.js |
dist/app.js |
/inc?x=1 |
API({"result": 2})。path operation が優先されるので dist/ は見に行かない |
StaticFiles との違い¶
似たことは StaticFiles でもできるけど、いくつか手間が違う。
- import:
StaticFilesはfrom fastapi.staticfiles import StaticFilesが要る。app.frontend()はFastAPIだけで使える。 - 優先順位:
StaticFilesを/にマウントすると、その配下のパスはマウントが受け持つので、 API ルートと同居させるときに順番を気にする必要がある。app.frontend()は path operation を先にチェックすると決まっているので、 API の邪魔をしない。 - SPA 対応:
app.frontend()は存在しないパスをindex.htmlや404.htmlに流すfallbackを持っている。
SPA のフォールバック¶
React や Vue などでクライアントサイドルーティングを使うときは fallback を指定する。
fallback="auto"(デフォルト) …dist/に404.htmlがあればそれを、無ければindex.htmlをフォールバックに使う。fallback="index.html"… 存在しないパスへのブラウザのナビゲーションをindex.htmlに流して、フロント側のルーターに処理させる。 JS や CSS、画像など実ファイルが無い場合は404のまま。fallback="404.html"…404.htmlを404ステータスで返す。fallback=None… フォールバックしない(通常の404)。
今回のような単一の index.html のデモはデフォルト(auto)のままで動く。例えば存在しない /not-exist をブラウザで開くと、 URL は /not-exist のまま index.html が返る。一方、 /favicon.ico のような存在しないアセットは 404 のまま。