Skip to content

API

構成方針

  • API 本体は Hono で実装する。
  • Nuxt の server/api/[...].ts から Hono アプリをマウントし、SSR と同一 Worker で配信する。
  • フロント-サーバ間の型共有は Hono RPC を使う(Hono の型推論を hono/client 側で受ける)。

Nuxt とのマウント

ts
// app/server/api/[...].ts (擬似コード)
import { app } from '~~/server/hono'
export default defineEventHandler((event) => {
  return app.fetch(event.node.req as any, {
    DB: useDB(event),
    KV: useKV(event),
    R2: useR2(event),
    SECRETS: useSecrets(event),
  })
})

ルート設計

Prefix用途
/api/v1/auth/*Better Auth ルート
/api/v1/me自分自身の情報
/api/v1/tenants/:tenantId/...テナントスコープのリソース
/api/v1/customers自テナントの顧客(暗黙テナント)
/api/v1/contracts同上
/api/v1/schedules同上
/api/v1/activities同上
/api/v1/files書類 / 画像
/api/v1/line/channels / /messages / /menus / /surveysLINE 関連
/api/v1/billing/*テナント課金(OWNERのみ)
/api/admin/*ADMIN 用
/api/webhooks/line/:channelIdLINE Webhook
/api/webhooks/stripe / /api/webhooks/square決済 Webhook

「テナントID をパス必須にするか暗黙にするか」: 暗黙(セッション由来) を基本 とし、ADMIN による横断操作のみ :tenantId を明示する /api/admin/tenants/:tenantId/... を用意。

ミドルウェア順

  • Auth: Cookie からセッション復元
  • Tenant: user.tenantId をコンテキストへ
  • RoleGuard: ルート単位で requireRole('OWNER')
  • Validation: 入力は Zod スキーマ。RPC の型推論にも使う
  • Error MW: HTTPException を 4xx/5xx へ整形

入力検証とレスポンス

  • 入力: zValidator('json', schema) / 'query' / 'param'
  • レスポンス形:
    json
    // 正常
    { "data": { ... } }
    // エラー
    { "error": { "code": "FORBIDDEN", "message": "...", "details": {...} } }
  • HTTPステータスは意味と一致させる。200 / 201 / 204 / 400 / 401 / 403 / 404 / 409 / 422 / 429 / 500

エラーコード(暫定)

コード意味
UNAUTHENTICATED未認証
FORBIDDEN認可エラー(テナント越境を含む)
NOT_FOUNDリソース未存在
VALIDATION_ERROR入力検証エラー
CONFLICT競合(一意制約等)
RATE_LIMITEDレート制限
INTEGRATION_ERROR外部API失敗
INTERNAL想定外

Hono RPC クライアント(フロント側)

ts
// app/composables/useApi.ts (擬似コード)
import { hc } from 'hono/client'
import type { AppType } from '~~/server/hono'

export const useApi = () => hc<AppType>('/api/v1')
  • ルート定義の戻り値型をフロントで再利用 → リクエスト/レスポンスの取り違えがコンパイル時に検出される。
  • useApi() を Nuxt の useFetch と組み合わせず、SSR時は Worker 内で直呼び(app.request(...))するパターンも検討。

ページネーションとカーソル

  • 大量データは カーソルベース が基本(?cursor=...&limit=50)。
  • 集計や全件取得が必要な箇所は管理画面に隔離。

レート制限

  • パブリックエンドポイント / Webhook には KV ベースの簡易レート制限を入れる。
  • 一斉配信などの自エンドポイントはアプリ層でジョブ化(Cloudflare Queues検討)。

ロギング

  • リクエスト ID(crypto.randomUUID())を付与し全ログに含める。
  • センシティブ情報(メール本文、LINE 本文、PII)はログに残さない or マスク。

未確定

  • バージョニング戦略(v1 を破壊的変更で v2