背景
個人開発している英単語アプリで、ログイン機能を実装して拡張する際にアーキテクチャ全体の見直しを行いました。
もともとは Firebase Hosting + Gemini API 構成で動かしており、フロント側だけで完結する設計でしたが、一般公開を見据えて次のような課題がありました。
- APIキーがフロント側に露出してしまうリスクがある
- CORS制約によって開発効率が下がる
- ロジックやログイン機能の拡張が難しい
これらを解決するために、Hono + Cloud Run による軽量APIサーバー構成へ移行しました。
この変更によって、フロント(Vite)とサーバーを明確に分離し、安全でスケーラブルなアプリ基盤を実現できました。
それぞれの役割
Hono:軽量なAPIサーバーを作るためのフレームワーク
Docker:アプリをどこでも動かせる「箱」にまとめるツール
Cloud Run:その箱(コンテナ)をクラウド上で自動的に動かしてくれる
たとえを交えると
Honoが「料理」、Dockerが「お弁当箱」、Cloud Runが「配達サービス」のようなイメージです。
Hono
Honoは、軽量でシンプルなAPI構築が可能なフレームワークです。TypeScriptとの相性も非常に良く、型補完が強力な点が魅力でした。
Docker
Dockerは、アプリを動かすための環境を「箱詰め(コンテナ化)」できる仕組みです。これを使うことで、ローカルで動いていたHonoサーバーをそのままクラウドで再現できるようになりました。
Cloud Run
Cloud RunはDockerコンテナをそのままデプロイできる柔軟さと、リクエストに応じた自動スケーリング機能を持っています。Firebase Functionsよりも制約が少なく、OpenAI APIキーなどの機密情報をGCP上の環境変数として安全に管理できる点も大きなメリットでした。
アーキテクチャ構成
Before:Firebase + Gemini API
After:Vercel + Hono + Cloud Run + Docker + OpenAI
具体的な部分
Hono
- Node.js + TypeScript でAPIサーバーを作成
- /chat エンドポイントを定義し、OpenAI APIを呼び出す処理を実装
- hono/cors でCORS対応を追加
- .env でAPIキーを設定してローカルで動作確認
ローカルで「AI応答が返るAPI」を完成させました。
CORS制約
Hono側でCORS設定を入れないと下記のようなエラーに遭遇します。
Access to fetch at 'https://hono-server-xxx.run.app/chat'
from origin 'http://localhost:5173' has been blocked by CORS policy.
このように設定しエラーを解消しました。
import { cors } from 'hono/cors'
app.use('*', cors({ origin: '*' }))
Docker
- Dockerfile を作成してHonoサーバーをコンテナ化
- Node.jsのバージョンや依存関係を明示的に固定
- ローカルで docker build / docker run を実行して動作確認
Cloud Run
- Dockerfile を用意してHonoアプリをコンテナ化
- gcloud builds submit でDockerイメージをビルド
- gcloud run deploy でクラウド上にデプロイ
- 環境変数 OPENAI_API_KEY を設定
- 発行されたURLをフロントの .env に指定して通信確認
クラウド上で「公開APIサーバー」を動かせるようにしました。
まとめ
ログイン機能をきっかけにアーキテクチャを見直し、フロントとサーバーを疎結合化することで、セキュリティ・保守性・拡張性を大きく向上させることができました。