# 機能設計書 39-Supabaseクライアント

## 概要

本ドキュメントは、Liam DBアプリケーションで使用されるSupabaseクライアント生成機能について記述する。本機能は、Server Components、Client Components、バックグラウンドジョブの3つの実行コンテキストに応じた型安全なSupabaseクライアントを提供する。

### 本機能の処理概要

**業務上の目的・背景**：Next.js AppRouterでは、Server ComponentsとClient Componentsで異なるSupabaseクライアントが必要である。また、バックグラウンドジョブ用にはSSRに依存しないクライアントが必要である。本機能は、これらの要件を満たす型安全なクライアント生成関数を提供する。

**機能の利用シーン**：
- Server Components/Server Actionsでのデータベースアクセス
- Client Componentsでのデータベースアクセス
- バックグラウンドジョブ（cron、webhook）でのデータベースアクセス

**主要な処理内容**：
1. createServerClient：Server Components/Server Actions用（@supabase/ssr）
2. createBrowserClient：Client Components用（@supabase/ssr）
3. createClient：バックグラウンドジョブ用（@supabase/supabase-js）
4. toResultAsync：neverthrowによるエラーハンドリング
5. retry：エクスポネンシャルバックオフによるリトライ

**関連システム・外部連携**：
- @supabase/ssr：SSR対応Supabaseクライアント
- @supabase/supabase-js：基本Supabaseクライアント
- neverthrow：Result型によるエラーハンドリング
- type-fest：MergeDeepによる型オーバーライド

**権限による制御**：
Supabaseの Row Level Security (RLS) により制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 全画面 | 共通 | 利用画面 | 全データベースアクセス |

## 機能種別

インフラストラクチャライブラリ

## 入力仕様

### 入力パラメータ

#### createServerClient / createBrowserClient

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| supabaseUrl | string | Yes | Supabase URL | 環境変数から取得 |
| supabaseKey | string | Yes | Supabase Anon Key | 環境変数から取得 |
| options | object | No | Cookie設定等 | - |

#### toResultAsync

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| queryBuilder | PromiseLike | Yes | Supabaseクエリ | - |
| options.allowNull | boolean | No | null許可 | デフォルト: false |

#### retry

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fn | () => ResultAsync | Yes | リトライ対象関数 | - |
| options.maxAttempts | number | No | 最大試行回数 | デフォルト: 3 |
| options.baseDelayMs | number | No | 基本遅延（ms） | デフォルト: 1000 |
| options.maxDelayMs | number | No | 最大遅延（ms） | デフォルト: 8000 |
| options.backoffMultiplier | number | No | 遅延倍率 | デフォルト: 2 |

### 入力データソース

- 環境変数（NEXT_PUBLIC_SUPABASE_URL、NEXT_PUBLIC_SUPABASE_ANON_KEY）
- Cookie（認証情報）

## 出力仕様

### 出力データ

- SupabaseClientインスタンス
- ResultAsync<T, Error>（toResultAsync）
- ResultAsync<T, E>（retry）

### 出力先

- アプリケーションコード内での利用

## 処理フロー

### 処理シーケンス

#### クライアント生成フロー

```
1. コンテキスト判定
   ├─ Server Components → createServerClient
   ├─ Client Components → createBrowserClient
   └─ Jobs → createClient
2. 型パラメータ適用
   └─ AppDatabaseOverrides（カスタム型）
3. クライアント生成
   └─ Supabaseクライアントインスタンス返却
```

#### toResultAsyncフロー

```
1. クエリビルダー受け取り
2. ResultAsync.fromPromiseでラップ
3. エラー判定
   ├─ error存在 → errAsync(Error)
   ├─ data=null かつ allowNull=false → errAsync(Error)
   └─ 成功 → okAsync(data)
```

### フローチャート

```mermaid
flowchart TD
    A[データベースアクセス] --> B{実行コンテキスト}
    B -->|Server Components| C[createServerClient]
    B -->|Client Components| D[createBrowserClient]
    B -->|Jobs| E[createClient]
    C --> F[AppDatabaseOverrides適用]
    D --> F
    E --> F
    F --> G[Supabaseクエリ実行]
    G --> H[toResultAsync]
    H --> I{結果判定}
    I -->|成功| J[okAsync data]
    I -->|エラー| K[errAsync Error]
    I -->|null + !allowNull| L[errAsync 'No data returned']
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | コンテキスト別クライアント | Server/Client/Jobで異なるクライアント使用 | 全データベースアクセス |
| BR-39-02 | 型オーバーライド | MergeDeepでカスタム型をマージ | 全クライアント |
| BR-39-03 | nullエラー変換 | allowNull=false時はnullをエラーとする | toResultAsync |
| BR-39-04 | リトライ対象エラー | ネットワークエラー/5xxエラーのみリトライ | retry |
| BR-39-05 | エクスポネンシャルバックオフ | 遅延 = baseDelay * multiplier^(attempt-1) | retry |

### リトライ対象エラーパターン

| パターン | 説明 |
|---------|------|
| fetch failed | fetchエラー |
| network error | ネットワークエラー |
| timeout | タイムアウト |
| connection | 接続エラー |
| socket hang up | ソケット切断 |
| enotfound / econnreset / etimedout | DNS/接続エラー |
| 500 / 502 / 503 / 504 | サーバーエラー |
| bad gateway / gateway timeout / service unavailable | ゲートウェイエラー |

### 型オーバーライド対象

| テーブル | オーバーライド内容 |
|---------|------------------|
| schema_file_paths | カスタム型 |
| project_repository_mappings | カスタム型 |
| design_sessions | カスタム型 |
| building_schemas | カスタム型 |
| checkpoints | カスタム型 |
| checkpoint_blobs | カスタム型 |
| checkpoint_writes | カスタム型 |

## データベース操作仕様

本機能自体はデータベース操作を行わないが、データベースアクセスの基盤を提供する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | No data returned | allowNull=false でデータがnull | クエリ条件の確認 |
| - | ネットワークエラー | 接続失敗 | retryで自動リトライ |
| - | 認証エラー | トークン無効/期限切れ | 再ログイン |

### リトライ仕様

- 最大試行回数：3回（デフォルト）
- 基本遅延：1000ms
- 最大遅延：8000ms
- 遅延計算：min(baseDelay * 2^(attempt-1), maxDelay)

## トランザクション仕様

Supabaseの標準トランザクション機能に依存。

## パフォーマンス要件

- クライアント生成は即座に完了
- リトライ時の遅延は最大8秒

## セキュリティ考慮事項

- Anon Keyのみをクライアントサイドで使用
- Service Role Keyはサーバーサイドのみ
- RLSによるアクセス制御

## 備考

- neverthrowのResult型でエラーハンドリングを統一
- MergeDeepによる型のディープマージ
- 自動生成型ファイルとの整合性維持

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: エントリポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `frontend/internal-packages/db/src/index.ts` | 3種類のクライアント生成関数 |

**主要処理フロー**:
- **1-5行目**: @supabase/ssr、@supabase/supabase-jsからのインポート
- **8-17行目**: 型のエクスポート
- **21行目**: SupabaseClientType型定義
- **23-24行目**: createServerClient - Server Components用
- **26-27行目**: createBrowserClient - Client Components用
- **29-30行目**: createClient - Jobs用

#### Step 2: 型オーバーライドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | types/supabase-overrides/index.ts | `frontend/internal-packages/db/src/types/supabase-overrides/index.ts` | MergeDeepによる型マージ |

**主要処理フロー**:
- **1行目**: type-festのMergeDeepインポート
- **2行目**: 自動生成されたDatabase型
- **3-9行目**: 各テーブルのオーバーライド型インポート
- **11-20行目**: AppDatabaseOverrides - 全オーバーライドをマージ

#### Step 3: toResultAsyncを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | utils/toResultAsync.ts | `frontend/internal-packages/db/src/utils/toResultAsync.ts` | neverthrowによるラップ |

**主要処理フロー**:
- **6-8行目**: オプション型定義（allowNull）
- **31-44行目**: オーバーロード定義（3種類）
- **45-72行目**: 実装 - ResultAsync.fromPromise使用
- **55-68行目**: エラー判定ロジック

#### Step 4: リトライ機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | utils/retry.ts | `frontend/internal-packages/db/src/utils/retry.ts` | エクスポネンシャルバックオフ |

**主要処理フロー**:
- **6-11行目**: RETRY_CONFIG定数
- **16-46行目**: isRetryableError - リトライ対象判定
- **51-56行目**: RetryOptions型
- **61-96行目**: retry関数 - 再帰的リトライ実装
- **82-85行目**: 遅延計算（エクスポネンシャルバックオフ）

### プログラム呼び出し階層図

```
@liam-hq/db
    ├─ createServerClient（Server Components用）
    │      └─ @supabase/ssr.createServerClient<AppDatabaseOverrides>
    │
    ├─ createBrowserClient（Client Components用）
    │      └─ @supabase/ssr.createBrowserClient<AppDatabaseOverrides>
    │
    ├─ createClient（Jobs用）
    │      └─ @supabase/supabase-js.createClient<AppDatabaseOverrides>
    │
    ├─ toResultAsync
    │      └─ neverthrow.ResultAsync
    │             ├─ fromPromise
    │             ├─ okAsync
    │             └─ errAsync
    │
    └─ retry
           └─ neverthrow.ResultAsync
                  └─ fromPromise（再帰的リトライ）

AppDatabaseOverrides（型）
    └─ MergeDeep
           ├─ DatabaseGenerated（自動生成）
           ├─ SchemaFilePathsOverride
           ├─ ProjectRepositoryMappingsOverride
           ├─ DesignSessionsOverride
           ├─ BuildingSchemasOverride
           ├─ CheckpointsOverride
           ├─ CheckpointBlobsOverride
           └─ CheckpointWritesOverride
```

### データフロー図

```
アプリケーションコード
         │
         ↓
┌────────────────────────────────┐
│ createServerClient/            │
│ createBrowserClient/createClient│
│ <AppDatabaseOverrides>         │
└────────────┬───────────────────┘
             │
             ↓
┌────────────────────────────────┐
│ Supabaseクエリ実行             │
│ client.from('table').select()  │
└────────────┬───────────────────┘
             │
             ↓
┌────────────────────────────────┐
│ toResultAsync                  │
│ (オプショナル)                 │
└────────────┬───────────────────┘
             │
    ┌────────┴────────┐
    ↓                 ↓
okAsync(data)    errAsync(Error)
    │                 │
    ↓                 ↓
Result.isOk()    Result.isErr()
    │                 │
    ↓                 ↓
data取得         エラーハンドリング
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `frontend/internal-packages/db/src/index.ts` | ソース | エントリポイント |
| toResultAsync.ts | `frontend/internal-packages/db/src/utils/toResultAsync.ts` | ソース | ResultAsync変換 |
| retry.ts | `frontend/internal-packages/db/src/utils/retry.ts` | ソース | リトライ機能 |
| types/index.ts | `frontend/internal-packages/db/src/types/index.ts` | ソース | 型エクスポート |
| supabase-overrides/index.ts | `frontend/internal-packages/db/src/types/supabase-overrides/index.ts` | ソース | 型オーバーライド定義 |
| database.types.ts | `frontend/internal-packages/db/supabase/database.types.ts` | ソース | 自動生成型定義 |
| package.json | `frontend/internal-packages/db/package.json` | 設定 | 依存関係定義 |
