1. はじめに
Webアプリケーションを開発する際、フロントエンドとバックエンドを分離し、JavaScriptを多用したSPA(Single Page Application)として構築する方法が一般的になっています。
一方で、すべてのWebアプリケーションに複雑なフロントエンド環境が必要なわけではありません。特にTodoアプリのようなCRUDを中心としたアプリケーションでは、「サーバー側でHTMLを生成し、必要な部分だけを非同期で更新する」というシンプルな構成も有力な選択肢になります。
そこで本記事では、FastAPIとHTMXを中心に、Todoアプリを実装します。
2. 対象読者 / 想定シーン
本記事は、以下のような方を対象としています。
- 小規模なWebアプリケーションに適した技術構成を検討している方
- PythonでWebアプリケーションを開発してみたい方
- FastAPIとhtmxを組み合わせた開発に興味がある方
- ReactやVueなどを使わずにWeb UIを構築する方法を知りたい方
Pythonの基本的な文法に加えて、HTTPやHTML、Web APIについて基本的な知識があると、理解しやすくなります。
3. 成果物
↓リポジトリ
4. 設計 / 採用技術
今回のアプリケーションにおける、採用技術と、その用途は以下の通りです。
| 技術 | 用途 |
|---|---|
| FastAPI | Web フレームワーク |
| SQLModel + SQLite | ORM によるデータモデル定義とデータ永続化 |
| HTMX | サーバーが返す HTML 断片による非同期 UI 更新 |
| Jinja2 | HTML テンプレートエンジン |
| Alpine.js | 軽量な UI インタラクション(トグル・モーダル等)の付与 |
| Tailwind CSS | ユーティリティベースのスタイリング |
| daisyUI | Tailwind 上に構築された UI コンポーネント・テーマ |
5. 技術の棚卸
技術トレンド整理
ここでwebアプリケーション開発のトレンドを整理します。
ざっくり時代で見ると、
| 時代 | 典型的な構成 | 特徴 |
|---|---|---|
| 2000年代 | PHP / JSP / Rails | サーバーサイドHTML生成 |
| 2010年代前半 | Flask/Django + Jinja2 | PythonでHTML生成 |
| 2010年代後半 | React/Vue/Angular + REST API | SPA、JavaScript中心 |
| 2020年代 | REST API + React/Next.js | API + SPA/SSR |
| 最近 | FastAPI + Jinja2 + HTMX + Alpine.js | サーバー中心+必要な部分だけ動的化 |
面白いのは、最近のHTMX系は「昔に戻った」ようにも見えることです。
ただし完全に昔に戻ったわけではありません。
HTMX → Ajax的な部分更新
Alpine.js → UI操作
Tailwind + daisyUI → 見た目
これらの技術の進展により、"かなり現代的なUIを作れる"、というのが大きな変化ととらえられます。
レンダリングの違い
初回表示をシーケンス図にすると、両者の違いがかなり明確になります。
特に比較したいポイントは、
- React/Vue/Angular → JSONを受け取り、ブラウザ側でHTMLを生成・更新
- FastAPI + Jinja2 + HTMX → HTMLをサーバー側で生成し、ブラウザ側はDOMを更新
です。
- React / Vue / Angular + REST APIの画面表示まで
- FastAPI + Jinja2 + HTMXの画面表示まで
6. コード解説
FastAPI+HTMX構成における主要技術をコードとともに見ていきます。
- Jinja2
PythonのデータをHTMLに埋め込み、サーバー側でHTMLを生成するテンプレートエンジンです。本プロジェクトは以下の様な場面で使用しています。
from fastapi.templating import Jinja2Templates
# Jinja2テンプレートエンジンを初期化
# "app/templates" ディレクトリ配下のHTMLファイルをテンプレートとして使用する
templates = Jinja2Templates(directory="app/templates")
# ルートパス("/")へのGETリクエストを処理するエンドポイント
# response_class=HTMLResponse を指定し、HTMLを返すことを明示
@app.get("/", response_class=HTMLResponse)
async def index(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
request=request,
name="index.html",
context={"todos": get_todos()},
)
- HTMX
HTMLの属性を使ってHTTPリクエストを発行し、サーバーから返されたHTML断片で画面の一部を更新できるライブラリです。本プロジェクトは以下の様な場面で使用しています。
todoの内容をHTTP POSTするシーン
<form
hx-post="/todos"
hx-target="#todo-list"
hx-swap="outerHTML"
hx-indicator="#add-indicator"
hx-disabled-elt="find button[type='submit'], find input[name='title']"
hx-target-4*="#form-error"
hx-target-5*="#form-error"
hx-on::after-request="..."
>
| 属性 | 説明 |
|---|---|
hx-post="/todos" |
フォーム送信時、通常のページ遷移ではなく Ajax(非同期通信) で /todos に POST リクエストを送る |
hx-target="#todo-list" |
レスポンスで返ってきたHTMLを、id="todo-list" の要素に対して差し込む(リクエスト成功時) |
hx-swap="outerHTML" |
差し込み方法の指定。対象要素自体(#todo-list)ごとレスポンスHTMLで置き換える |
hx-indicator="#add-indicator" |
リクエスト中に id="add-indicator" 要素へ htmx-request クラスを付与し、ローディング表示を制御する |
hx-disabled-elt="find button..., find input..." |
リクエスト中、送信ボタンと入力欄を disabled にして二重送信を防ぐ |
hx-target-4*="#form-error" |
4xx系エラー(400番台)のレスポンス時は #form-error にHTMLを差し込む(バリデーションエラー表示用) |
hx-target-5*="#form-error" |
5xx系エラー(500番台)のレスポンス時も同様に #form-error にHTMLを差し込む |
hx-on::after-request="..." |
htmxの独自イベント htmx:afterRequest 後に実行するJS。成功時(event.detail.successful)にフォームをリセットし、エラー表示をクリアする |
- Alpine.js
少量のJavaScriptで、モーダルやトグルなどのUI状態・インタラクションを手軽に追加できる軽量なJavaScriptフレームワークです。本プロジェクトは以下の様な場面で使用しています。
リスト表示する状態管理
<li
x-cloak
x-show="
filter === 'all' ||
(filter === 'active' && !{{ 'true' if todo.completed else 'false' }}) ||
(filter === 'completed' && {{ 'true' if todo.completed else 'false' }})
"
>
x-show:条件式がtrueのときだけその要素を表示(falseならdisplay:none)。ここでのfilterは、<main x-data="{ filter: 'all' }">で定義された状態を、子要素であるこの<li>が参照しています。
このようにし、Jinja2がHTMLを生成し、HTMXが通信を担い、Alpine.jsが見た目の状態管理を担う、という役割分担になっていいます。
7. どんなアプリケーションに向いているか
今回FastAPI + HTMXでアプリケーションを作ってみて、どんなアプリなら採用できるか以下の様に整理しました。
FastAPI + HTMXが向いている
- 生産実績ダッシュボード
- 在庫管理
- 検索・一覧・編集中心のテーブル表示中心のアプリ
- CMS
React系を検討したい
- 高度なリアルタイムDashboard
- WebSocketを大量利用するUI
- ドラッグ&ドロップ中心のアプリケーション
- ブラウザ内で複雑な状態を扱うUI
8. まとめ
フロントエンド開発は技術の進化が速く、次々と登場する新しい技術や概念をキャッチアップする必要があります。その一方で、Reactをはじめとしたフロントエンド技術の複雑さから「React疲れ」といった言葉が生まれることもあります。
ただし、Reactを使わないこと自体が目的なのではありません。重要なのは、アプリケーションの要件やUIの複雑さに応じて、必要なフロントエンド技術を適切に選択することだと感じました。
CRUDを中心とした比較的シンプルなWebアプリケーションであれば、FastAPI + Jinja2 + HTMX + Alpine.jsのようなサーバー中心の構成でも、十分に現代的で使いやすいUIを実現できます。
アプリケーションの複雑さに合わせて「どこまでフロントエンドを複雑にする必要があるのか」を判断することも、Webアプリケーション開発において重要な知識だと感じました。
一言
FastAPIのコンテナ1つ用意すればデプロイできる + フロントエンドのビルド作業ななくなる点は、かなり魅力的でした。
