# 機能設計書 74-headers()

## 概要

本ドキュメントは、Next.jsの`headers()`関数の設計について記述する。`headers()`はServer Components、Server Actions、Route Handlers、Middleware内でHTTPリクエストヘッダーを読み取るための非同期API関数である。

### 本機能の処理概要

`headers()`関数は、現在のリクエストのHTTPヘッダー情報に読み取り専用でアクセスするためのAPI関数である。Promiseを返し、awaitすることでReadonlyHeadersオブジェクトが得られる。動的レンダリングを強制するDynamic APIの1つであり、静的生成時にはプリレンダリングの中断やポストポーンが発生する。

**業務上の目的・背景**：Server Components内でリクエストヘッダー（Accept-Language、Authorization、Cookie等）にアクセスする必要がある場面は多い。例えば、認証トークンの取得、コンテンツネゴシエーション、ユーザー言語の判定などである。`headers()`はこれらのユースケースをReactのServer Components内で安全に実現する手段を提供する。

**機能の利用シーン**：Server Componentsでの認証ヘッダーの読み取り、Accept-Languageによるロケール判定、カスタムヘッダーの取得、Server Actionsでのリクエスト情報参照、Route Handlersでのリクエストヘッダー読み取り。

**主要な処理内容**：
1. workAsyncStorage/workUnitAsyncStorageから現在の実行コンテキスト（WorkStore/WorkUnitStore）を取得
2. 実行コンテキストの種類（prerender, request, cache等）に応じた分岐処理
3. 静的生成時のハンギングPromise生成、PPRプリレンダリング時のポストポーン、レガシープリレンダリング時の中断
4. リクエスト時の動的レンダリング追跡と読み取り専用ヘッダーの返却
5. 開発モードでの同期アクセス警告（Promise未awaitで直接プロパティアクセスした場合）

**関連システム・外部連携**：AsyncLocalStorage（workAsyncStorage/workUnitAsyncStorage）、HeadersAdapter、Dynamic Rendering制御

**権限による制御**：`headers()`は読み取り専用APIであり、ヘッダーの変更はできない（append/set/deleteメソッドはReadonlyHeadersErrorをスローする）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | headers()は画面に直接関連しない。サーバーサイドのリクエスト情報アクセスに使用される |

## 機能種別

データ参照（読み取り専用）/ Dynamic API

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| - | - | - | headers()は引数を取らない | - |

### 入力データソース

- AsyncLocalStorage（workAsyncStorage）から取得されるWorkStoreの状態
- AsyncLocalStorage（workUnitAsyncStorage）から取得されるWorkUnitStoreのheadersプロパティ
- HTTPリクエストヘッダー（リクエスト処理時にRequestStoreに格納済み）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| headers | Promise\<ReadonlyHeaders\> | 読み取り専用のHTTPリクエストヘッダーオブジェクト |

### 出力先

Server Components、Server Actions、Route Handlers、Middleware内のコードで使用される。

## 処理フロー

### 処理シーケンス

```
1. workAsyncStorage/workUnitAsyncStorageから実行コンテキスト取得
   └─ WorkStore, WorkUnitStoreの参照
2. after()内実行チェック
   └─ after()内かつisRequestAPICallableInsideAfter()がfalseの場合エラー
3. forceStatic チェック
   └─ trueの場合、空のReadonlyHeadersを返す
4. WorkUnitStore種別による分岐
   └─ cache/unstable-cache: エラースロー（キャッシュ内でのDynamic API利用不可）
   └─ prerender: ハンギングPromise生成（プリレンダリングを中断させない）
   └─ prerender-ppr: postponeWithTrackingでポストポーン
   └─ prerender-legacy: throwToInterruptStaticGenerationで静的生成中断
   └─ prerender-runtime: delayUntilRuntimeStageでランタイムステージまで遅延
   └─ request: trackDynamicDataInDynamicRenderで動的レンダリング追跡後、ヘッダー返却
5. dynamicShouldError チェック
   └─ dynamic="error"設定時にStaticGenBailoutErrorをスロー
6. 結果返却
   └─ Promise<ReadonlyHeaders>として返却
```

### フローチャート

```mermaid
flowchart TD
    A[headers 呼び出し] --> B{WorkStore存在?}
    B -->|No| Z[throwForMissingRequestStore]
    B -->|Yes| C{after 内?}
    C -->|Yes & 不許可| Y[Error: after内で使用不可]
    C -->|No| D{forceStatic?}
    D -->|Yes| E[空ヘッダーを返却]
    D -->|No| F{WorkUnitStore種別}
    F -->|cache| G[Error: cache内で使用不可]
    F -->|unstable-cache| H[Error: unstable_cache内で使用不可]
    F -->|prerender| I[ハンギングPromise生成]
    F -->|prerender-ppr| J[postponeWithTracking]
    F -->|prerender-legacy| K[throwToInterruptStaticGeneration]
    F -->|prerender-runtime| L[delayUntilRuntimeStage]
    F -->|request| M{dynamicShouldError?}
    M -->|Yes| N[StaticGenBailoutError]
    M -->|No| O[trackDynamicDataInDynamicRender]
    O --> P{NODE_ENV === development?}
    P -->|Yes| Q[Dev警告付きヘッダー返却]
    P -->|No| R[通常ヘッダー返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-74-01 | Dynamic API | headers()はDynamic APIであり、呼び出すと動的レンダリングが強制される | 常時 |
| BR-74-02 | 読み取り専用 | 返却されるHeadersオブジェクトは読み取り専用。append/set/deleteはReadonlyHeadersErrorをスロー | 常時 |
| BR-74-03 | キャッシュ内使用不可 | "use cache"やunstable_cache内でのheaders()呼び出しはエラー | cache/unstable-cache コンテキスト |
| BR-74-04 | after内制限 | after()コールバック内でのheaders()は原則使用不可（after()外で取得してから渡す） | after()内 |
| BR-74-05 | 非同期API | headers()はPromiseを返す。awaitまたはReact.use()で値を取得する必要がある | 常時 |
| BR-74-06 | forceStatic時の動作 | forceStaticが有効な場合、空のヘッダーオブジェクトが返される | forceStatic=true |

### 計算ロジック

特になし。

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

### 操作別データベース影響一覧

headers()はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| Error | コンテキストエラー | headers()がリクエストスコープ外で呼ばれた場合 | Server Components/Actions/Route Handlers/Middleware内で呼ぶ |
| Error | after()内使用エラー | after()コールバック内でheaders()を呼んだ場合 | after()の外でheaders()を呼び、結果を渡す |
| Error | cache内使用エラー | "use cache"内でheaders()を呼んだ場合 | キャッシュの外でheaders()を呼び、引数として渡す |
| Error | unstable_cache内使用エラー | unstable_cache()内でheaders()を呼んだ場合 | キャッシュの外でheaders()を呼ぶ |
| StaticGenBailoutError | 静的生成エラー | dynamic="error"のルートでheaders()を呼んだ場合 | dynamic設定を変更するかheaders()を使わない |
| ReadonlyHeadersError | 読み取り専用エラー | 返却されたヘッダーにappend/set/deleteした場合 | ヘッダーの変更はMiddlewareやRoute Handlerで行う |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- CachedHeaders WeakMapによる同一リクエスト内でのPromiseキャッシュ
- 開発モードでは同期アクセス警告のためのProxyオーバーヘッドあり

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

- headers()は読み取り専用であり、リクエストヘッダーの改竄は不可
- AuthorizationヘッダーやCookieヘッダーなどの機密情報がServer Components内で参照可能なため、クライアントコンポーネントへの受け渡しに注意

## 備考

- 開発モードでは`headers().get`のように同期的にプロパティアクセスした場合に警告が出力される（awaitが必要）
- `createDedupedByCallsiteServerErrorLoggerDev`によりコールサイトごとに重複を排除して警告される
- prerender-client（クライアントコンポーネントプリレンダリング）では`InvariantError`がスローされる

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | headers.ts (adapter) | `packages/next/src/server/web/spec-extension/adapters/headers.ts` | ReadonlyHeaders型、HeadersAdapterクラス、HeadersAdapter.seal()メソッドの実装を確認 |

**読解のコツ**: `HeadersAdapter`はNode.jsの`IncomingHttpHeaders`オブジェクトをWeb標準の`Headers`インターフェースに適合させるアダプタ。内部でProxyを使用してキーの大文字小文字変換を行う。`seal()`メソッドはmutatingメソッドをエラースロー関数で上書きするProxyを生成する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | headers.ts | `packages/next/src/server/request/headers.ts` | headers()関数本体（40-158行目）。WorkStore/WorkUnitStoreの取得と、WorkUnitStore種別による分岐処理を確認 |

**主要処理フロー**:
1. **42-43行目**: workAsyncStorage/workUnitAsyncStorageからストアを取得
2. **46-54行目**: after()内での使用チェック
3. **56-61行目**: forceStaticチェックと空ヘッダー返却
4. **64-88行目**: WorkUnitStore種別による分岐（cache/unstable-cache/prerender系/request）
5. **90-94行目**: dynamicShouldErrorチェック
6. **96-153行目**: 種別ごとの処理（ハンギングPromise/ポストポーン/中断/遅延/通常返却）

#### Step 3: キャッシュ機構を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | headers.ts | `packages/next/src/server/request/headers.ts` | CachedHeaders WeakMap（161行目）と各make*Headers関数の実装を確認。同一プリレンダリングストアに対してPromiseがキャッシュされる仕組みを理解する |

#### Step 4: 開発モード警告を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | headers.ts | `packages/next/src/server/request/headers.ts` | instrumentHeadersPromiseWithDevWarnings関数（228-249行目）。Promise上にget/has/set等のプロパティをdefinePropertyで定義し、同期アクセス時に警告を出す仕組みを確認 |

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

```
headers() [headers.ts:40]
    |
    +-- workAsyncStorage.getStore()
    +-- workUnitAsyncStorage.getStore()
    |
    +-- [forceStatic]
    |       +-- HeadersAdapter.seal(new Headers({}))
    |       +-- makeUntrackedHeaders()
    |
    +-- [prerender]
    |       +-- makeHangingHeaders()
    |               +-- makeHangingPromise()
    |
    +-- [prerender-ppr]
    |       +-- postponeWithTracking()
    |
    +-- [prerender-legacy]
    |       +-- throwToInterruptStaticGeneration()
    |
    +-- [prerender-runtime]
    |       +-- delayUntilRuntimeStage()
    |
    +-- [request]
            +-- trackDynamicDataInDynamicRender()
            +-- makeUntrackedHeaders() / makeUntrackedHeadersWithDevWarnings()
                    +-- [dev] instrumentHeadersPromiseWithDevWarnings()
                            +-- replaceableWarningDescriptor()
```

### データフロー図

```
[入力]                          [処理]                           [出力]

AsyncLocalStorage ------> headers()                      Promise<ReadonlyHeaders>
  (WorkStore,                |                                    |
   WorkUnitStore)            +-> コンテキスト判定                  |
                             +-> 種別分岐                         |
HTTPリクエストヘッダー         |   (prerender/request等)           |
  (RequestStore.headers) --> +-> ReadonlyHeaders生成 ----------->+
                             +-> Promiseキャッシュ               |
                             +-> [dev] 警告Proxy生成             |
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| headers.ts | `packages/next/src/server/request/headers.ts` | ソース | headers()関数本体 |
| headers.ts (adapter) | `packages/next/src/server/web/spec-extension/adapters/headers.ts` | ソース | HeadersAdapter/ReadonlyHeaders型定義 |
| headers.ts (api) | `packages/next/src/api/headers.ts` | ソース | 公開API再エクスポート |
| dynamic-rendering.ts | `packages/next/src/server/app-render/dynamic-rendering.ts` | ソース | postponeWithTracking等の動的レンダリング制御 |
| work-async-storage.external.ts | `packages/next/src/server/app-render/work-async-storage.external.ts` | ソース | WorkStore AsyncLocalStorage |
| work-unit-async-storage.external.ts | `packages/next/src/server/app-render/work-unit-async-storage.external.ts` | ソース | WorkUnitStore AsyncLocalStorage |
| dynamic-rendering-utils.ts | `packages/next/src/server/dynamic-rendering-utils.ts` | ソース | makeHangingPromise等のユーティリティ |
