0009. API を Cloudflare Workers (Hono) で構築する¶
- ステータス: Accepted(実装済み)
- 関連: 0002 Supabase を採用 / 0010 環境分割 / 0011 公開オンボーディング
コンテキストと課題¶
クライアントは基本 Supabase に直接アクセスする (ADR 0002) が、
RLS をバイパスする service_role を要する操作 (他人の auth ユーザー作成・メンバー管理など)、
認証前に叩く公開エンドポイント (組織サインアップ)、秘密鍵を伴う外部連携 (請求・将来の
Claude Vision OCR など) はブラウザに置けない。これらを担うサーバ側のレイヤーが必要。
決定¶
Hono を Cloudflare Workers 上で動かす API (apps/api) を導入する。
- ランタイム / FW: Cloudflare Workers + Hono (
wrangler.toml:name = "waste-link-api",main = "src/index.ts",compatibility_flags = ["nodejs_compat"])。env.staging/env.productionで別 Worker (waste-link-api-staging/-production) にデプロイ (ADR 0010)。 - ルート構成 (
src/index.ts):/health・/signup(公開)・/team・/manifests・/vehicle-costs・/billing。 CORS は許可オリジンを動的判定。 - 認証ミドルウェア (
middleware/auth.tsのrequireAuth):Authorization: Bearer <token>を 受け、anon クライアントでauth.getUser()して JWT を検証。app_metadataからtenantId/tenantType/roleを取り出して context にセットする。 - service_role クライアント (
getServiceRoleClient):SUPABASE_SERVICE_ROLE_KEYで生成 (autoRefreshToken: false,persistSession: false)。管理操作と公開/signupがこれを使う。 公開/signupはrequireAuthを通さない代わり、招待コード検証を主防御とする (ADR 0011)。 - Web からの呼び出し:
VITE_API_URLを API ベース URL として使う (未設定時は staging Worker URL に フォールバック)。メンバー管理・組織登録・請求・(排出業者の) マニフェスト作成などで利用。 - 秘密鍵の置き方: 外部 API キー (例
CLAUDE_API_KEY) はwrangler secret put ... --env <env>か Dashboard で Worker に投入する (デプロイワークフローからは設定しない)。
影響 (Consequences)¶
良い点¶
service_roleのような強い権限を サーバ側 (Worker) に隔離 でき、ブラウザに秘密を出さずに 特権操作・公開サインアップ・外部連携を実装できる。- エッジ実行で低レイテンシ、
apps/webと同じ Cloudflare 上に収まり、共有スキーマ (@workspace/shared) を API でも再利用できる。 - 環境 (staging/production) ごとに別 Worker へ分離でき、Web/DB の環境分割と整合する。
トレードオフ / 注意点¶
- データ経路が二系統になる (クライアント直 Supabase + API 経由)。どの操作をどちらに置くかの 判断が要り、認可も RLS と API の両方で意識する必要がある。
service_roleは RLS を完全にバイパスするため、getServiceRoleClientを使う各エンドポイントが 自前でテナント/権限チェックを行う責務を負う (ミスれば越権)。VITE_API_URLを環境ごとに正しく注入する必要がある (未設定だと staging にフォールバックする)。- README には当初「API 層は初期実装に含めない (必要になれば Hono + Workers で追加)」とあったが、 本 ADR の通り 実際に追加済み。README の当該記述は経緯説明として残っている。
根拠 (典拠)¶
apps/api/package.json:17-27—hono ^4.6.0/wrangler ^3.90.0/@cloudflare/workers-types/@anthropic-ai/sdk/@supabase/supabase-js/@workspace/shared workspace:*。apps/api/wrangler.toml:1-37—name/main/compatibility_date = "2026-05-29"/compatibility_flags = ["nodejs_compat"]/[env.staging]/[env.production]。apps/api/src/index.ts:29-60— Hono app 生成、logger、CORS、ルート (/health//signup//team//manifests//vehicle-costs//billing)。apps/api/src/middleware/auth.ts:24-58,81-90—requireAuth(anon クライアント +auth.getUser()+app_metadata取り出し) とgetServiceRoleClient(SUPABASE_SERVICE_ROLE_KEY)。apps/api/src/routes/signup.ts:203— 公開/signupがgetServiceRoleClientを使う。apps/web/src/lib/db/team.tsほか —import.meta.env.VITE_API_URL ?? '...staging.workers.dev'。.github/workflows/deploy-api.yml:8-19— Worker 秘密 (CLAUDE_API_KEY等) はワークフローからは設定しない方針。README.md:21— API 層を Hono + Cloudflare Workers で別タスク追加する旨 (本決定の前提)。