Nuxt.js と Python(FastAPI)を用いた AI レシピ提案アプリの開発プロジェクト
本プロジェクトは、以下の 3 つのスキル習得・向上を目的としています。
- 新しい技術への挑戦: これまで使用経験のない Nuxt.js と Python(FastAPI)に触れ、モダンな Web 開発の知見を広げる。
- チーム開発フローの向上: Git/GitHub を利用したブランチ戦略、プルリクエストベースのコードレビュー、コンフリクト解消など、チーム開発における一連のプロセスを実践し、円滑な共同作業のスキルを磨く。
- 外部 API の利用体験: OpenAI や Gemini といった生成系 AI の API を実際にアプリケーションに組み込み、その仕組みと効果的な活用方法への理解を深める。
冷蔵庫にある食材を入力すると、AI がそれらを使ったレシピを提案してくれる Web アプリケーションです。ユーザーはアカウントを作成でき、気に入ったレシピを保存して後から見返すことができます。
| サービス名 | URL | 特徴 |
|---|---|---|
| Pecco | https://pecco.app/ | 冷蔵庫の食材からレシピを提案するサービス |
| 冷蔵庫にあるものでレシピ提案プロンプト | https://chapro.jp/prompt/1964 | ChatGPT プロンプトを使った食材提案 |
| Panasonic 冷蔵庫 AI カメラ | https://panasonic.jp/reizo/app/live-pantry/vegetable-ai.html | AI カメラで野菜の使い切りをサポート |
| サービス名 | URL | 特徴 |
|---|---|---|
| DELISH KITCHEN | https://delishkitchen.tv/ | 動画でわかりやすいレシピ提案 |
| キッコーマン 今日の献立 | https://www.kikkoman.co.jp/homecook/calendar/this-week.html | 食材・調味料メーカー提供のレシピ |
| タニタ社員食堂レシピ | https://www.tanita-thl.co.jp/ | 健康志向のレシピ提案 |
| 分類 | 技術 | バージョン | 選定理由 |
|---|---|---|---|
| フロントエンド | Nuxt.js | v3 | Vue ベースで学習コストが低く、SSR・SPA 両対応が可能 |
| フロントエンド | TypeScript | 最新 | 型安全で保守性を高めるため |
| フロントエンド | TailwindCSS | 最新 | 開発効率が高く、デザインを素早く適用できるため |
| バックエンド | Python | 3.11 | 学習済み、エコシステムが豊富 |
| バックエンド | FastAPI | 最新 | 型ヒントを活かした高速な API 開発が可能 |
| 認証 | JWT (JSON Web Token) | 標準 | ステートレスな認証を実現し、SPAとの連携が容易なため |
| データベース | PostgreSQL | 15 系 | JSON 型や拡張性が強力で、本格運用に耐えるため |
| インフラ | Docker | 最新 | 環境構築の一貫性を担保するため |
| インフラ | Docker Compose | 最新 | ローカル開発でのマルチコンテナ管理が容易なため |
| 外部 API | OpenAI API | 最新 | 高精度な自然言語処理を利用するため |
| 外部 API | Google Gemini API | 最新 | マルチモーダル AI を試すため |
| パッケージ管理 | pnpm | 最新 | ワークスペース対応が強力でモノレポ管理に適しているため |
| サービス名 | URL | 特徴 |
|---|---|---|
| DELISH KITCHEN | https://delishkitchen.tv/ | 動画でわかりやすいレシピ提案 |
| キッコーマン 今日の献立 | https://www.kikkoman.co.jp/homecook/calendar/this-week.html | 食材・調味料メーカー提供のレシピ |
| タニタ社員食堂レシピ | https://www.tanita-thl.co.jp/ | 健康志向のレシピ提案 |
| 分類 | 技術 | バージョン | 選定理由 |
|---|---|---|---|
| フロントエンド | Nuxt.js | v3 | Vue ベースで学習コストが低く、SSR・SPA 両対応が可能 |
| フロントエンド | TypeScript | 最新 | 型安全で保守性を高めるため |
| フロントエンド | TailwindCSS | 最新 | 開発効率が高く、デザインを素早く適用できるため |
| バックエンド | Python | 3.11 | 学習済み、エコシステムが豊富 |
| バックエンド | FastAPI | 最新 | 型ヒントを活かした高速な API 開発が可能 |
| データベース | PostgreSQL | 15 系 | JSON 型や拡張性が強力で、本格運用に耐えるため |
| インフラ | Docker | 最新 | 環境構築の一貫性を担保するため |
| インフラ | Docker Compose | 最新 | ローカル開発でのマルチコンテナ管理が容易なため |
| 外部 API | OpenAI API | 最新 | 高精度な自然言語処理を利用するため |
| 外部 API | Google Gemini API | 最新 | マルチモーダル AI を試すため |
| パッケージ管理 | pnpm | 最新 | ワークスペース対応が強力でモノレポ管理に適しているため |
| 認証機能 | JWT | 最新 | 認証機能を1から作る経験をするため |
本プロジェクトでは、機能ごとに担当者を分け、フロントエンドからバックエンドまでを一気通貫で開発する「垂直分割」スタイルを採用します。
- 担当 A(MAI): ユーザー認証機能、レシピ保存(CRUD)機能
- 担当 B(HARUMU): AI レシピ提案機能、UI/UX 全般
開発は以下のフローを基本とします。詳細はdocs/github_rules.mdを参照してください。
- 担当機能の Issue を元に、
mainブランチから作業ブランチを作成 (feature/issue番号-機能名)。 - 作業が完了したら、変更をリモートに
pushする。 - GitHub 上でプルリクエストを作成し、もう一方のメンバーをレビュー担当者に指定する。
- レビューと承認を経て、
mainブランチにマージする。
- Day 1: 環境構築、github 練習
- Day 2: 設計 FIX、API 基盤構築
- Day 3: 各担当のコア機能(認証, AI 連携)の実装
- Day 4: レシピ保存機能の実装とテスト
- Day 5: 統合テスト、デモ準備
開発のセットアップから動作確認までに必要なコマンドをすべて網羅した表です。 サーバー起動コマンドを、ルートディレクトリで実行できるようにしました。 pnpm devを使ってフロントエンド・バックエンドの起動が可能です。
| 分類 | 目的・確認方法 | コマンド | 実行場所 |
|---|---|---|---|
| 初期セットアップ | 全ての依存関係をインストール | pnpm install |
ルート |
| データベース | DBコンテナを起動 | docker-compose up -d |
ルート |
| DBコンテナを停止 | docker-compose down |
ルート | |
| 仮想環境 | Python仮想環境を有効化 | source venv/bin/activate |
packages/backend |
| Python仮想環境を無効化 | deactivate |
(venv有効化中の) ターミナル |
|
| DBマイグレーション | テーブル構造を最新の状態に更新 | alembic upgrade head |
packages/backend ( venv有効化後) |
| サーバー起動 | バックエンドサーバーを起動 | pnpm dev:backend |
ルート |
| バックエンドサーバーを停止 | Ctrl + C |
実行中のターミナル | |
| フロントエンドサーバーを起動 | pnpm dev:frontend |
ルート | |
| フロントエンドサーバーを停止 | Ctrl + C |
実行中のターミナル | |
| テスト実行 | バックエンドの単体テストを実行 | pytest |
packages/backend ( venv有効化後) |
| フロントエンドの単体テストを実行 | pnpm --filter nuxt-app test |
ルート | |
| E2E(統合)テストを実行 | pnpm exec playwright test |
ルート |
- 定例会: 毎朝 10 分程度の朝会を実施し、進捗と課題を共有します。
- ツール: 随時の連絡には Discord/Zoom を使用します。
- 時間設定: 解決にかける時間意識を持ち、30 分以上悩んでも解決しない場合はすぐにチームメンバーに相談します。
| 項目 | 内容・教訓 |
|---|---|
| 環境構築の再現性 | PythonのvenvはPC環境に依存しやすく、requirements.txtとバージョン統一の重要性を学んだ。 |
| DBの互換性 | テスト(SQLite)と本番(PostgreSQL)でのARRAY型の非互換性に直面。JSON型のような汎用的な型が重要。 |
| 設定ファイルの一元管理 | チーム開発では.envとconfig.pyの同期が不可欠。extra = 'ignore'設定で堅牢性を高める必要があった。 |
| 的確なエラーハンドリング | CORSエラーの裏でバックエンドがクラッシュしているなど、ログを突き合わせた根本原因の特定が不可欠だった。 |
| 項目 | 内容・気づき |
|---|---|
| FastAPI vs Express.js | Express.jsでは手動で設定が必要だったリクエストボディのバリデーションが、FastAPIではPydanticのスキーマ定義だけで完結し、型安全かつ高速に開発できる。API仕様書(Swagger UI)の自動生成も開発体験として非常に優れていた。 |
| Nuxt.js vs Next.js | Nuxt 3の自動インポート機能は、Next.jsに比べ規約が強く、記述量が少なくて済む印象。一方で、TypeScriptの型定義連携で予期せぬエラーに遭遇し、フレームワークの内部理解の重要性も感じた。 |
| Pythonの環境管理 | Node.jsのpnpmのようなロックファイルによる厳密なバージョン管理に慣れていると、Pythonのvenvは環境差異が出やすく、チームでの厳密な管理が必要だと実感した。 |
Details
### 違い| 項目 | JWT (JSON Web Token) | Firebase Authentication |
|---|---|---|
| 役割 | 規格・仕様 | サービス・製品 |
| 概要 | 認証情報をJSON形式で安全にやり取りするための「トークンの設計図」。署名によって改ざんを防ぐ。 | Googleが提供する、ユーザー認証機能一式をまとめたバックエンドサービス(BaaS)。内部でJWTなどの技術を使っている。 |
| 使い方 | 自分でトークンの生成、検証、失効などのロジックをすべて実装する必要がある。 | SDKを導入し、数行のコードでGoogleログインやメール認証などを実装できる。ユーザー管理などもすべてFirebase側が行う。 |
| 自由度 | 非常に高い。トークンに含める情報や有効期限など、すべてを自由に設計できる。 | 低い。Firebaseの提供する認証フローに乗る必要がある。 |
| 分類 | 目的・確認方法 | コマンド | 実行場所 |
|---|---|---|---|
| 初期セットアップ | 全ての依存関係をインストール | pnpm install |
ルート |
| データベース | DBコンテナを起動 | docker-compose up -d |
ルート |
| DBコンテナを停止 | docker-compose down |
ルート | |
| 仮想環境 | Python仮想環境を有効化 | source venv/bin/activate |
packages/backend |
| Python仮想環境を無効化 | deactivate |
(venv有効化中の) ターミナル |
|
| DBマイグレーション | テーブル構造を最新の状態に更新 | alembic upgrade head |
packages/backend ( venv有効化後) |
| サーバー起動 | バックエンドサーバーを起動 | pnpm dev:backend |
ルート |
| バックエンドサーバーを停止 | Ctrl + C |
実行中のターミナル | |
| フロントエンドサーバーを起動 | pnpm dev:frontend |
ルート | |
| フロントエンドサーバーを停止 | Ctrl + C |
実行中のターミナル | |
| テスト実行 | バックエンドの単体テストを実行 | pytest |
packages/backend ( venv有効化後) |
| フロントエンドの単体テストを実行 | pnpm --filter nuxt-app test |
ルート | |
| E2E(統合)テストを実行 | pnpm exec playwright test |
ルート |
- 定例会: 毎朝 10 分程度の朝会を実施し、進捗と課題を共有。
- ツール: 随時の連絡には Discord/Zoom を使用。
- 時間設定: 解決にかける時間意識を持ち、30 分以上悩んでも解決しない場合はすぐにチームメンバーに相談。
| 項目 | 内容・教訓 |
|---|---|
| 環境構築の再現性 | PythonのvenvはPC環境に依存しやすく、requirements.txtとバージョン統一の重要性を学んだ。 |
| DBの互換性 | テスト(SQLite)と本番(PostgreSQL)でのARRAY型の非互換性に直面。JSON型のような汎用的な型が重要。 |
| 設定ファイルの一元管理 | チーム開発では.envとconfig.pyの同期が不可欠。extra = 'ignore'設定で堅牢性を高める必要があった。 |
| 的確なエラーハンドリング | CORSエラーの裏でバックエンドがクラッシュしているなど、ログを突き合わせた根本原因の特定が不可欠だった。 |
| 項目 | 内容・気づき |
|---|---|
| FastAPI vs Express.js | Express.jsでは手動で設定が必要だったリクエストボディのバリデーションが、FastAPIではPydanticのスキーマ定義だけで完結し、型安全かつ高速に開発できる。API仕様書(Swagger UI)の自動生成も開発体験として非常に優れていた。 |
| Nuxt.js vs Next.js | Nuxt 3の自動インポート機能は、Next.jsに比べ規約が強く、記述量が少なくて済む印象。一方で、TypeScriptの型定義連携で予期せぬエラーに遭遇し、フレームワークの内部理解の重要性も感じた。 |
| Pythonの環境管理 | Node.jsのpnpmのようなロックファイルによる厳密なバージョン管理に慣れていると、Pythonのvenvは環境差異が出やすく、チームでの厳密な管理が必要だと実感した。 |
| 項目 | JWT (JSON Web Token) | Firebase Authentication |
|---|---|---|
| 役割 | 規格・仕様 | サービス・製品 |
| 概要 | 認証情報をJSON形式で安全にやり取りするための「トークンの設計図」。署名によって改ざんを防ぐ。 | Googleが提供する、ユーザー認証機能一式をまとめたバックエンドサービス(BaaS)。内部でJWTなどの技術を使っている。 |
| 使い方 | 自分でトークンの生成、検証、失効などのロジックをすべて実装する必要がある。 | SDKを導入し、数行のコードでGoogleログインやメール認証などを実装できる。ユーザー管理などもすべてFirebase側が行う。 |
| 自由度 | 非常に高い。トークンに含める情報や有効期限など、すべてを自由に設計できる。 | 低い。Firebaseの提供する認証フローに乗る必要がある。 |
例えるなら:
- JWT: 家の「設計図」。どんな部屋をどこに作るか(トークンの中身)は自由だが、家を建てるのは自分。
- Firebase: 「建売住宅」または「マンション」。設計は決まっているが、すぐに住み始めることができる。
今回のプロジェクトでは、JWTの規格に沿って、認証機能をフルスクラッチで実装しました。
-
トークンの発行(バックエンド:
auth_logic.py)- ユーザーが
/users/loginに正しいメールアドレスとパスワードを送信します。 - サーバーはそれを検証し、成功すれば
python-joseライブラリを使ってJWT(アクセストークン)を生成します。 - トークンの中身(ペイロード)には、ユーザーを識別するための情報(
sub: user.email)と有効期限(exp)を含ませました。 - 生成したトークンをクライアント(フロントエンド)に返します。
- ユーザーが
-
トークンの保持(フロントエンド: Piniaストア)
- フロントエンドは受け取ったアクセストークンを、状態管理ライブラリPiniaのストアに保存します。
- さらに、
localStorageにもトークンを保存することで、ページをリロードしてもログイン状態が維持されるようにしました。
-
トークンの検証(バックエンド:
dependencies.py) _ フロントエンドは、認証が必要なAPI(/recipesなど)を呼び出す際、作成した共通関数useApiFetchを使って、リクエストヘッダーにAuthorization: Bearer <トークン>の形でトークンを付与します。 _ バックエンドは、get_current_userという依存性注入関数で、送られてきたトークンを検証します。 _ トークンが改ざんされていないか、有効期限が切れていないかを確認し、問題がなければユーザー情報をDBから取得して、APIの処理を続行します。 _ もしトークンが無効であれば、401 Unauthorizedエラーを返します。
| 項目 | 内容 |
|---|---|
| レシピ画像の自動生成 | DALL-Eなどの画像生成AIを連携させ、レシピに画像を表示する。 |
| AI応答の高速化 | レシピのレスポンスに時間がかかっているので、Server-Sent Events (SSE)を利用したストリーミング表示を実装し、体感速度を向上させる。 |
| テストの拡充と自動化 | コードカバレッジを計測し、E2EテストをCIに組み込んで品質保証を自動化する。 |
- トークンの保持(フロントエンド: Piniaストア)
- フロントエンドは受け取ったアクセストークンを、状態管理ライブラリPiniaのストアに保存します。
- さらに、
localStorageにもトークンを保存することで、ページをリロードしてもログイン状態が維持されるようにしました。
- トークンの検証(バックエンド:
dependencies.py)- フロントエンドは、認証が必要なAPI(
/recipesなど)を呼び出す際、作成した共通関数useApiFetchを使って、リクエストヘッダーにAuthorization: Bearer <トークン>の形でトークンを付与します。 - バックエンドは、
get_current_userという依存性注入関数で、送られてきたトークンを検証します。 - トークンが改ざんされていないか、有効期限が切れていないかを確認し、問題がなければユーザー情報をDBから取得して、APIの処理を続行します。
- もしトークンが無効であれば、
401 Unauthorizedエラーを返します。
- フロントエンドは、認証が必要なAPI(
| 項目 | 内容 |
|---|---|
| レシピ画像の自動生成 | DALL-Eなどの画像生成AIを連携させ、レシピに画像を表示する。 |
| AI応答の高速化 | レシピのレスポンスに時間がかかっているので、Server-Sent Events (SSE)を利用したストリーミング表示を実装し、体感速度を向上させる。 |
| テストの拡充と自動化 | コードカバレッジを計測し、E2EテストをCIに組み込んで品質保証を自動化する。 |