• #architecture
  • #tech-stack
  • #streamlit
  • #fastapi
  • #htmx
  • #nuxt
未分類

サービス高速立ち上げの技術選定ガイド

TL;DR(結論を先に)

サービス特性推奨スタック
AI/MLデモ、データ分析ダッシュボードStreamlit
AI + カスタムUI、業務アプリFastAPI + htmx
コンテンツサイト、SEO重要、長期運用Nuxt
リアルタイム性、複雑なUI状態Nuxt or React

3つのスタック概要

1. Streamlit

pip install streamlit
streamlit run app.py

特徴:

  • Pythonだけで完結(HTML/CSS/JS不要)
  • ウィジェット(スライダー、ボタン等)が組み込み
  • 状態管理が自動(再実行ベース)
  • Streamlit Cloud で無料デプロイ

コード例:

import streamlit as st
import pandas as pd

st.title("売上ダッシュボード")
uploaded = st.file_uploader("CSVをアップロード")
if uploaded:
    df = pd.read_csv(uploaded)
    st.line_chart(df["revenue"])

2. FastAPI + htmx

pip install fastapi uvicorn jinja2

特徴:

  • Python統一だがHTML/CSSは書く
  • htmxでJS不要の非同期通信
  • API設計が残るので本番移行しやすい
  • UIの自由度が高い

コード例:

@app.get("/search")
async def search(q: str):
    results = await db.search(q)
    return templates.TemplateResponse(
        "partials/results.html",
        {"results": results}
    )
<input hx-get="/search" hx-target="#results"
       hx-trigger="keyup changed delay:300ms">
<div id="results"></div>

3. Nuxt (Vue)

pnpm create nuxt-app
pnpm dev

特徴:

  • フルスタックフレームワーク
  • SSR/SSG対応でSEO強い
  • Vue エコシステム(コンポーネント、状態管理)
  • TypeScript完全サポート

比較表

観点StreamlitFastAPI + htmxNuxt
学習コスト最小(Pythonのみ)中(HTML/CSS必要)高(Vue/TS/SSR)
立ち上げ速度最速(数時間)速い(1-2日)普通(数日)
UIカスタマイズ限定的自由完全に自由
本番移行難しいしやすいそのまま使える
SEO×△(SSR可能)
リアルタイム更新△(再実行ベース)○(SSE/WebSocket)
複雑な状態管理
AI/ML連携◎(Python直接)◎(Python直接)△(API経由)

サービス特性別の選定フローチャート

図を読み込み中...

ユースケース別の推奨

Streamlit が最適なケース

  1. AI/MLモデルのデモ
    • 画像認識、テキスト生成のプロトタイプ
    • モデルのパラメータ調整UI
  2. データ分析ダッシュボード
    • CSVアップロード → 可視化
    • 社内向けBIツール
  3. PoC(概念実証)
    • 投資家向けデモ
    • 1日で動くものを見せたい

向いていないケース:

  • ユーザー認証が複雑
  • 一般公開でSEOが必要
  • 見た目の差別化が重要

FastAPI + htmx が最適なケース

  1. AI搭載の業務アプリ
    • LLMチャットアプリ(カスタムUI)
    • AIアシスタント付きの管理画面
  2. 社内ツール・管理画面
    • CRUD操作メイン
    • フォーム入力 → DB保存
  3. Streamlitからのステップアップ
    • PoCが通って本番化したい
    • UIをブランドに合わせたい

向いていないケース:

  • SEOが最重要
  • 複雑なクライアント状態(ドラッグ&ドロップ、リアルタイム協調編集)
  • TypeScript必須の要件

Nuxt が最適なケース

  1. コンテンツサイト・ブログ
    • SEO重要
    • マークダウンベースのコンテンツ
  2. 長期運用するWebアプリ
    • チーム開発
    • 型安全性が重要
  3. 複雑なインタラクション
    • リアルタイムチャート
    • ドラッグ&ドロップUI
    • 複数コンポーネント間の状態同期
  4. エコシステム活用
    • 既存のVueコンポーネントを使いたい
    • Chart.js, D3.js などの統合

向いていないケース:

  • Pythonエンジニアしかいない
  • 数時間で動くものが必要
  • AI/MLと密結合

htmx vs Vue/Nuxt: 状態管理の観点から

htmxとVue/Nuxtは同じレイヤー(フロントエンド)の選択肢である。どちらを選ぶかは「状態をどこに持つか」で決まる。

思想の違い

観点htmxVue/Nuxt
状態の置き場所サーバークライアント
更新の仕組みサーバーがHTML返却クライアントでリアクティブ更新
通信頻度操作ごとにサーバー往復必要時のみAPI呼び出し

htmx を選ぶべきケース

  1. 表示がメインで操作が少ない
    • ユーザープロフィール表示
    • 履歴・ログ一覧
    • 検索結果表示
  2. フォーム送信が中心
    • お問い合わせフォーム
    • 設定変更画面
    • 管理画面のCRUD
  3. チームがPythonのみ
    • フロントエンド専門家がいない
    • Vue/React の学習コストを避けたい
  4. SEOが不要な社内ツール
    • 管理画面
    • 社内ダッシュボード
    • バックオフィスツール
  5. シンプルさを優先
    • ビルドプロセスなし
    • node_modules 不要
    • デバッグが直感的

htmx の具体例:

<!-- 検索: サーバーが結果HTMLを返す -->
<input hx-get="/search" hx-target="#results"
       hx-trigger="keyup changed delay:300ms">

<!-- 削除: サーバーが空HTMLを返して消える -->
<button hx-delete="/items/123" hx-target="closest tr"
        hx-swap="outerHTML">削除</button>

<!-- 無限スクロール -->
<div hx-get="/items?page=2" hx-trigger="revealed"
     hx-swap="afterend">Loading...</div>

Vue/Nuxt を選ぶべきケース

  1. 複数コンポーネント間の状態共有
    • ヘッダーのカート数 + 商品一覧 + サイドバー
    • 一箇所の変更が複数箇所に反映
  2. 楽観的UI更新(Optimistic UI)
    • ボタン押下で即座に見た目変更
    • バックグラウンドでサーバー同期
    • 失敗時にロールバック
  3. リアルタイム・高頻度更新
    • 株価チャート
    • チャットアプリ
    • 通知バッジ
  4. 複雑なフォーム
    • マルチステップウィザード
    • 条件分岐するバリデーション
    • 動的にフィールド追加/削除
  5. ドラッグ&ドロップ、アニメーション
    • タスク並び替え
    • ファイルアップロードUI
    • スムーズなトランジション
  6. オフライン対応・PWA
    • Service Worker連携
    • ローカルキャッシュ

Vue/Nuxt の具体例:

<script setup>
const cart = useCart()  // 複数コンポーネントで共有
const { addToCart, isAdding } = useAddToCart()

// 楽観的更新: 即座にUIに反映、後でサーバー同期
async function handleAdd(product) {
  cart.items.push(product)  // 即座に追加
  try {
    await addToCart(product.id)
  } catch {
    cart.items.pop()  // 失敗したら戻す
  }
}
</script>

<template>
  <!-- ヘッダーのカート数も自動更新 -->
  <Header :cart-count="cart.items.length" />

  <button @click="handleAdd(product)" :disabled="isAdding">
    カートに追加
  </button>
</template>

機能別の判断表

機能htmxVueNuxt推奨
一覧表示どれでもOK
検索・フィルター(単純)どれでもOK
検索・フィルター(複数条件組み合わせ)Vue/Nuxt
フォーム送信どれでもOK
マルチステップフォームVue/Nuxt
ユーザー認証(基本)Vue/Nuxt がやや楽
カート・お気に入り(即時反映)Vue/Nuxt
リアルタイム通知Vue/Nuxt
ドラッグ&ドロップ×Vue/Nuxt
オフライン対応×Nuxt(PWA対応)
複雑なチャート連動Vue/Nuxt
アニメーションVue/Nuxt
SEO(検索エンジン対策)×Nuxt(SSR/SSG)
OGP・SNSシェア×Nuxt
静的サイト生成××Nuxt
ルーティング自動生成××Nuxt

Vue vs Nuxt の使い分け

観点Vue (SPA)Nuxt
SEO×(クライアントレンダリング)◎(SSR/SSG)
初期表示速度△(JS読み込み後に描画)◎(サーバーでHTML生成)
構成の自由度◎(自分で決める)○(規約あり)
セットアップ自分で構成自動で揃う
学習コスト高(Vue + Nuxt両方)

判断:

  • SEOが必要 → Nuxt 一択
  • 社内ツール・管理画面(SEO不要) → Vue で十分
  • ファイルベースルーティングが欲しい → Nuxt
  • 既存APIに繋ぐだけのフロント → Vue で十分

Nuxt で得られるもの(SEO以外)

SEO以外にも Nuxt を選ぶメリットは多い。

1. 初期表示速度(Core Web Vitals)

指標Vue SPANuxt SSR/SSG
FCP (First Contentful Paint)遅い(JS実行後)速い(HTML即表示)
LCP (Largest Contentful Paint)遅い速い
CLS (Cumulative Layout Shift)発生しやすい制御しやすい

→ ユーザー体験とGoogle検索順位の両方に影響

2. ファイルベースルーティング

pages/
├── index.vue          → /
├── about.vue          → /about
├── blog/
│   ├── index.vue      → /blog
│   └── [slug].vue     → /blog/:slug

→ ルーター設定を書く必要なし、ディレクトリ構造がそのままURL

3. 自動インポート

<script setup>
// import { ref, computed } from 'vue'  ← 不要
// import { useRoute } from 'vue-router' ← 不要
const count = ref(0)
const route = useRoute()
</script>

→ import文の管理が不要、コードがすっきり

4. サーバーAPI(Nitro)

server/
├── api/
│   └── users.ts       → /api/users

→ 別途バックエンドを立てなくてもAPIが書ける(フルスタック)

5. ビルド時データフェッチ(SSG)

<script setup>
// ビルド時に実行、静的HTMLに埋め込み
const { data } = await useAsyncData('posts', () =>
  queryContent('/blog').find()
)
</script>

→ CDNから静的配信、サーバー負荷ゼロ、爆速

6. エコシステム(Nuxt Modules)

モジュール機能
@nuxt/contentMarkdown/MDXでコンテンツ管理
@nuxt/image画像最適化、自動WebP変換
@nuxtjs/i18n多言語対応
@vueuse/nuxt便利なComposables集

→ nuxt.config.ts に1行追加で機能追加

SEO以外で Nuxt を選ぶ理由まとめ

理由説明
表示速度重視SSR/SSGで初期表示が速い
規約による生産性ルーティング・インポート自動化
フルスタックAPI + フロント + コンテンツを一箇所で
モジュールエコシステム機能追加が楽
長期運用構成が標準化されチーム開発しやすい

Vue SPA で十分なケース(条件をすべて満たす場合)

Vue SPA を選ぶには以下の条件をすべて満たす必要がある:

条件説明
SEOが不要社内ツール、管理画面、ログイン後のみ使うアプリ
初期表示速度が重要でない多少の待ち時間が許容される
Nuxtの便利機能が不要ファイルベースルーティング、自動インポートなど
構成を自由に決めたいNuxtの規約に縛られたくない

具体例:

┌─────────────────────────────────────────────────────┐
│ ケース: 社内管理画面(既存APIに接続)                 │
├─────────────────────────────────────────────────────┤
│ ・SEO不要(ログイン必須)                ✓           │
│ ・初期表示速度は許容範囲                 ✓           │
│ ・シンプルな構成で十分                   ✓           │
│ → Vue SPA で十分                                    │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│ ケース: 一般公開サービス(既存APIに接続)             │
├─────────────────────────────────────────────────────┤
│ ・SEO必要(検索流入が欲しい)            ✗           │
│ → バックエンドが別でも Nuxt を使うべき              │
└─────────────────────────────────────────────────────┘

重要: 「バックエンドが別にある」だけでは Vue SPA で十分とは言えない

バックエンドが別 +選択
SEO不要Vue SPA で十分
SEO必要Nuxt
初期表示速度重視Nuxt
長期運用・チーム開発Nuxt(規約で統一)

判断フローチャート(状態管理観点)

Q: ユーザー操作後、即座にUIを更新したい?
├─ No(サーバー応答待ちでOK) → htmx で十分
└─ Yes → Q2へ

Q2: 複数の場所が同時に更新される?
├─ No(1箇所だけ更新) → htmx で十分
└─ Yes(ヘッダー + 本文 + サイドバーなど) → Vue/Nuxt

Q3: オフラインでも動作させたい?
├─ No → 上記の判断に従う
└─ Yes → Vue/Nuxt 一択

実際の判断例

例1: LLMを使ったテキスト要約サービス

要件:

  • ユーザーがテキストを入力 → LLMで要約
  • 社内利用から開始、将来は一般公開

判断:

  • 初期: Streamlit で爆速PoC
  • 本番化: FastAPI + htmx でカスタムUI
  • 一般公開時: フロントを Nuxt に置き換え(APIはそのまま)

例2: 株価分析ダッシュボード

要件:

  • 複数チャートの連動
  • 指標の選択・軸の切り替え
  • 一般公開でSEOも欲しい

判断:

  • Nuxt 一択
  • Chart.js/ECharts のVue連携が活きる
  • SSRでSEO対応

例3: 画像認識AIのデモ

要件:

  • 画像アップロード → 認識結果表示
  • 投資家プレゼン用

判断:

  • Streamlit 一択
  • st.file_uploader() + モデル推論
  • 30分で動くものが作れる

移行パス

Streamlit → FastAPI + htmx → Nuxt (フロント) + FastAPI (API)
   ↓              ↓                    ↓
 数時間         1-2日               1週間〜
  PoC          MVP/社内利用        本番・一般公開

ポイント:

  • FastAPI で作った API は Nuxt からも呼べる
  • 段階的に移行できる設計にしておく

まとめ

状況選択
「とにかく今日動くものを見せたい」Streamlit
「Pythonで完結させたい + UIもこだわりたい」FastAPI + htmx
「長期運用・SEO・複雑なUI」Nuxt
「AI + 一般公開」FastAPI (API) + Nuxt (フロント)

現プロジェクト(mdx-playground)の立ち位置:

  • コンテンツサイト + 財務チャート → Nuxt は正解
  • AI機能を追加する場合 → FastAPI を別途立てて API 連携が現実的