コンテンツにスキップ

0009. API を Cloudflare Workers (Hono) で構築する

コンテキストと課題

クライアントは基本 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.tsrequireAuth): 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 がこれを使う。 公開 /signuprequireAuth を通さない代わり、招待コード検証を主防御とする (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-27hono ^4.6.0 / wrangler ^3.90.0 / @cloudflare/workers-types / @anthropic-ai/sdk / @supabase/supabase-js / @workspace/shared workspace:*
  • apps/api/wrangler.toml:1-37name/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-90requireAuth(anon クライアント + auth.getUser() + app_metadata 取り出し) と getServiceRoleClient(SUPABASE_SERVICE_ROLE_KEY)。
  • apps/api/src/routes/signup.ts:203 — 公開 /signupgetServiceRoleClient を使う。
  • 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 で別タスク追加する旨 (本決定の前提)。