# 機能設計書 71-Middleware

## 概要

本ドキュメントは、Next.jsのMiddleware機能の設計について記述する。Middlewareは、リクエストが完了する前にEdge Runtimeベースで実行される中間処理であり、リライト・リダイレクト・ヘッダー操作・レスポンス生成を行う。

### 本機能の処理概要

Middlewareは、すべての受信リクエストに対してルーティング処理の前段で実行されるインターセプト層である。プロジェクトルートまたは`src/`ディレクトリの`middleware.ts`（または`middleware.js`）ファイルに定義される単一のエクスポート関数として動作する。

**業務上の目的・背景**：Webアプリケーションでは、認証チェック・地域別リダイレクト・A/Bテスト・ボット検出・ヘッダー操作など、ルーティング前に共通的に実行すべき処理が多数存在する。MiddlewareはこれらをEdge Runtime上で低レイテンシに実行することで、エンドユーザーへの応答速度を損なわずに横断的関心事を処理する仕組みを提供する。

**機能の利用シーン**：認証・認可によるアクセス制御、国際化対応のロケールリダイレクト、URLリライトによるルーティングカスタマイズ、リクエスト/レスポンスヘッダーの付与・変更、特定パスへのアクセスログ記録、レート制限の実装など。

**主要な処理内容**：
1. `middleware.ts`のデフォルトエクスポート関数がリクエストごとに呼び出される
2. `NextRequest`オブジェクトと`NextFetchEvent`オブジェクトが引数として渡される
3. `NextResponse.redirect()`によるリダイレクト、`NextResponse.rewrite()`によるリライト、`NextResponse.next()`による後続処理への引き渡しを返す
4. `config.matcher`によるパスマッチングでMiddleware適用範囲を制御する
5. Edge Runtimeサンドボックス内で実行され、Node.js APIの一部は利用不可

**関連システム・外部連携**：Edge Runtime（CDNエッジ環境）、Router Server（リクエストルーティング）、Incremental Cache（キャッシュ管理）、OpenTelemetryトレーシング

**権限による制御**：Middleware自体は全リクエストに対して実行されるが、Middleware内のロジックで認証状態に基づくリダイレクトやアクセス制御を実装することが一般的なパターンである。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | Middlewareは画面に直接関連しない。全リクエストの前段処理として動作する |

## 機能種別

リクエスト前処理 / ルーティング制御 / アクセス制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| request | NextRequest | Yes | 受信HTTPリクエストを表すオブジェクト。URL、ヘッダー、Cookie等を含む | URLはvalidateURL()で検証される |
| event | NextFetchEvent | Yes | FetchEventの拡張。waitUntilメソッドを提供する | - |

### 入力データソース

- HTTPリクエスト（クライアントからの全リクエスト）
- `config.matcher`によるパスフィルタリング設定（middleware.tsファイル内で定義）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| response | NextResponse / Response / null / undefined / void | Middlewareの処理結果。リダイレクト・リライト・レスポンスヘッダーの設定等 |
| waitUntil | Promise<any> | レスポンス送信後に完了を待つ非同期処理 |
| fetchMetrics | FetchMetrics | fetch呼び出しのメトリクス情報 |

### 出力先

- HTTPレスポンス（クライアントへのレスポンス）
- 内部ヘッダー（`x-middleware-rewrite`, `x-middleware-next`, `x-middleware-set-cookie`, `Location`等）を通じてRouter Serverに処理指示を伝達

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ Router Serverがリクエストを受け取り、Middlewareの実行を開始
2. adapter()関数の実行（adapter.ts）
   └─ テストAPIインターセプト、Instrumentation登録の確認
3. URL正規化
   └─ RSC URLの正規化、クエリパラメータの正規化、ビルドIDの処理
4. NextRequestHintオブジェクトの生成
   └─ 内部検索パラメータを除去したURLでリクエストオブジェクトを構築
5. NextFetchEventの生成
   └─ waitUntilコンテキストを設定
6. Middlewareハンドラの実行
   └─ workAsyncStorage/workUnitAsyncStorageのコンテキスト内で実行
   └─ OpenTelemetryトレーシングでラップ
7. レスポンスの後処理
   └─ リライトURL・リダイレクトURLの正規化
   └─ RSCリクエスト用ヘッダーの設定
   └─ Flight headersの復元
8. FetchEventResultの返却
   └─ response, waitUntil, fetchMetricsを返す
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[adapter関数呼び出し]
    B --> C[URL正規化・クエリパラメータ処理]
    C --> D[NextRequestHint生成]
    D --> E[NextFetchEvent生成]
    E --> F{isMiddleware?}
    F -->|Yes| G[AsyncStorage コンテキスト構築]
    G --> H[OpenTelemetry トレース開始]
    H --> I[Middlewareハンドラ実行]
    F -->|No| I
    I --> J{レスポンス種別判定}
    J -->|rewrite| K[リライトURL正規化・ヘッダー設定]
    J -->|redirect| L[リダイレクトURL正規化・Locationヘッダー設定]
    J -->|next| M[x-middleware-nextヘッダー設定]
    J -->|null/void| N[NextResponse.next をデフォルト生成]
    K --> O[Flight headers復元]
    L --> O
    M --> O
    N --> O
    O --> P[FetchEventResult返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-71-01 | 単一Middleware | プロジェクトに定義できるMiddlewareは1つのみ（ルートまたはsrc/配下） | 常時 |
| BR-71-02 | Edge Runtime制約 | MiddlewareはEdge Runtimeで実行され、Node.js固有APIは使用不可 | 常時 |
| BR-71-03 | リダイレクトステータス制限 | リダイレクトに使用できるHTTPステータスコードは301, 302, 303, 307, 308のみ | NextResponse.redirect()使用時 |
| BR-71-04 | パスマッチング | config.matcherで指定したパスにのみMiddlewareが適用される | matcher設定時 |
| BR-71-05 | Flight headers保護 | RSC関連のFlight headersはMiddlewareで上書き・削除不可 | RSCリクエスト時 |

### 計算ロジック

特になし。

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

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

Middlewareは直接的なデータベース操作を行わない。IncrementalCacheを通じてキャッシュの読み書きを行う場合がある。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| TypeError | 型エラー | handler が Response インスタンスでないオブジェクトを返した場合 | Responseオブジェクトを返すよう修正 |
| RangeError | 範囲エラー | 無効なリダイレクトステータスコードを指定した場合 | 301/302/303/307/308を使用 |
| PageSignatureError | シグネチャエラー | 非推奨API（request, respondWith, waitUntil）を直接呼び出した場合 | 新しいAPI形式に移行 |
| Error | URL不正 | 不正なURL文字列が渡された場合 | 絶対URLを使用 |

### リトライ仕様

Middleware自体にリトライ機構はない。エラー発生時はエラーレスポンスが返却される。

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

該当なし。Middlewareはステートレスな処理である。

## パフォーマンス要件

- Edge Runtimeでの実行により低レイテンシを実現（ターゲット: 数ミリ秒以内）
- Middlewareはストリーミング不可（レスポンスはハンドラの完了後に一括送信）
- IncrementalCacheの共有インスタンス利用によりキャッシュオーバーヘッドを削減

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

- Edge Runtimeサンドボックスにより、Middlewareコードはファイルシステムやプロセスへの直接アクセスが制限される
- `x-middleware-*` ヘッダーは内部通信用であり、外部からの操作を許可しない
- クロスサイトアクセスブロック機構との連携あり

## 備考

- `NextMiddleware`型は`NextProxy`に名称変更が予定されている（middleware.tsはproxy.tsへ）
- `config`エクスポートにより、matcher、regions等のMiddleware設定を定義可能
- 開発モードではエラーに`edge-server`タグが付与され、Dev Overlayでの表示に対応

---

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

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

### 推奨読解順序

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

まず、Middlewareの入出力に関わる型定義を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/next/src/server/web/types.ts` | RequestData, FetchEventResult, NextMiddleware, NextMiddlewareResult型の定義を確認。MiddlewareがNextRequest/NextFetchEventを受け取りResponse系を返す構造を理解する |
| 1-2 | request.ts | `packages/next/src/server/web/spec-extension/request.ts` | NextRequestクラスの内部構造（INTERNALS Symbol、cookies/nextUrl/urlプロパティ）を確認 |
| 1-3 | response.ts | `packages/next/src/server/web/spec-extension/response.ts` | NextResponseクラスの静的メソッド（json/redirect/rewrite/next）とcookiesプロキシの仕組みを確認 |

**読解のコツ**: `INTERNALS` Symbolはクラスの内部状態を外部から直接アクセスさせないためのパターン。TypeScriptの`Symbol('...')`を使ってprivate-likeなプロパティを実現している。

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

Middlewareの実行起点となるアダプタ関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | adapter.ts | `packages/next/src/server/web/adapter.ts` | adapter()関数がMiddlewareの実行全体を制御する中核関数であることを理解する |

**主要処理フロー**:
1. **109-113行目**: adapter()関数のエントリーポイント。テストAPIインターセプト、Instrumentation登録を実行
2. **119-149行目**: URL正規化処理。RSC URL、クエリパラメータ、ビルドIDの処理
3. **151-170行目**: リクエストヘッダー処理。Flight headersの一時保存と除去
4. **178-189行目**: NextRequestHintの生成。内部検索パラメータを除去したURLで構築
5. **242-341行目**: Middlewareハンドラの実行。isMiddleware判定後、AsyncStorageコンテキスト内でOpenTelemetryトレース付きで実行
6. **358-482行目**: レスポンス後処理。リライト・リダイレクトURLの正規化、RSCヘッダーの設定
7. **484-509行目**: Flight headers復元とFetchEventResult返却

#### Step 3: サンドボックス実行環境を理解する

Edge Runtimeサンドボックスでの実行メカニズムを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sandbox.ts | `packages/next/src/server/web/sandbox/sandbox.ts` | run()関数がEdge Runtimeコンテキストを生成し、Middlewareのエッジ関数を実行する仕組みを理解する |

**主要処理フロー**:
- **105-157行目**: run()関数。Edge Runtimeコンテキストの取得、エッジ関数の実行、禁止ヘッダーの除去を行う
- **126-150行目**: edgeSandboxNextRequestContext内でのリクエスト処理実行

#### Step 4: イベント・ライフサイクルを理解する

NextFetchEventとwaitUntilの仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | fetch-event.ts | `packages/next/src/server/web/spec-extension/fetch-event.ts` | FetchEvent/NextFetchEventクラスのwaitUntil実装（internal/externalの2種類）を理解する |

**主要処理フロー**:
- **9-49行目**: FetchEventクラス。waitUntilSymbolによるPromise管理の仕組み
- **59-92行目**: NextFetchEventクラス。非推奨メソッドのエラースロー

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

```
Router Server (router-server.ts)
    |
    +-- sandbox/run() [sandbox.ts:105]
    |       |
    |       +-- getRuntimeContext() [sandbox.ts:71]
    |       |       +-- getModuleContext() [context.ts]
    |       |
    |       +-- edgeFunction() [adapter.ts:adapter()]
    |               |
    |               +-- ensureTestApisIntercepted() [adapter.ts:96]
    |               +-- ensureInstrumentationRegistered() [globals.ts]
    |               +-- NextRequestHint() [adapter.ts:40]
    |               +-- NextFetchEvent() [fetch-event.ts:59]
    |               +-- propagator() [adapter.ts:86]
    |               |       +-- getTracer().trace() [tracer.ts]
    |               |       +-- createRequestStoreForAPI() [request-store.ts]
    |               |       +-- createWorkStore() [work-store.ts]
    |               |       +-- workAsyncStorage.run() -> workUnitAsyncStorage.run()
    |               |               +-- params.handler(request, event) // ユーザーのMiddleware関数
    |               |
    |               +-- [レスポンス後処理]
    |                       +-- NextURL() [リライトURL正規化]
    |                       +-- getRelativeURL() [リダイレクトURL正規化]
```

### データフロー図

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

HTTPリクエスト -------> Router Server                        FetchEventResult
  (headers,            |                                      (response,
   method,             +-> sandbox/run()                       waitUntil,
   url,                    |                                   fetchMetrics)
   body)                   +-> adapter()                          |
                               |                                  |
                               +-> URL正規化                      |
                               +-> NextRequestHint生成             |
                               +-> NextFetchEvent生成              |
                               +-> AsyncStorage構築                |
                               +-> ユーザーMiddleware実行  ------->+
                               +-> レスポンス後処理                 |
                                   (rewrite/redirect/next         |
                                    URL正規化、ヘッダー設定)  ---->+
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| adapter.ts | `packages/next/src/server/web/adapter.ts` | ソース | Middleware実行のメインアダプタ |
| types.ts | `packages/next/src/server/web/types.ts` | ソース | Middleware関連の型定義 |
| request.ts | `packages/next/src/server/web/spec-extension/request.ts` | ソース | NextRequestクラス定義 |
| response.ts | `packages/next/src/server/web/spec-extension/response.ts` | ソース | NextResponseクラス定義 |
| fetch-event.ts | `packages/next/src/server/web/spec-extension/fetch-event.ts` | ソース | NextFetchEventクラス定義 |
| sandbox.ts | `packages/next/src/server/web/sandbox/sandbox.ts` | ソース | Edge Runtimeサンドボックス実行 |
| context.ts | `packages/next/src/server/web/sandbox/context.ts` | ソース | モジュールコンテキスト管理 |
| next-url.ts | `packages/next/src/server/web/next-url.ts` | ソース | NextURL クラス（URL拡張） |
| utils.ts | `packages/next/src/server/web/utils.ts` | ソース | ヘッダー変換・URL検証ユーティリティ |
| error.ts | `packages/next/src/server/web/error.ts` | ソース | Middleware固有エラークラス |
| router-server.ts | `packages/next/src/server/lib/router-server.ts` | ソース | Middleware呼び出し元のRouter Server |
