# 画面専用APIポリシー

画面が必要とするデータは、画面専用のAPIエンドポイントで提供する。汎用APIの流用で画面を組み立てない。

## 原則

| 原則 | 基準 |
|------|------|
| 画面とAPIの対応 | 画面が必要とするデータ構造に合わせた専用エンドポイントを用意する |
| 一覧と詳細の分離 | 一覧APIのレスポンスを詳細画面で使い回さない |
| 集計は集計APIで | 件数・合計・判定に全件取得して手元で集計しない |
| ページング責務はサーバー | ページサイズ・ソート順・フィルタ条件はサーバーが管理する |
| 不足はAPIを足す | フロントで回避するのではなく、バックエンドにエンドポイントを追加する |

## 画面単位のAPI設計

画面ごとに「その画面が何を表示し、何を操作するか」を基準にエンドポイントを設計する。

| パターン | 判定 | 理由 |
|---------|------|------|
| 一覧画面が一覧API、詳細画面が詳細APIを使う | OK | 画面責務とAPI責務が一致 |
| 詳細画面が一覧APIを叩いて結果からIDで検索する | REJECT | 不要なデータを取得し、APIの契約もずれる |
| 判定用に全件取得してフロントで集計する | REJECT | サーバーに集計APIを用意する |
| 処理中と完了済みを同じAPIで取って画面で仕分ける | REJECT | サーバー側で分けて返す |
| 既存APIにない情報が画面に必要になった | APIを追加する | フロントで別APIを組み合わせて代替しない |

```typescript
// REJECT - 一覧APIを詳細画面で流用
async function loadDetail(id: string) {
  const list = await fetchList({ date })
  return list.items.find(item => item.id === id)
}

// OK - 詳細画面は詳細APIを使う
async function loadDetail(id: string) {
  return await fetchDetail(id)
}
```

## ページングとデータ量の制御

ページングの責務はサーバーに寄せる。フロントはページサイズを指定せず、サーバーが適切な件数を返す。

| パターン | 判定 |
|---------|------|
| サーバーがデフォルトのページサイズを持ち、フロントは nextId だけ送る | OK |
| フロントが limit を指定してサーバーに送る | 避ける（サーバーだけで変更できなくなる） |
| 一覧APIが件数制限なしで全件返す | REJECT |
| サーバーが limit の上限をバリデーションする | OK |

```typescript
// 避ける - フロントがページサイズを決めている
const result = await fetchList({ date, limit: 10, nextId })

// OK - フロントは nextId だけ送り、ページサイズはサーバー任せ
const result = await fetchList({ date, nextId })
```

## 集計と判定の責務

「何件あるか」「生成可能か」「再生成すべきか」のような判定はサーバーが行い、結果を返す。

| パターン | 判定 |
|---------|------|
| 一括生成の可否判定のために記録全件を取得する | REJECT |
| 集計APIで確認済み件数・未確認件数を返す | OK |
| 再生成可能フラグをサーバーが計算して返す | OK |
| フロントで生成済みメモIDを比較して再生成可能を判定する | REJECT |

```typescript
// REJECT - 判定のために全件取得
const memos = await fetchAllMemos({ date })
const canGenerate = memos.filter(m => m.confirmed).length > 0

// OK - サーバーが集計を返す
const counts = await fetchMemoCounts({ date })
// counts: { childId, memoCount, confirmedCount, unconfirmedCount }
```

## タブ・画面遷移と通信スコープ

通信はタブ・画面単位で閉じる。他タブのために先読みしない。

| パターン | 判定 |
|---------|------|
| タブ切替で表示中の画面だけが通信する | OK |
| 全タブ共通の親でまとめて通信し、子タブに配る | REJECT |
| 定期ポーリングは表示中の画面だけで行う | OK |
| 非表示タブでもポーリングが動き続ける | REJECT |

## バックエンド側の責務

フロントが画面専用APIを使えるよう、バックエンドは以下を提供する。

| 責務 | 内容 |
|------|------|
| 一覧と詳細の分離 | 一覧は軽量なサマリー、詳細は必要な情報を含む |
| 関連データの内包 | 画面が必要とする関連データはレスポンスに含める（N+1回避） |
| 集計エンドポイント | 件数・確認状況等の集計は専用エンドポイントで返す |
| 判定フラグの算出 | 再生成可否・生成可否等はサーバーが計算してフラグで返す |
| ページング基盤 | nextId カーソル、デフォルトページサイズ、上限バリデーション |

## 禁止事項

- **汎用API流用** - 一覧APIを詳細画面で、全件取得を集計目的で使わない
- **フロント側集計** - 全件取得してフロントで件数を数えない
- **クロスタブ通信** - 他タブが使うデータを先読みしない
- **ページサイズのフロント指定** - サーバーに任せる
- **APIがないからフロントで代替** - 専用エンドポイントを追加する
