一夜で組むFirebase×Cloud Run×FastAPI:AIアプリの土台づくり
はじめに
「まず動く“土台”だけ一晩で作りたい」──そんな想いで組み上げた記録です。
この記事では、筆者が 一夜で“アプリの土台”を組み上げたときの構成を、添付キャプチャ(アーキテクチャ図)をベースに解説します。
- どのサービスが何を担当しているのか
- 設定のポイント(ここを押さえると詰まりにくい)
- 最小構成で安全に動かすための実務Tips
- そして「土台はできたが、作り込みはこれから」という 今後の展望
まで、初学者が迷わない粒度でまとめます。
この記事は「土台づくり」が主題です。
UIの作り込み・分析ロジックの精度改善・プロンプト最適化・データ設計の深掘りなどは、これから育てていく前提で書いています。
この記事でわかること
- 添付キャプチャの構成(Firebase Hosting / Auth / Storage / Firestore / Cloud Run / Vertex AI)の役割分担
- Next.js(静的エクスポート)+ Firebase Hosting でフロントを最短で公開する方法
-
/api/*を Hosting から Cloud Run に rewrite して“APIだけ別”にする設計 - Firebase Auth の IDトークン検証を FastAPI 側で行う最小実装
- Firestore/Storage を使うときの 権限設計(Security Rules と IAM の境界)
- Vertex AI(Gemini)を「Optional」にしておく理由と、あとから足すときのポイント
全体アーキテクチャ
まずは全体像です。
図1: Firebase Hosting(Next.js静的)→ /api/* を Cloud Run(FastAPI)へリライト。Auth/Storage/FirestoreをFirebaseでまとめ、必要ならVertex AI(Gemini)を足す。
リクエストの流れ(ざっくり)
- ユーザーは Firebase Hosting に配置した静的サイト(Next.js export)へアクセス
- フロントエンドは Firebase Auth(Google Sign-In) でログインし、IDトークンを取得
- フロントが
/api/*を叩くと、Hosting の rewrite で Cloud Run(FastAPI) に転送される - FastAPI は受け取った IDトークンを検証し、ユーザー(uid)を特定
- 必要に応じて
- Firebase Storage から画像をダウンロード(または署名付きURL生成)
- Firestore に分析結果・レポート・チャット履歴などを保存
- Vertex AI(Gemini) で画像/テキストを解析(Optional)
なぜこの構成が「一夜で土台を作る」のに向いているのか
この構成の強みは、“迷いどころ”をマネージドサービスで潰せる点です。
- フロント公開:Firebase Hosting(CDN付き)で即デプロイ
- 認証:Firebase Auth(Google Sign-In)でID管理を丸投げ
- API:Cloud Run に FastAPI を載せるだけ(スケールも運用も軽い)
- データ:Firestore/Storage で“とりあえず動く”永続化がすぐできる
- AI:Vertex AI を Optional にしておき、後から繋げられる
「最初から完璧」ではなく、“まず成立する形”を最短で作るのに向いています。
まず押さえる:サービス別の役割と設定ポイント(詳細)
ここからは、図1に出てくる各サービスを「概要 → できること → 設定のポイント → 落とし穴」の順で整理します。
1) Firebase Hosting(Next.js Static Export)
概要
静的ファイル(HTML/CSS/JS)をCDN配信できる Firebase のホスティング機能です。
今回は Next.jsを静的エクスポートして Hosting に置きます。
何ができるのか
- 静的サイトの配信(キャッシュ・HTTPS・独自ドメイン対応)
-
/api/*のようなパスを Cloud Run へ rewrite(重要) - プレビュー環境(Preview Channels)で安全に試せる
設定のポイント
-
rewrite を最初から入れる
「フロントは静的、APIは別」にすると設計がシンプルになります。 -
ルーティングは静的に寄せる(Next.js export前提)
SSR/ISRに頼る設計だと export できずに詰まります。 -
キャッシュ制御(後回しにしがち)
Cache-Controlを雑にすると、更新が反映されない/逆にキャッシュが効かない、の両方が起きます。
firebase.json(Hosting + Cloud Run rewrite の最小例)
{
"hosting": {
"public": "out",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "/api/**",
"run": { "serviceId": "agent-api", "region": "asia-northeast1" }
}
]
}
}
publicは Next.js の export 先(例:out/)に合わせます。
serviceIdとregionは Cloud Run 側と合わせてください。
落とし穴(よくある)
-
rewriteを入れたのに 404
→ Hosting 側のfirebase deployが反映されていない / region違い / serviceId違い - Next.js export で
next/imageが動かない
→ exportでは最適化が使えないことがあるので、unoptimized設定を検討(後述)
2) Next.js(Static Export)
概要
Next.js を 静的サイトとして書き出して Hosting に置く方式です。
何ができるのか
- SPA的な体験(ページ遷移・UI)を静的配信で実現
- Firebase Auth を組み込み、ログイン状態で画面を切り替え
- API呼び出しは
/api/*で Cloud Run に逃がす
設定のポイント
- Next.jsのバージョンにより export 方法が違うため、プロジェクトに合わせる
-
output: "export"を使う方式 -
next exportを使う方式
-
- export前提なら、画像最適化は
unoptimizedが安全なことが多い
例(next.config.js):
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
images: { unoptimized: true }
};
module.exports = nextConfig;
落とし穴
- 動的ルート(例:
/reports/[id])をそのまま使う
→ export向けにgenerateStaticParamsなどを考える or ルーティング方針を調整 - 環境変数の扱い
→ 静的ビルドに埋め込まれるため、秘密情報は入れない(NEXT_PUBLIC_のみ)
3) Firebase Auth(Google Sign-In)
概要
ユーザー認証を Firebase に任せる構成です。フロントでログイン → IDトークンを取得し、APIに渡します。
何ができるのか
- Googleログイン(OAuth)の実装をほぼ省略
- ユーザーID(
uid)を一意に管理 - セッション維持やトークン更新をSDKが面倒見てくれる
設定のポイント
- Firebase Console で Google プロバイダを有効化
- OAuth同意画面や承認済みドメイン設定(環境によって必要)
- フロント → API は IDトークンを
Authorization: Bearerで渡すのが定番
フロント(例:概念コード)
import { getAuth } from "firebase/auth";
async function callApi() {
const auth = getAuth();
const token = await auth.currentUser?.getIdToken();
const res = await fetch("/api/health", {
headers: { Authorization: `Bearer ${token}` }
});
return res.json();
}
落とし穴
- 「動くけどセキュアじゃない」状態になりやすい
→ API側でIDトークン検証して、uidベースでアクセス制御する - 開発中にCORSで詰まる
→ Hosting から叩くなら同一オリジンに寄るのでCORSは減る(ローカル時は別途対策)
4) Cloud Run(FastAPI / agent-api)
概要
FastAPI をコンテナ化して Cloud Run に載せる構成です。
Hosting の /api/* rewrite により、フロントからは“同じドメインのAPI”のように見えます。
何ができるのか
- Python API を最小運用で公開(オートスケール)
- Firebase Admin SDK / Firestore / Storage / Vertex AI などをまとめて叩ける
- 重い処理(画像解析など)をフロントから分離できる
設定のポイント(ここが土台の肝)
-
コンテナは
PORTを listen(Cloud Runの基本) - サービスアカウントを分けて最小権限にする(後述)
- 公開方法は2択
- まずは 未認証で公開して、アプリ側のIDトークンで守る(最短)
- きっちりやるなら Cloud Run自体を認証必須にして invoker を絞る(後回しでもOK)
FastAPI の最小構成(IDトークン検証つき)
# main.py
from fastapi import FastAPI, Header, HTTPException
import firebase_admin
from firebase_admin import auth
# Cloud Run では ADC(サービスアカウント)で初期化できる想定
firebase_admin.initialize_app()
app = FastAPI()
def verify_bearer(authorization: str | None) -> dict:
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing bearer token")
token = authorization.removeprefix("Bearer ").strip()
try:
decoded = auth.verify_id_token(token, check_revoked=True)
return decoded
except Exception:
raise HTTPException(status_code=401, detail="Invalid ID token")
@app.get("/api/health")
def health(authorization: str | None = Header(default=None)):
decoded = verify_bearer(authorization)
return {"ok": True, "uid": decoded["uid"]}
Dockerfile(Cloud Run向けの最小例)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PYTHONUNBUFFERED=1
ENV PORT=8080
CMD ["uvicorn", "main:app", "--host=0.0.0.0", "--port=8080"]
落とし穴
-
uvicornが127.0.0.1で起動していて疎通できない
→--host=0.0.0.0が必須 - Cloud Run の リージョンと Hosting rewrite の region がズレる
→firebase.jsonと Cloud Run を同じ region にする - トークン検証を入れたのに 401 が出る
→ フロントが token を付けていない / 古い token /Authorizationヘッダが落ちている
5) Firestore(Analysis / Reports / Chats )
概要
NoSQL(ドキュメントDB)で、アプリのデータをスピーディに持てます。
図1では「分析結果・レポート・チャット」を Firestore に保存する想定です。
何ができるのか
- ユーザー別データをコレクションで管理しやすい
- フロントから直接読み書き(リアルタイム)もできる
- バックエンドから Admin SDK で集計・書き込みもできる
設定のポイント(“土台”で決めておくと後が楽)
-
ドキュメントパス設計を先に固定(後から移行がつらい)
- 例:
users/{uid}/photos/{photoId} - 例:
users/{uid}/reports/{reportId} - 例:
users/{uid}/chats/{threadId}/messages/{messageId}
- 例:
-
書き込み元を分ける
- フロント:ユーザー操作の範囲(Security Rulesで制御)
- バック:分析結果の確定書き込み(Admin SDKで実行)
- インデックスは“必要になったら足す”でもOK(ただし詰まったら最優先で対応)
Security Rules(最小例)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{document=**} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
バックエンド(Admin SDK)は原則ルールをバイパスします。
だからこそ API側で uid を必ず検証して、他人のデータに触れないように実装します。
落とし穴
- “楽だから”とトップレベルに何でも置く
→ 後から権限・クエリ・課金がつらくなるので、まずusers/{uid}/...に寄せるのがおすすめ - 1ドキュメントが肥大化
→ チャットやログは「メッセージ単位でコレクション」に逃がす
6) Firebase Storage(Photos)
概要
画像などのバイナリを置くストレージです。
図1では Photos を Storage に置き、必要に応じて Cloud Run がダウンロードして解析します。
何ができるのか
- フロントから直接アップロード(SDK + Security Rules)
- バックエンドからダウンロード/解析
- 署名付きURLで期間限定の配布もできる
設定のポイント
-
パス設計(Firestore同様に先に決める)
- 例:
users/{uid}/photos/{photoId}.jpg
- 例:
- 画像アップロードはフロント、解析はバック、という分業がわかりやすい
- ライフサイクル(不要画像の削除)を後からでも入れられるようにしておく
Storage Rules(最小例)
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/photos/{allPaths=**} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
落とし穴
- 画像を公開設定にしてしまう
→ 「とりあえず動く」けど後で事故る。まずはユーザー単位で閉じる。 - Cloud Run からのアクセス権がない
→ Cloud Run の サービスアカウントにStorage Object Viewerなどを付与する(後述)
7) Vertex AI(Gemini)※Optional
概要
画像/テキスト解析を Google の生成AIで行う想定です。
ただし最初から入れると「課金・速度・安全性・プロンプト設計」が一気に難しくなるので、図1でも Optional にしてあります。
何ができるのか
- 画像を含むマルチモーダル解析(例:食事写真 → 食材推定 → 栄養推定)
- 文章生成(レポート作成、チャット応答)
- 将来的に関数呼び出し/ツール連携などへ拡張
設定のポイント(後から繋ぐために“今”決めておくこと)
-
呼び出しは Cloud Run 側に集約
フロント直叩きは、キー管理や制限が難しくなりがちです。 - “AIの入出力”を Firestore に ログとして残す設計にすると、後で改善しやすい
- 入力(画像パス、ユーザーの指示)
- 出力(推定結果、根拠、バージョン)
- メタ(モデル名、温度、失敗理由)
落とし穴
- いきなり本番データで回す
→ コストと情報漏洩リスクが読めないので、最初は「ダミーデータ + 上限付き」で回すのがおすすめ
“土台”を一夜で通すための手順(再現用)
ここからは「同じ土台を作るならこの順が早い」という並びで書きます。
(細部はプロジェクト事情で変わりますが、順序はかなり効きます)
Step 0. 事前準備(必要なもの)
- Google Cloud プロジェクト(請求先設定)
- Firebase プロジェクト(上と同一でOK)
- ローカル環境
- Node.js(Next.js + Firebase CLI)
- Python(FastAPI)
- gcloud CLI(Cloud Run deploy)
- Firebase CLI
Step 1. Firebaseプロジェクトを作る
- Firebase Consoleでプロジェクト作成
- 「Webアプリ」を追加(Hosting用)
- Auth/Firestore/Storage を有効化
ここで作った
firebaseConfig(APIキー等)は秘密情報ではありませんが、
“公開して良い設定”として扱い、環境を分けたい場合はprojectIdを切り替えます。
Step 2. Auth(Google Sign-In)を有効化
- Authentication → Sign-in method → Google を有効化
- 必要なら承認済みドメイン、OAuth同意画面を設定
この時点で、フロントでログイン→ユーザー情報が取れると “土台”の半分は完成です。
Step 3. Firestore / Storage のパス設計を決める
おすすめは users/{uid}/... に寄せる設計です。
例(今回の図に沿った叩き台)
users/{uid}/photos/{photoId}users/{uid}/analysis/{analysisId}users/{uid}/reports/{reportId}users/{uid}/chats/{threadId}/messages/{messageId}
Step 4. Cloud Run(FastAPI)を先にデプロイして疎通する
- FastAPI の
/api/healthを作る(トークン検証は一旦オフでもOK) - Dockerfile を作る
- Cloud Run にデプロイして
curlで疎通
例(ローカルのターミナル)
gcloud run deploy agent-api \
--source . \
--region asia-northeast1 \
--allow-unauthenticated
まずは最短で動かすため
--allow-unauthenticatedにしています。
実運用で絞る方法は後述します。
Step 5. Hosting から Cloud Run へ /api/* を rewrite
firebase.json の rewrites を設定して firebase deploy します。
firebase deploy --only hosting
これでフロントから /api/health が叩けるようになります。
Step 6. IDトークン検証を Cloud Run に入れる
- フロント:
Authorization: Bearer <token>を付与 - バック:
firebase_admin.auth.verify_id_token()で検証
ここまで通れば、「ログイン必須API」 の土台が完成です。
Step 7. Storage / Firestore の読み書きを足す
- 画像は Storage
- メタや分析結果は Firestore
に分けると扱いやすいです。
ここから先は「アプリ固有の作り込み」になるので、この記事では土台までに留めます。
IAM(最小権限)の考え方:どこで何を守るか
この構成は「Firebase(Rules)と Cloud Run(IAM/アプリ認証)」で守る場所が分かれます。

図2: フロント直アクセスはRulesで制御、バックエンドはサービスアカウントに最小権限。uid検証はアプリで必須。
推奨の分担
- フロント → Firestore/Storage:Security Rules でユーザー単位に制限
- Cloud Run → Firestore/Storage/Vertex AI:Cloud Run のサービスアカウントに最小権限
- Cloud Run の API 認証:Firebase IDトークン検証(これが主砦)
Cloud Run サービスアカウント(例)
agent-api-sa のような専用SAを作り、必要な権限だけ付けます。
- Firestore:
Cloud Datastore User(用途により Admin も) - Storage:
Storage Object Viewer(ダウンロードのみなら) - Vertex AI:
Vertex AI User(呼び出すなら)
役割名は組織ポリシーで変わることがあるため、まずは「読み取りだけ」「必要になったら昇格」が安全です。
実務Tips:一夜で作った“あと”に効く、壊れにくい工夫
1) 「環境分離」を早めに入れる(dev/stg/prod)
- Firebase プロジェクトを分ける(または
firebase use --addで切り替え) - Cloud Run サービスも
agent-api-dev / agent-api-prodのように分ける - Firestore/Storage のデータが混ざるのが一番辛いので、早めに分けるのがおすすめ
2) APIは最初から「ログ」を残す
Cloud Run の Cloud Logging を前提に、最低限これをログに残すと後で助かります。
- request id(自前 or Cloud Run の trace)
- uid
- 処理時間
- 失敗理由(例外内容は機密を含めない)
3) AIは“後から足す”前提でインターフェースを切る
Vertex AI を Optional にするなら、例えばこう分けると後で差し替えやすいです。
-
POST /api/analyze:入力(画像パス等)を受け取り、analysisIdを返す - バックグラウンド処理(後で Cloud Tasks / PubSub / Workflows に拡張)
-
GET /api/analysis/{analysisId}:結果参照
一夜で作る段階では同期でもOKですが、将来非同期にできる形にしておくと伸びます。
トラブルシュート(よくある詰まり)
/api/* が 404 になる
-
firebase.jsonの rewrite が入っているか -
firebase deploy --only hostingしたか - Cloud Run の
serviceId / regionが一致しているか
401(Missing/Invalid ID token)
- フロントが
Authorizationを付けているか - ログイン直後で
currentUserがまだnullになっていないか - バック側が
Bearer文字列を正しく剥がしているか
Storage/Firestore の権限エラー
- フロントからのアクセス:Rules を見直す
- Cloud Run からのアクセス:サービスアカウントの IAM を見直す
Vertex AI が 403 / quota
- Vertex AI API が有効化されているか
- リージョンとモデルが合っているか
- サービスアカウント権限とクォータを確認
今後の展望(ここから“アプリ”に育てる)
一夜で作ったのは、あくまで「土台」です。ここから先は、プロダクトの価値を作り込むフェーズになります。
- Firestore のスキーマを固める(履歴・集計・検索性)
- 画像解析の精度を上げる(プロンプト、前処理、評価)
- チャットUX(スレッド、文脈、要約)
- 通知(毎朝のレポート、リマインド)
- コスト最適化(Cloud Run min instances、AI呼び出し制御)
- セキュリティ(App Check、レート制限、監査ログ、データ保持)
このあたりは別記事で深掘りする予定です。
まとめ
- Firebase Hosting + Next.js static export で最短でフロント公開
-
/api/*rewrite → Cloud Run(FastAPI) でAPIを分離し、拡張しやすい - 認証は Firebase Auth(Google Sign-In)、APIは IDトークン検証が主軸
- 画像は Storage、メタ/結果は Firestore で持つと見通しが良い
- Vertex AI(Gemini)は Optional にして、土台が固まってから足すのが安全
参考リンク(一次情報)
- Firebase Hosting(rewrites / Cloud Run 連携)
- Next.js Static Export
- Firebase Authentication(IDトークン)
- Firebase Admin SDK(IDトークン検証)
- Firestore
- Firebase Storage
- Cloud Run
- FastAPI
- Vertex AI(Generative AI)
Discussion