以前共同開発したプロジェクトの全範囲を理解する。
utils/
supabase/
- 事前知識
import { create(Server/Browser)Client } from '@supabase/ssr'
suoabaseへのrequest送信元(server/client)の違い
supabase/ssr :
Supabase が提供するユーティリティパッケージ。
server/client実行環境で Supabase クライアントを安全に作成・セッション連携できるようにするもの。
-
ここでいうサーバー側 : リクエストを受けてレスポンスを返す処理や、SSR(Server Side Rendering)、API エンドポイント処理を行う場所
ex.)Webフレームワーク系(Next.js の Server Components, Remix の loaders / actions), ランタイム環境(Node.js, Deno), サーバーレス実行環境(Vercel Functions / Edge Functions) -
逆にここでいうブラウザ側 : ユーザーの端末で動く クライアント実行環境
ex.)Next.js / React の Client Components, script タグ, SPA -
まとめ
requestが、- ブラウザ→supabase : セッションはクッキーや localStorage に保持, APIキーを安全に隠せない(public keyしか置けない)
- サーバー→supabase : クライアントからのリクエストを受けて処理・認証連携, 秘密鍵(service_role key など)を安全に扱える
- server.ts
requestのcookie ・ Supabase session 間の連携
next/headers の cookies() から cookieStore を取得
→ getAll(), setAll()
- middleware.ts
updateSession() :
サーバーランタイムで Supabase セッションを検証・同期し、
未認証ユーザーを /login にリダイレクトする
supabase.auth.getUser()
- supabaseResponseを作成
- cookieとやり取り
- getAll(), setAll(resにcookieをset)
- client.ts
clientを返す
Web Speech API 管理
Web Speech API : JavaScript APIの一種のweb APIの一種
sound → 認識
sound ← text
の機能。
用途別にオブジェクトがあり(ex.)SpeechRecognition)、それらを個々でラップして使用。
-
JavaScript API : 「JavaScriptで呼び出せるAPI」の総称。
Webブラウザ上で使えるもの(Web API)もあれば、Node.jsのようなサーバー環境専用のAPIもある- Web APIの利点
JSという共通の言語から直接APIにアクセスできる
ブラウザでもサーバーでも「同じ言語」でAPIを叩ける。
OSも問わない。
低レベルOS機能を直接触らずJSで呼べる。
JSは「ユーザー操作に即応する言語」だから、APIを直接呼び出せるとリアルタイムUIや非同期処理が作りやすい。
- Web APIの利点
技術 : TypeScriptのクラスベースOOP+ES Modulesによるモジュール設計。
- speech-recognition.ts
音声認識開始, 停止, 一時停止・再開, 結果コールバック呼び出し
役割 :
Web Speech API を簡易ラップし、「シェフ」というウェイクワードを検出して以降の確定結果だけをアプリへ渡す
仕様 :
class内で、[class内部のfieldをprivateで定義]し、constructor[環境ブラウザのSpeechRecognitionを取得・設定]、fieldを使って呼び出すmethodを定義。
class : Web Speech APIのインターフェース(SpeechRecognition(標準) / webkitSpeechRecognition(古い))ラップ
デザインパターン :
getSpeechRecognition() : シングルトンの取得関数
シングルトン : インスタンスが常に 1つだけ(アプリ全体で1つしかない設定管理)
→ マイク入力は複雑な処理のため、シングルトンで管理。
- speech-synthesis.ts
読み上げ開始, 停止, キュー管理
テキストをキューに入れて順番に合成音声で読み上げ、start / end イベントを通知
class : Web Speech APIのインターフェース(SpeechSynthesis)ラップ
デザインパターン :
getSpeechSynthesis() : シングルトンの取得関数
アプリ全体で単一の発話キューを管理。
- timer-Logic.ts
[タイマーの開始, 停止, reset, "MM:SS", "M:SS"]機能
class TimerLogic() :
文字列形式の初期時間("MM:SS")を受け取り、1秒ごとにコールバックを呼ぶシンプルなカウントダウンタイマー
types/
- global.d.ts
SpeechRecognitionの型定義
global.d.ts :
グローバルに型を追加/拡張するファイル
d. : DefinitelyTyped
global.d.ts に型定義すべきもの :
ブラウザのグローバル環境(window オブジェクト)に存在する Web APIs のうち、TypeScript の組み込み型定義(lib.dom.d.ts)に含まれていないもの。
Next.jsにglobal.d.ts がよくあるのはなぜ?
client/server間で共有するため、グローバル(process.env やセッションオブジェクト)を直接扱うから。
(Remix はサーバー経由で必要な情報だけ渡す設計だから不要になりやすい。)
- recipeTypes.tss
IngredientTypes, StepTypes, RecipeTypes, GeneratedRecipeTypes
lib/
共通で使うロジック
- utils.ts
cn() :
clsxとtailwind-mergeで、TailwindCSS のクラス名を条件付きで結合・最適化
clsx :
JavaScriptやReactで、条件によってCSSのクラス名文字列を動的に生成する。
tailwind-merge :
複数のクラスの優先度を自動判別し最適化された1つのクラスを返す。
- handleVoiceQuery.ts
音声認識で取得したテキストを解析し、
- キーワードマッチング(step遷移やtimer処理等頻繁に使うもの)
- マッチしないものはAIへの質問
とすることで効率化している。
- atoms.ts
レシピアプリの主要な状態をatom化し、client側で使い回せる。
import { atom } from 'jotai'
jotai: React 向けの軽量な状態管理ライブラリ
atom: Jotai の基本的な状態単位
services/
- imageService.ts
uploadImage() :
- Supabase Storage を使用して画像をアップロード(バケット,キャッシュ指定)
- 公開 URL を取得する
- recipeService.ts
Supabase を使ってレシピデータ(recipes テーブル)を取得・登録するためのサービス関数群。
getAllRecipes() :
return : types/の型
supabase データ取得方法 :
supabase.from(バケット).select(SQL).order()
getAllFavoriteRecipes()
getRecipeById()
createRecipe() : レシピ,手順,材料もinsert
openai/
- visionToService.ts
OpenAIのVision API(GPT-4.1 Visionモデル)を使用し、画像(Base64データURI)から料理材料を自動抽出する。
OpenAIのAPI :
- Chat Completions / GPT系
チャット形式でテキスト生成ができる。(chat GPTもこれに含まれる。) - Vision API
画像分析 - Images API
テキストから画像を生成 - Whisper
STT - Embeddings
テキストをベクトル化し、検索・分類・類似度計算に使用。 - Codex
自然言語からコードを書く支援をする。(GitHub Copilotがこれ) - エージェント/ツール統合系
Responses API / Agents SDK
zodで(name, amount, unit)をチェック。
zodの有効性 :
typeはコンパイル時チェックのみだが、zodは実行中に変化するデータをチェックする。
環境変数のAPI keyでOpenAI clientを初期化。
↓
detectIngredients() :
画像(Base64データURI)から料理材料を自動抽出する。
OpenAI Vision API へプロンプトでリクエスト送信。
- textToService.ts
OpenAI API(GPT-4系)を使ってレシピ生成やQA応答を行う。
generateRecipesFromIngredients() :
引数IngredientTypesからレシピ生成指示プロンプトを作成。
const response = await openaiText.chat.completions.create({
model: "gpt-4.1-mini-2025-04-14",
messages: [
{
role: "system",
content:
"あなたは料理の専門家で、指定された材料から魅力的なレシピをできるだけ多く生成します。ただし、座領リストに含まれていないものは使用しないこと出力は指定されたJSON形式に従ってください。",
},
{ role: "user", content: promptText },
],
response_format: zodResponseFormat(RecipesResponseSchema, "recipe_generation"),
max_tokens: 2000,
temperature: 0.7,
});
上記property keyの補足
max_tokens : 返答の長さの上限を指定。
temperature : 出力のランダム性(創造性) を調整する。
0 : 決定的で再現性が高い ~ 2 : 非常に多様でランダム
answerQuestion() :
レシピ情報・現在の調理段階を踏まえた上での質問文をプロンプトへ。JSONで返すよう指示。
detectIngredients()も、answerQuestion()も、openAIのStructured Outputsで返却型を指定している。
Structured Outputs :
返却型が、開発者が提供するJSONスキーマと完全に一致するように設計。
今回は、zod判定で結果的にJSON指定をしている。
components/
- header.tsx
- Loading.tsx
- login-prompt-modal.tsx
- recipe-popup.tsx
ui/
- button.tsx
radix(根本) UI :
UIではあるが、見た目では無く「振る舞い」を制御。
slot :
asChildにより、propsを子要素にそのまま渡せるため、他の要素にも柔軟に適用できる
VariantProps(CVA) :
条件によって異なるクラス名を管理
- card.tsx
- TimerUI.tsx
app/
api/:APIルート(認証、AI、画像、レシピなどのAPIエンドポイント)
api-test/:APIテスト用のページやサブディレクトリ
cooking/:調理ステップや写真提出などのページ
favicon.ico:ファビコン
fonts/:フォントファイル
globals.css:グローバルCSS
history/:履歴ページ
ingredients/:食材ページ
layout.tsx:アプリ全体のレイアウト
login/:ログインページ
page.tsx:トップページ
recipe-book/:レシピ帳ページ
recipes/:レシピ一覧・詳細ページ
scan/:スキャンページ
api/
next.jsでは、api/にバックエンドAPIエンドポイントを置く。
ここに置いたファイルは、サーバーサイドでのみ実行され、APIリクエスト(例: /api/auth/login)に応答します。
ExpressやFastAPIのようなバックエンドの役割を、Next.jsの中で完結できます。
ここからlib/serviceやutil/supabaseを呼ぶ。
APIルートは「窓口」として機能し、実際の処理はサービスやユーティリティ層に分離することで、保守性・再利用性が高まります。
理解順序
- ファイル内の構造理解
- 技術工夫点