バックエンドツールチェーン完全ガイド
作成: 2026-06-15 / カテゴリ: バックエンド技術 / 対象プロジェクト: health-moniter
- ASGI(Asynchronous Server Gateway Interface): Python の非同期 Web アプリとサーバーをつなぐ標準インターフェース。旧来の同期版 WSGI の後継。
- アプリ(application): ここでは FastAPI の
appオブジェクト(リクエスト処理ロジックの本体)を指す。 - サーバー(server): ここでは uvicorn(HTTP を受けてアプリを呼び出すプロセス)を指す。「アプリ」と「サーバー」は別物として厳密に区別する。
- 冪等(idempotent): 同じ操作を何度繰り返しても結果(状態)が 1 回実行したときと同じになる性質。詳細は第 3 章。
1. 全体像 ——health-moniter のデータフロー
4 つの道具が「どこに位置し、何を受け渡すか」を 1 枚で押さえる。ブラウザ(React)からのリクエストを uvicorn が受け、FastAPI のロジックが SQLite と外部 API(Open-Meteo)からデータを集め、解析して plotly で可視化用のグラフを返す、という流れ。
| 道具 | 層 | 一言でいうと | health-moniter での役割 |
|---|---|---|---|
| FastAPI | アプリ | API のロジック本体 | 症状記録 API(POST /api/symptoms 等)の定義・検証 |
| uvicorn | サーバー | アプリを動かすプロセス | FastAPI app を実際に起動し HTTP を待ち受ける |
| SQLite | データ | 1 ファイル完結の DB | 症状記録を health.db に永続化。起動時 init_db でスキーマ用意 |
| plotly | 可視化 | グラフ描画ライブラリ | 症状レベル × 気象データの相関グラフ(Phase 3) |
2. FastAPI ——API フレームワーク
FastAPI は Python の Web/API フレームワーク。ASGI 上で動き、関数にデコレータを付けるだけでルートを定義できる。 最大の特徴は pydantic による型定義から、入力バリデーションと API ドキュメント(/docs)が自動生成されること。
最小コード例 ——ルーティングと型バリデーション
# app/main.py from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() # pydantic モデル: 受け取る JSON の「型の設計図」 class Symptom(BaseModel): level: int # 1〜5 の症状レベル note: str | None = None # GET ルート: ヘルスチェック @app.get("/api/health") def health(): return {"status": "ok"} # POST ルート: 症状を記録。body は Symptom 型で自動検証される @app.post("/api/symptoms") def create_symptom(s: Symptom): # level が int でない / 範囲外 などは FastAPI が 422 で自動拒否 return {"saved": True, "level": s.level}
ポイントは 「型を書く=検証ルールとドキュメントを書く」こと。level: int と宣言すれば、文字列 "3" は int に変換され、変換できない値や範囲外は FastAPI が自動で 422 Unprocessable Entity として拒否する。手書きの if 文による検証が要らない。
health-moniter では症状記録 API(POST /api/symptoms など)をこの形で提供する。
3. uvicorn ——アプリを動かす ASGI サーバー
FastAPI で書いた app はそれ自体ではポートを開かない。HTTP を受け取って app を呼び出す「サーバー」が別に必要で、それが uvicorn(ASGI サーバー)。
「FastAPI=アプリ本体」「uvicorn=それを走らせるエンジン」と切り分けて理解するのが肝心。
app(app.main:app)を ASGI 経由で呼び出す。app 単体はポートを開けない。🟢起動コマンドと主要オプション
# app.main の中の app を、port 8000 で起動(開発時) uvicorn app.main:app --reload --port 8000 # ── 引数の意味 ───────────────────── # app.main:app → app/main.py の中の app オブジェクトを指す(モジュール:変数) # --reload → ソースを保存するたびに自動再起動(開発用。本番では使わない) # --port 8000 → 待ち受けるポート番号
| 開発時 | 本番時 | |
|---|---|---|
| 起動例 | uvicorn app.main:app --reload | uvicorn app.main:app --host 0.0.0.0 --workers 4(または gunicorn 経由) |
--reload | 使う(保存で即反映) | 使わない(無駄な再起動・性能低下を防ぐ) |
| ワーカー数 | 1(デバッグ容易) | 複数(CPU を使い切る) |
| 前段 | 不要 | Nginx 等のリバースプロキシを置くことが多い |
「FastAPI を書き換えた/コマンドだけで動かしている」のように混同しやすいが、実際に OS から見えるプロセスは uvicorn 1 つで、FastAPI app はその内側で呼ばれるオブジェクト、という関係を押さえておくと、ログやプロセス監視の読み方が変わる。
4. SQLite と init_db ——冪等性が核
SQLite はサーバープロセスを持たない「組み込み型」DBで、DB 全体が 1 つのファイル(health-moniter では health.db)に収まる。Python では標準ライブラリ sqlite3 だけで使えるため追加インストールが要らない。
接続の基本 ——foreign_keys / row_factory
import sqlite3 def get_conn(): conn = sqlite3.connect("data/health.db") # SQLite は既定で外部キー制約をオフにする → 明示的にオンへ conn.execute("PRAGMA foreign_keys = ON") # 行を「列名でアクセスできる」形(dict 風)で受け取る conn.row_factory = sqlite3.Row return conn # conn.execute(...).fetchone()["level"] のように列名で読める
4.1 冪等(idempotent)とは何か
冪等とは「同じ操作を 1 回やっても 100 回やっても、最終的な状態が同じ」という性質。日常の例で言えば、エレベーターの「↑ボタン」は何回押しても呼び出し状態は 1 つだけ=冪等。一方「カウンターに +1 する」操作は押すたびに増えるので冪等ではない。
4.2 init_db が冪等であるべき理由
init_db は「アプリ起動時にスキーマ(テーブル定義)を用意する」関数。ここで CREATE TABLE IF NOT EXISTS を使うと、
初回起動ではテーブルを作り、2 回目以降は何もしないため、何度呼んでも DB の状態は同じになる=冪等になる。
冪等だと何が嬉しいか。アプリは再起動・デプロイ・複数ワーカー起動などで init_db() が何度も呼ばれうる。
冪等でなければ「2 回目の起動でクラッシュ」してしまう。冪等なら「テーブルが無ければ作る/あれば触らない」だけなので、
起動の度に無条件で呼んでも安全になり、「初回かどうかを気にする条件分岐」が不要になる。
# app/db.py def init_db(): conn = get_conn() conn.executescript(""" CREATE TABLE IF NOT EXISTS symptoms ( id INTEGER PRIMARY KEY AUTOINCREMENT, level INTEGER NOT NULL, note TEXT, recorded_at TEXT NOT NULL DEFAULT (datetime('now')) ); """) # IF NOT EXISTS が冪等性の要 conn.commit() conn.close()
起動時(lifespan)に init_db を呼ぶ
from contextlib import asynccontextmanager from fastapi import FastAPI from app.db import init_db @asynccontextmanager async def lifespan(app: FastAPI): init_db() # 起動のたびに呼ぶ。冪等なので何度でも安全 yield # ここでアプリが稼働 # (終了時のクリーンアップを書く場所) app = FastAPI(lifespan=lifespan)
5. plotly ——データ可視化
plotly はインタラクティブなグラフ(ズーム・ホバー表示など)を描くライブラリ。重要なのは 「Python の plotly」と「plotly.js(ブラウザで描く本体)」が同じ仕組みの表裏であること。 Python 側はグラフを JSON 仕様として組み立て、それを実際に画面に描くのはブラウザ上の plotly.js。
最小コード例 ——相関グラフを JSON で返す
import plotly.express as px @app.get("/api/chart/correlation") def correlation_chart(): # 例: 気圧 x 軸、症状レベル y 軸の散布図 fig = px.scatter(df, x="pressure", y="level", labels={"pressure": "気圧(hPa)", "level": "症状レベル"}) # 図を JSON にして返す → ブラウザの plotly.js が描く return json.loads(fig.to_json())
| plotly | Chart.js | |
|---|---|---|
| 主な言語 | Python と JS の両対応(同じ図仕様) | JavaScript 中心 |
| Python 連携 | 得意(サーバーで図を組み立て JSON 化) | 基本データだけ渡し JS 側で構成 |
| 機能の幅 | 科学計算寄り。3D・統計図も豊富 | 軽量。基本的なグラフが中心 |
| 向く場面 | 解析結果を Python から描く本プロジェクト | 軽い UI グラフ |
health-moniter では Phase 3 で「症状レベル × 気圧/気温/湿度」の相関グラフをダッシュボードに描く。 Python(FastAPI)側で解析しグラフ仕様を作り、React フロントエンドの plotly.js が描画する分担になる。 🟡 Phase 3 は計画段階のため、最終的な API 形状はプロジェクト進行で変わりうる。
6. まとめ(疑問と答え)
| 疑問 | 答え |
|---|---|
| FastAPI だけでサーバーは動く? | 動かない。uvicorn(ASGI サーバー)が app を起動して初めて HTTP を受けられる |
| app.main:app の意味は? | app/main.py の中の app という変数(モジュール:オブジェクト)を指す |
| --reload は本番でも使う? | No。開発時のみ。本番は外して複数ワーカーで動かす |
| init_db を毎回呼んで壊れない? | 壊れない。CREATE TABLE IF NOT EXISTS で冪等だから何度呼んでも状態は同じ |
| SQLite に別途インストールは要る? | 不要。Python 標準ライブラリ sqlite3 で使える。DB は 1 ファイル |
| plotly のグラフは誰が描く? | Python が JSON 仕様を作り、ブラウザの plotly.js が実描画する |
関連項目
- フロントエンドツールチェーン(Node.js / Vite / React / TypeScript) ——このドキュメントの対になるフロントエンド側。ブラウザ(React)から本バックエンドを叩く