# API設計書

## 概要

本ドキュメントは、Next.jsフレームワーク（packages/next）が提供するAPI基盤の設計書である。Next.jsはフルスタックReactフレームワークとして、2つのAPIルーティングシステム（Pages Router API Routes / App Router Route Handlers）と、フレームワーク内部API（画像最適化、静的再検証等）を提供する。本書では、これらのAPIの設計仕様、リクエスト/レスポンス形式、および内部動作について記述する。

## 共通仕様

### ベースURL

```
{デプロイ先ホスト}[/{basePath}]
```

- `basePath` は `next.config.js` の `basePath` 設定で指定される（デフォルト: なし）
- ローカル開発時: `http://localhost:3000`

### 認証方式

Next.jsフレームワーク自体には認証機構は組み込まれていない。認証はアプリケーション開発者がミドルウェアまたはAPI Routeハンドラー内で実装する。フレームワークが提供する認証関連機能は以下の通り。

- **Draft Mode（下書きモード）**: `__prerender_bypass` / `__next_preview_data` Cookieベースの認証
- **On-Demand Revalidation**: `x-prerender-revalidate` ヘッダーによるトークンベース認証

### 共通ヘッダー

| ヘッダー名 | 必須 | 説明 |
| --- | --- | --- |
| Content-Type | - | リクエストボディの形式（Pages API Routesでは自動パース対象） |
| x-prerender-revalidate | - | オンデマンド再検証用トークン |
| x-nextjs-data | - | Next.jsデータリクエスト識別用内部ヘッダー |
| x-middleware-rewrite | - | ミドルウェアによるリライト指示（内部使用） |
| x-middleware-next | - | ミドルウェアによる処理続行指示（内部使用） |

### 共通エラーレスポンス

| ステータスコード | 説明 |
| --- | --- |
| 400 | Bad Request - リクエストパラメータ不正（無効なJSON、無効なHTTPメソッド等） |
| 404 | Not Found - ルートが存在しない、またはリソース未検出 |
| 405 | Method Not Allowed - App Router Route Handlerで未実装のHTTPメソッドが呼ばれた場合 |
| 413 | Payload Too Large - リクエストボディがサイズ上限を超過（デフォルト: 1MB） |
| 500 | Internal Server Error - サーバー内部エラー |

---

## API一覧

| カテゴリ | エンドポイント | メソッド | 説明 |
| --- | --- | --- | --- |
| Pages API Routes | `/api/*` | 任意 | Pages Router配下のAPI Routes（`pages/api/`で定義） |
| App Route Handlers | `/任意パス` | GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS | App Router配下のRoute Handlers（`app/**/route.ts`で定義） |
| 画像最適化 | `/_next/image` | GET | Next.js組み込みの画像最適化API |
| 静的ファイル | `/_next/static/*` | GET | ビルド生成された静的アセットの配信 |
| データ取得 | `/_next/data/{buildId}/*.json` | GET | Pages RouterのgetServerSideProps/getStaticPropsデータ取得（内部） |
| HMR | `/_next/webpack-hmr` | GET (WebSocket) | 開発モードのHot Module Replacement（開発時のみ） |

---

## 各APIエンドポイント定義

### Pages Router API Routes

#### 1. Pages API Route Handler

Pages Router（`pages/api/`ディレクトリ）配下に配置されたAPIルートハンドラー。開発者が定義するAPIエンドポイントの基盤となる。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `{任意メソッド} /api/{ユーザー定義パス}` |
| 認証 | アプリケーション依存 |
| 権限 | アプリケーション依存 |
| ルーティング種別 | `PAGES_API`（RouteKind） |

**パスパラメータ**

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| (動的ルート) | string / string[] | - | `pages/api/[id].ts` のような動的ルートで自動抽出される |

**クエリパラメータ**

| パラメータ名 | 型 | 必須 | デフォルト | 説明 |
| --- | --- | --- | --- | --- |
| (任意) | string / string[] | - | - | URLクエリパラメータは `req.query` として取得可能 |

**リクエストボディ**

- Content-Typeに応じて自動パースされる（`config.api.bodyParser !== false` の場合）
- 対応形式: `application/json`, `application/ld+json`, `application/x-www-form-urlencoded`, `text/plain`
- サイズ上限: デフォルト `1mb`（`config.api.bodyParser.sizeLimit` で変更可能）

**リクエストオブジェクト（NextApiRequest）**

| プロパティ | 型 | 説明 |
| --- | --- | --- |
| query | `Partial<{ [key: string]: string \| string[] }>` | URLクエリパラメータ |
| cookies | `Partial<{ [key: string]: string }>` | リクエストCookie |
| body | `any` | パース済みリクエストボディ |
| env | `Env` | 環境変数 |
| draftMode | `boolean \| undefined` | 下書きモード状態 |
| preview | `boolean \| undefined` | プレビューモード状態（非推奨） |
| previewData | `PreviewData \| undefined` | プレビューデータ |

**レスポンスオブジェクト（NextApiResponse）**

| メソッド | シグネチャ | 説明 |
| --- | --- | --- |
| status | `(statusCode: number) => NextApiResponse` | HTTPステータスコードを設定 |
| send | `(body: any) => void` | レスポンスボディを送信（Content-Type自動判定） |
| json | `(data: any) => void` | JSONレスポンスを送信 |
| redirect | `(statusOrUrl: number \| string, url?: string) => NextApiResponse` | リダイレクトレスポンス（デフォルト: 307） |
| setDraftMode | `(options: { enable: boolean }) => NextApiResponse` | 下書きモードの有効/無効切替 |
| setPreviewData | `(data: object \| string, options?: { maxAge?: number, path?: string }) => NextApiResponse` | プレビューデータ設定 |
| clearPreviewData | `(options?: { path?: string }) => NextApiResponse` | プレビューデータクリア |
| revalidate | `(urlPath: string, opts?: { unstable_onlyGenerated?: boolean }) => Promise<void>` | オンデマンドISR再検証 |

**レスポンス（成功時）**

ステータスコード: アプリケーション定義による

```json
{
  "data": "アプリケーション定義による任意のレスポンス"
}
```

**レスポンス（エラー時）**

ステータスコード: `413 Payload Too Large`（ボディサイズ超過時）

```json
"Body exceeded 1mb limit"
```

ステータスコード: `400 Bad Request`（無効なJSON等）

```json
"Invalid JSON"
```

ステータスコード: `500 Internal Server Error`（サーバーエラー時）

```json
"Internal Server Error"
```

**ページ設定（PageConfig）**

```typescript
export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',     // リクエストボディサイズ上限
    },
    responseLimit: '4mb',    // レスポンスサイズ上限警告（デフォルト: 4MB）
    externalResolver: false, // 外部リゾルバ使用フラグ
  },
}
```

**レスポンスサイズ制限**

- デフォルト上限: 4MB（`RESPONSE_LIMIT_DEFAULT`）
- 上限超過時は警告ログが出力される（レスポンス自体はブロックされない）

---

### App Router Route Handlers

#### 2. App Route Handler

App Router（`app/`ディレクトリ）配下の`route.ts`ファイルで定義されるRoute Handler。Web標準のRequest/Response APIに基づく。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `{GET\|HEAD\|POST\|PUT\|DELETE\|PATCH\|OPTIONS} /{ユーザー定義パス}` |
| 認証 | アプリケーション依存 |
| 権限 | アプリケーション依存 |
| ルーティング種別 | `APP_ROUTE`（RouteKind） |

**サポートHTTPメソッド**

| メソッド | 自動実装 | 説明 |
| --- | --- | --- |
| GET | - | リソース取得（静的生成対象） |
| HEAD | あり | GETハンドラーが定義されている場合、自動的にGETで代替 |
| POST | - | リソース作成（非静的メソッド） |
| PUT | - | リソース更新（非静的メソッド） |
| DELETE | - | リソース削除（非静的メソッド） |
| PATCH | - | リソース部分更新（非静的メソッド） |
| OPTIONS | あり | 未定義の場合、実装済みメソッドに基づくAllowヘッダー付き204レスポンスを自動生成 |

**パスパラメータ**

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| (動的セグメント) | `Promise<Record<string, string \| string[] \| undefined>>` | - | `app/api/[id]/route.ts` のような動的セグメントから抽出（非同期） |

**ハンドラーシグネチャ**

```typescript
export async function GET(
  request: NextRequest,
  context: { params?: Promise<Record<string, string | string[] | undefined>> }
): Promise<Response> {
  // ハンドラー実装
  return new Response(JSON.stringify({ data: "example" }), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  })
}
```

**リクエストオブジェクト（NextRequest）**

Web標準の`Request`を拡張したクラス。

| プロパティ | 型 | 説明 |
| --- | --- | --- |
| cookies | `RequestCookies` | リクエストCookie |
| nextUrl | `NextURL` | パース済みURL（pathname, searchParams等を含む） |
| url | `string` | リクエストURL文字列 |
| method | `string` | HTTPメソッド |
| headers | `Headers` | リクエストヘッダー |
| body | `ReadableStream \| null` | リクエストボディストリーム |
| json() | `Promise<any>` | ボディをJSONとしてパース |
| text() | `Promise<string>` | ボディをテキストとして取得 |
| formData() | `Promise<FormData>` | ボディをFormDataとして取得 |
| arrayBuffer() | `Promise<ArrayBuffer>` | ボディをArrayBufferとして取得 |

**レスポンスオブジェクト（NextResponse）**

Web標準の`Response`を拡張したクラス。

| プロパティ/メソッド | 型 | 説明 |
| --- | --- | --- |
| cookies | `ResponseCookies` | レスポンスCookie操作 |
| NextResponse.json() | `static (body, init?) => NextResponse` | JSONレスポンス生成 |
| NextResponse.redirect() | `static (url, init?) => NextResponse` | リダイレクトレスポンス生成（301, 302, 303, 307, 308） |
| NextResponse.rewrite() | `static (destination, init?) => NextResponse` | ミドルウェア用リライトレスポンス生成 |
| NextResponse.next() | `static (init?) => NextResponse` | ミドルウェア用処理続行レスポンス生成 |

**レスポンス（成功時）**

ステータスコード: `200 OK`（アプリケーション定義）

```json
{
  "data": {
    "id": "1",
    "name": "example"
  }
}
```

**レスポンス（エラー時）**

ステータスコード: `405 Method Not Allowed`（未実装メソッド呼出時）

```
(空レスポンス)
```

ステータスコード: `400 Bad Request`（無効なHTTPメソッド）

```
(空レスポンス)
```

**動的レンダリング設定（Segment Config）**

```typescript
// route.ts でエクスポート可能な設定
export const dynamic = 'auto' | 'force-dynamic' | 'force-static' | 'error'
export const revalidate = false | 0 | number  // 秒数
export const fetchCache = 'auto' | 'default-cache' | 'only-cache' | 'force-cache' | 'default-no-store' | 'only-no-store' | 'force-no-store'
```

| 設定値 | 動作 |
| --- | --- |
| `dynamic = 'auto'` | 動的アクセスを自動検出し、必要に応じて動的レンダリング |
| `dynamic = 'force-dynamic'` | 常に動的レンダリング。リクエスト情報への全アクセスを許可 |
| `dynamic = 'force-static'` | 静的生成を強制。リクエスト情報は空スタブに置換 |
| `dynamic = 'error'` | 静的生成を強制し、動的APIアクセス時にエラーを発生 |

**非静的メソッドの動作**

POST, PUT, DELETE, PATCH, OPTIONSメソッドが定義されている場合:
- 静的生成時に `DynamicServerError` がスローされ、動的レンダリングにフォールバック
- これらのメソッドは常にリクエスト時に実行される

---

### フレームワーク内部API

#### 3. 画像最適化API

Next.js組み込みの画像最適化エンドポイント。`next/image`コンポーネントが内部的に使用する。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `GET /_next/image` |
| 認証 | 不要 |
| 権限 | なし |
| ルーティング種別 | `IMAGE`（RouteKind） |

**クエリパラメータ**

| パラメータ名 | 型 | 必須 | デフォルト | 説明 |
| --- | --- | --- | --- | --- |
| url | string | ○ | - | 最適化対象の画像URL（ローカルまたはリモート） |
| w | number | ○ | - | 出力画像の幅（`next.config.js` の `images.imageSizes` または `images.deviceSizes` で定義された値のいずれか） |
| q | number | ○ | - | 画像品質（1-100） |

**レスポンス（成功時）**

ステータスコード: `200 OK`

- Content-Type: `image/avif`, `image/webp`, `image/png`, `image/jpeg` 等（Acceptヘッダーとソース画像形式に基づく）
- Cache-Control: キャッシュ設定に基づく
- x-nextjs-cache: `HIT` | `MISS` | `STALE`（キャッシュ状態）
- ETag: 画像コンテンツのハッシュ

**対応画像形式**

| 入力形式 | 出力形式 | 備考 |
| --- | --- | --- |
| JPEG | JPEG, WebP, AVIF | Acceptヘッダーに基づく最適形式選択 |
| PNG | PNG, WebP, AVIF | 同上 |
| WebP | WebP, AVIF | 同上 |
| GIF | GIF | アニメーションGIFはバイパス |
| SVG | SVG | 最適化なし（バイパス） |
| ICO | ICO | 最適化なし（バイパス） |
| BMP | BMP | 最適化なし（バイパス） |

---

#### 4. オンデマンド再検証API

ISR（Incremental Static Regeneration）のオンデマンド再検証を実行する内部API。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | Pages API Route / App Route Handlerからプログラム的に呼び出し |
| 認証 | `x-prerender-revalidate` ヘッダーによるトークン認証 |
| 権限 | previewModeIdトークン一致が必要 |

**Pages API Route経由の再検証**

```typescript
// pages/api/revalidate.ts
export default async function handler(req, res) {
  await res.revalidate('/path-to-revalidate')
  return res.json({ revalidated: true })
}
```

**App Router経由の再検証関数**

```typescript
import { revalidatePath, revalidateTag } from 'next/cache'

// パス単位の再検証
revalidatePath('/blog/post-1')
revalidatePath('/blog/post-1', 'page')  // pageのみ
revalidatePath('/blog/post-1', 'layout') // layoutのみ

// タグ単位の再検証
revalidateTag('blog-posts', 'max')
```

**再検証関数一覧**

| 関数 | シグネチャ | 説明 |
| --- | --- | --- |
| revalidatePath | `(path: string, type?: 'layout' \| 'page') => void` | 指定パスのキャッシュを無効化 |
| revalidateTag | `(tag: string, profile: string \| CacheLifeConfig) => void` | 指定タグのキャッシュを無効化 |
| updateTag | `(tag: string) => void` | Server Action内でのタグ更新（read-your-own-writes対応） |
| refresh | `() => void` | Server Action内でのクライアントキャッシュリフレッシュ |

**制約事項**

- `revalidatePath` のパス長上限: `NEXT_CACHE_SOFT_TAG_MAX_LENGTH`
- `updateTag` はServer Actionからのみ呼び出し可能（Route Handlerからは不可）
- `refresh` はServer Actionからのみ呼び出し可能
- `use cache` スコープ内からの再検証関数呼び出しは禁止
- レンダリングフェーズでの呼び出しは禁止

---

#### 5. 静的アセット配信API

ビルドで生成された静的アセット（JavaScript, CSS, メディア等）を配信する内部API。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `GET /_next/static/{buildId}/{ファイルパス}` |
| 認証 | 不要 |
| 権限 | なし |

**レスポンス**

- Content-Type: ファイル拡張子に基づく適切なMIMEタイプ
- Cache-Control: `public, max-age=31536000, immutable`（ビルドハッシュ付きファイル）

---

#### 6. Pages Routerデータ取得API

Pages RouterのgetServerSideProps / getStaticPropsのデータをJSON形式で返す内部API。クライアントサイドナビゲーション時に使用される。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `GET /_next/data/{buildId}/{ページパス}.json` |
| 認証 | 不要（Draft Mode時は`__prerender_bypass` Cookie必要） |
| 権限 | なし |

**パスパラメータ**

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| buildId | string | ○ | アプリケーションのビルドID |
| ページパス | string | ○ | データ取得対象のページパス |

**レスポンス（成功時）**

ステータスコード: `200 OK`

```json
{
  "pageProps": {
    "data": "getServerSideProps/getStaticPropsの戻り値"
  },
  "__N_SSP": true
}
```

---

### サーバー初期化API

#### 7. Router Server 初期化

Next.jsサーバーの初期化エンドポイント。アプリケーション起動時に呼び出される内部API。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| 関数 | `initialize(opts)` |
| 所在 | `packages/next/src/server/lib/router-server.ts` |

**初期化オプション**

| パラメータ名 | 型 | 必須 | デフォルト | 説明 |
| --- | --- | --- | --- | --- |
| dir | string | ○ | - | プロジェクトディレクトリ |
| port | number | ○ | - | リスンポート |
| dev | boolean | ○ | - | 開発モードフラグ |
| hostname | string | - | - | リスンホスト名 |
| minimalMode | boolean | - | false | 最小モード（サーバーレス環境用） |
| customServer | boolean | - | false | カスタムサーバー使用フラグ |
| keepAliveTimeout | number | - | - | Keep-Aliveタイムアウト（ミリ秒） |
| quiet | boolean | - | false | ログ抑制フラグ |

---

## Draft Mode（下書きモード）

### Cookie仕様

| Cookie名 | 説明 |
| --- | --- |
| `__prerender_bypass` | Draft Modeの有効/無効を制御するセッションCookie |
| `__next_preview_data` | JWTエンコードされたプレビューデータ（上限: 2KB） |

### Cookie属性

| 属性 | 開発環境 | 本番環境 |
| --- | --- | --- |
| httpOnly | true | true |
| sameSite | lax | none |
| secure | false | true |
| path | / | / |

### プレビューデータの暗号化

- アルゴリズム: HS256（JWT署名）
- 暗号化: AES（`previewModeEncryptionKey`による対称暗号化）
- データサイズ上限: 2KB（JWTペイロード全体）

---

## 備考

### Next.jsのAPIアーキテクチャ

Next.jsは「フレームワーク」であるため、通常のWebアプリケーションとは異なり、APIエンドポイントはアプリケーション開発者が定義する。本設計書ではフレームワークが提供するAPI基盤（ルーティング、リクエスト/レスポンス処理、内部API）を記述している。

### Pages API Routes vs App Route Handlers

| 観点 | Pages API Routes | App Route Handlers |
| --- | --- | --- |
| ディレクトリ | `pages/api/` | `app/**/route.ts` |
| API仕様 | Node.js `IncomingMessage` / `ServerResponse` ベース | Web標準 `Request` / `Response` ベース |
| ハンドラー形式 | デフォルトエクスポート関数 | HTTPメソッド名のnamed export |
| ボディパース | 自動（設定で無効化可能） | 手動（`request.json()` 等） |
| 静的生成 | 非対応 | GET メソッドで対応 |
| Edge Runtime | 対応（config指定） | 対応（config指定） |
| ルーティング種別 | `PAGES_API` | `APP_ROUTE` |

### HTTPメソッドの制約

- App Route Handlersではメソッド名は**大文字**でエクスポートする必要がある（小文字はエラー）
- `default` エクスポートは App Route Handlers では非サポート
- `output: 'export'` 設定時、`dynamic = 'force-dynamic'` は使用不可

### レスポンス制約（App Route Handlers）

- ハンドラーは必ず `Response` または `NextResponse` インスタンスを返す必要がある
- `NextResponse.rewrite()` はApp Route Handlerでは使用不可（ミドルウェア専用）
- `NextResponse.next()` はApp Route Handlerでは使用不可（ミドルウェア専用）
