# Lambdaで本番HTTP APIを作る：API Gateway（REST/HTTP API）・Function URLs・ALBの選定と認証・検証・エラー設計

> AWS LambdaでHTTP/REST APIを本番品質で作るための実装ガイド。API Gateway REST API・HTTP API・Lambda Function URLs・ALBの4つの入口を機能/料金/レイテンシで選定し、ペイロード形式2.0のレスポンス推論、JWT/Lambda/IAMオーソライザー、リクエスト検証、CORS、エラーマッピング、スロットリングまでAWS公式仕様に忠実な実コードで解説します。

- 公開日: 2026-06-27
- 著者: 友田 陽大
- タグ: AWS, Lambda, API Gateway, サーバーレス, アーキテクチャ設計
- URL: https://tomodahinata.com/blog/aws-lambda-api-gateway-function-urls-rest-api-production-guide

## 要点

- Lambdaの前段は4択：HTTP API（安い・速い・JWT内蔵）/ REST API（検証・APIキー・WAF・X-Ray）/ Function URLs（最小構成・プロトタイプ）/ ALB（既存ALBに相乗り）
- HTTP APIはREST APIより低コスト（$1.00 vs $3.50/100万）かつ低レイテンシ。WAF・リクエスト検証・APIキー・X-Rayが要るときだけREST API
- ペイロード形式2.0は、関数がstatusCode無しの有効なJSONを返すと200・application/jsonを自動推論。1.0は{statusCode, headers, body}の明示が必須
- 認証はUIではなく入口で強制：HTTP APIのJWTオーソライザー（iss/aud検証・Cognito対応）かLambdaオーソライザー、サービス間はIAM(SigV4)
- 統合タイムアウトはREST 29秒（Regional/Privateは引き上げ可）・HTTP API 30秒固定。API Gatewayの最大ペイロード10MBに対しLambda同期は6MB

---

「Lambda でAPIを作りたい。でも前段に API Gateway を置くのか、Function URL で十分なのか、そもそも REST API と HTTP API は何が違うのか」——サーバーレスでHTTP APIを本番に出すとき、最初に必ず立ち止まる分岐です。ここを間違えると、**払わなくていい料金を払い続ける**か、**後から必要になった機能（WAF・リクエスト検証・APIキー）が足りずに作り直す**ことになります。

この記事は、AWS Lambda で**本番品質のHTTP/REST API**を作るための実装ガイドです。**4つの入口の選定**から、ペイロード形式・認証・検証・CORS・エラー設計・スロットリングまでを一気通貫で解説します。題材として、経済産業大臣賞を受賞した木材流通B2B SaaS（[木材流通業界のDX](/case-studies/lumber-industry-dx)）での `API Gateway → … → 221本のAPIエンドポイント` という本番運用の判断も交えます。Lambda 本体の実行モデル・冪等性・可観測性は姉妹記事 [AWS Lambda 本番運用ガイド](/blog/aws-lambda-production-guide) に委ね、本稿は**「HTTPの入口」一点**に集中します。

> **この記事のルール**：仕様・パラメータ名・料金・クォータは **AWS 公式ドキュメント（2026年6月時点）** に基づきます。料金はリージョン・時期で変わり（本稿はUS East (N. Virginia)・標準ティアの公示値）、クォータも改定されます。本番投入前に必ず公式（末尾「参考」）で最新値を確認してください。

---

## 0. メンタルモデル：HTTPの入口は「機能 ↔ コスト」のトレードオフ

Lambda の前段にHTTPを通す方法は4つ。**機能が多いほど高く、シンプルなほど安い**という一本の軸で並びます。

- **HTTP API（API Gateway v2）**：安い・速い・JWTオーソライザー内蔵。**最小機能で大半のAPIを賄う本命**。
- **REST API（API Gateway v1）**：リクエスト検証・APIキー/使用量プラン・WAF・プライベートエンドポイント・X-Ray・キャッシュ・ゲートウェイレスポンス。**「足りない機能」が要るときだけ**。
- **Function URLs**：API Gateway を挟まない関数直結のHTTPSエンドポイント。**プロトタイプ・社内ツール・単純なWebhook受け**に。
- **ALB + Lambda ターゲット**：すでにALBを運用していて、パス/ヘッダで一部をLambdaに振りたいとき。

> **公式の指針**：AWS は Function URLs を「**シンプルなアプリやプロトタイプ**でコストと複雑さを最小化したいとき」、API Gateway を「**本番のスケールするアプリ**」に推奨しています。迷ったら **HTTP API が既定の正解**、と覚えておけば大きく外しません。

---

## 1. 4つの入口を選ぶ：機能・料金・レイテンシの早見表

まず選定表で固定します。これが本記事の意思決定の核です。

| 観点 | HTTP API | REST API | Function URLs | ALB + Lambda |
| --- | --- | --- | --- | --- |
| **料金（100万リクエスト）** | $1.00（〜3億）/ 以降$0.90 | $3.50〜（最上位$1.51） | 追加料金なし（Lambda課金のみ） | ALB時間/LCU課金 |
| **レイテンシ** | 低（REST比 最大60%減） | 標準 | 最小（直結） | 低 |
| **JWTオーソライザー** | ✅ 内蔵 | ❌（Cognitoオーソライザーで代替） | ❌（IAM or 公開） | ❌（ALBのOIDC認証） |
| **リクエスト検証** | ❌ | ✅ | ❌ | ❌ |
| **APIキー / 使用量プラン** | ❌ | ✅ | ❌ | ❌ |
| **WAF** | ❌ | ✅ | ❌（CloudFront併用） | ✅ |
| **X-Rayトレース** | ❌ | ✅ | （Lambda側のみ） | （Lambda側のみ） |
| **プライベート/エッジ最適化** | ❌ | ✅ | ❌ | VPC内 |

選定ロジックはシンプルです。

1. **WAF・リクエスト検証・APIキー（使用量プラン）・X-Ray・プライベートAPIのどれかが要る？** → **REST API**。
2. **要らない（大半のAPIはこちら）？** → **HTTP API**。安くて速い。
3. **そもそもAPI Gatewayの機能が要らない単純な入口？** → **Function URLs**。
4. **既存のALB配下に相乗りさせたい？** → **ALB + Lambda**。

> **REST APIの料金は HTTP API の3倍以上**（$3.50 vs $1.00 / 100万・第1ティア）。「とりあえずREST API」で始めて、WAFも検証もAPIキーも使っていない——というのは**最も多い無駄**です。使う機能から逆算して選びます。

---

## 2. プロキシ統合とペイロード形式：1.0 と 2.0 の決定的な違い

API Gateway は**Lambdaプロキシ統合**で、リクエスト全体を `event` に詰めて関数に渡し、関数の戻り値をレスポンスにします。ここで HTTP API の**ペイロード形式（payload format version）**を理解していないと、ハマります。

### 2.1 形式2.0の「レスポンス自動推論」

公式の重要仕様：**形式2.0では、関数が `statusCode` を含まない有効なJSONを返すと、API Gateway が `statusCode: 200`・`content-type: application/json`・`isBase64Encoded: false`・`body: 戻り値` を自動で推論**します。つまり**ハンドラがオブジェクトをそのまま返すだけ**でよくなります。

```ts
// HTTP API（ペイロード形式 2.0）：オブジェクトを返すだけで 200 application/json になる
import type { APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2 } from "aws-lambda";

export const handler = async (
  event: APIGatewayProxyEventV2,
): Promise<APIGatewayProxyStructuredResultV2 | { ok: boolean; path: string }> => {
  // 2.0 では method/path は requestContext.http に入る（1.0 の httpMethod/path とは別物）
  const { method, path } = event.requestContext.http;
  if (method !== "GET") {
    // 明示したいときは従来どおり statusCode を返せる（推論より優先される）
    return { statusCode: 405, body: JSON.stringify({ message: "Method Not Allowed" }) };
  }
  // statusCode を省略 → 200 / application/json に自動推論される
  return { ok: true, path };
};
```

一方、**形式1.0（REST APIや、明示的に1.0を選んだHTTP API）では、必ず `{ statusCode, headers, body }` の封筒を返す**必要があります。封筒が壊れていると API Gateway は **502 Bad Gateway** を返します。

| 項目 | 形式 1.0 | 形式 2.0 |
| --- | --- | --- |
| メソッド/パス | `event.httpMethod` / `event.path` | `event.requestContext.http.method` / `.path` |
| 複数値ヘッダ | `multiValueHeaders` あり | 廃止（カンマ結合して `headers` へ） |
| Cookie | なし | `event.cookies` 配列（レスポンスも `cookies`） |
| レスポンス | `{statusCode, headers, body}` 必須 | 省略時は200/JSONを推論 |

> **設計の勘所**：新規のHTTP APIは**形式2.0が既定**（コンソールは最新版を既定選択）。ただし**CLI/CloudFormation/SDK で作るときは `payloadFormatVersion` を明示**しないと意図とズレることがあります。型は `@types/aws-lambda` の `APIGatewayProxyEventV2`（2.0）/ `APIGatewayProxyEvent`（1.0）を**取り違えない**こと——これが地味に多いバグの源です。

---

## 3. 認証・認可：UIではなく「入口」で強制する

認証は「クライアントを信じない」が出発点。**UIのif文ではなく、APIの入口（オーソライザー）で弾く**のが鉄則です。入口ごとに使える仕組みが違います。

### 3.1 JWTオーソライザー（HTTP API限定・最有力）

HTTP API には **JWTオーソライザーが内蔵**されており、OAuth2/OIDC のJWT（例：Cognito ユーザープール発行）を、**署名・発行者（`iss`）・オーディエンス（`aud`）**まで検証します。**コードを1行も書かずに**認証を入口で強制できる——これがHTTP APIを選ぶ最大の理由のひとつです。

```hcl
# Terraform: HTTP API の JWT オーソライザー（Cognito ユーザープールを発行者に）
resource "aws_apigatewayv2_authorizer" "jwt" {
  api_id           = aws_apigatewayv2_api.http.id
  authorizer_type  = "JWT"
  identity_sources = ["$request.header.Authorization"]
  name             = "cognito-jwt"

  jwt_configuration {
    audience = [aws_cognito_user_pool_client.app.id]                                  # aud を検証
    issuer   = "https://cognito-idp.${var.region}.amazonaws.com/${aws_cognito_user_pool.main.id}" # iss を検証
  }
}
```

JWT検証の内部（JWKSの取得・RS256・kid・有効期限）を自前で実装する場合の落とし穴は、姉妹記事 [Cognito JWT(RS256)検証の正しい実装](/blog/aws-cognito-jwt-rs256-verification-jwks-security-guide) に詳しくまとめています。

### 3.2 そのほかのオーソライザー

| 仕組み | 使える入口 | 用途 |
| --- | --- | --- |
| **JWTオーソライザー** | HTTP API | OIDC/OAuth2のJWT。最小コストで認証強制 |
| **Lambdaオーソライザー** | REST / HTTP API | 独自トークン・複雑な認可ロジック。REST=TOKEN/REQUEST、HTTP=REQUEST |
| **Cognitoオーソライザー** | REST API | Cognitoユーザープール（`COGNITO_USER_POOLS`） |
| **IAM認可（SigV4）** | REST / HTTP API / Function URL | サービス間・社内クライアント。`execute-api` 権限で制御 |

**Lambdaオーソライザー**は結果をキャッシュできます（REST APIの既定TTLは**300秒**、最大3600秒、`0`で無効）。認証のたびに重い検証を走らせないために重要なパラメータです。**サービス間通信**なら、トークンより **IAM認可（SigV4署名）**が安全です——鍵の管理が不要で、最小権限をIAMで表現できます。

---

## 4. リクエスト検証：境界で弾く（REST APIの強み）

**外部入力はすべて境界で検証する**——これはセキュリティの基本です。REST API は**モデル（JSON Schema）とリクエストバリデータ**で、関数に到達する前に不正なボディ・必須パラメータ欠落を**400で弾け**ます。Lambdaの実行時間を1msも使わずに不正を落とせるので、コストにも効きます。

HTTP API にはこの機能が無いため、**ハンドラ内で検証**します。型安全に弾くなら `zod` 等のスキーマを境界に置きます。

```ts
// HTTP API：リクエスト検証は関数の入口で。境界でparse→失敗は422で即返す。
import { z } from "zod";
import type { APIGatewayProxyEventV2 } from "aws-lambda";

const CreateOrder = z.object({
  itemId: z.string().min(1),
  quantity: z.number().int().positive(),         // 正の整数のみ許可
  note: z.string().max(500).optional(),
});

export const handler = async (event: APIGatewayProxyEventV2) => {
  const parsed = CreateOrder.safeParse(JSON.parse(event.body ?? "{}"));
  if (!parsed.success) {
    // 何が不正かをクライアントに返す（PIIは載せない）
    return { statusCode: 422, body: JSON.stringify({ errors: parsed.error.flatten() }) };
  }
  return await createOrder(parsed.data); // 以降は型安全な data を扱う
};
```

> **DRYの勘所**：REST API のモデルと、フロント/関数の型を**二重管理しない**。JSON Schema か Zod のどちらかを単一の真実源にし、もう一方を生成するのが理想です。検証ロジックを「入口（API Gateway）」と「関数」の両方にコピペすると、必ずズレます。

---

## 5. 制限とタイムアウト：知らないと本番で詰まる数字

API Gateway と Lambda の**制限の食い違い**は、本番障害の典型原因です。表で固定します。

| 項目 | 値（公式） | 注意 |
| --- | --- | --- |
| **統合タイムアウト（REST）** | 50ms〜**29秒** | Regional/Private は引き上げ可（上限は非公開・要申請）。エッジ最適化は29秒固定 |
| **統合タイムアウト（HTTP API）** | 最大**30秒**（固定） | 引き上げ不可 |
| **最大ペイロード（API Gateway）** | **10 MB** | 引き上げ不可 |
| **Lambda同期レスポンス** | **6 MB** | API Gatewayの10MBより小さい。大きい応答は要分割/署名付きURL |
| **既定スロットル** | 10,000 RPS + バースト5,000 | 一部13リージョンは 2,500 / 1,250。HTTP/REST/WebSocketで共有 |
| **Function URLスロットル** | 予約済み同時実行 × 10 RPS | 超過は429。`予約=0`で全停止（キルスイッチ） |

実務での効き方：

- **29秒/30秒の壁**：重い同期処理（レポート生成・外部API集約）は、この壁に当たる前に**非同期化**する。202を即返して結果はポーリング/Webソケット、あるいはStep Functions。長時間処理がそもそも要るなら [Fargate vs Lambda の選定](/blog/aws-ecs-fargate-vs-lambda-vs-app-runner-compute-selection-guide) を見直す。
- **6MB vs 10MB**：Lambdaは6MBが上限。大きいレスポンスは**S3署名付きURLを返す**設計にする（Lambdaレスポンスストリーミングなら200MBまで可だがFunction URL/専用API経由）。
- **スロットル**：使用量プランのスロットル/クォータは公式が「**コスト制御やアクセス遮断に依存するな**（ハード制限ではない）」と明言。**コスト上限の砦は予約済み同時実行とWAFのレート制限**で作る。

---

## 6. CORS とエラー設計：ブラウザと運用に優しく

### 6.1 CORS は「入口」で一元管理する

- **HTTP API**：CORSは**API単位で設定**し、プリフライト（OPTIONS）は**API Gatewayが自動応答**。**バックエンドが返すCORSヘッダは無視**されます。だから関数側でCORSヘッダを足しても無駄——設定で一元管理します。
- **REST API（プロキシ統合）**：**バックエンド（関数）が `Access-Control-Allow-*` を返す**必要がある。非プロキシならOPTIONSをモックで返す。
- **Function URL**：組み込みのCORS設定（`AllowOrigins` 等）を使う。手書きヘッダより設定を推奨。

```hcl
# HTTP API：CORSはAPI単位で宣言。ワイルドカード '*' を本番で使わず、許可originを列挙する
resource "aws_apigatewayv2_api" "http" {
  name          = "orders-api"
  protocol_type = "HTTP"
  cors_configuration {
    allow_origins = ["https://app.example.com"]   # 信頼するoriginだけ
    allow_methods = ["GET", "POST"]
    allow_headers = ["authorization", "content-type"]
    max_age       = 600
  }
}
```

### 6.2 エラーは正しいステータスで、情報を漏らさず

- **REST API**：API Gateway自体が生む4xx/5xx（認可拒否→403、統合タイムアウト→504、スロットル→429）は**ゲートウェイレスポンス**でカスタマイズ可（REST API限定）。
- **Lambdaエラーのマッピング**：プロキシ統合なら**関数が `statusCode` を返す**（4xx=クライアント、5xx=サーバー）。非プロキシは `selectionPattern` の正規表現でマッピングしないと、**関数が例外を投げても既定200**になってしまう。
- **情報漏洩を防ぐ**：スタックトレースや内部メッセージをそのままbodyに載せない。クライアントには相関ID（`context.awsRequestId`）だけ返し、詳細は構造化ログへ（[可観測性は本番運用ガイド参照](/blog/aws-lambda-production-guide)）。

```ts
// 例外を「クライアント向けの安全なレスポンス」に翻訳する薄い層
class HttpError extends Error {
  constructor(readonly status: number, readonly publicMessage: string) { super(publicMessage); }
}

export const handler = async (event: APIGatewayProxyEventV2) => {
  try {
    return await route(event);
  } catch (err) {
    if (err instanceof HttpError) {
      return { statusCode: err.status, body: JSON.stringify({ message: err.publicMessage }) };
    }
    // 想定外は500。内部詳細はログにだけ出し、bodyには相関IDのみ
    console.error(JSON.stringify({ level: "ERROR", requestId: event.requestContext.requestId, err: `${err}` }));
    return { statusCode: 500, body: JSON.stringify({ message: "Internal Server Error" }) };
  }
};
```

---

## 7. 可観測性：HTTP APIとREST APIの違いに注意

「APIが遅い・落ちている」を追うために、入口のメトリクスとログを押さえます。ここでも**HTTP APIとREST APIで使える機能が違う**点に注意。

- **メトリクス（共通）**：`AWS/ApiGateway` 名前空間の `Count` / `Latency` / `IntegrationLatency` / `4XXError` / `5XXError`。**`Latency`（全体）と `IntegrationLatency`（Lambda側）の差**が、API Gateway自身のオーバーヘッドです。
- **ログ**：REST APIは**実行ログ＋アクセスログ**の両方。HTTP APIは**アクセスログのみ**。アクセスログに `$context.requestId`・`$context.integration.latency` を出し、Lambda側の相関ID（`context.awsRequestId`）と突き合わせます。
- **X-Ray**：**REST APIのみ対応**（HTTP APIは非対応）。エンドツーエンドのトレースを入口から取りたいなら、ここはREST APIの利点。

> **アラートは症状で**：`5XXError`・`4XXError`（急増は認証/検証の事故）・`Latency` のP99・`Count` の異常な落ち込み。`Latency − IntegrationLatency` が膨らんだら API Gateway 側、`IntegrationLatency` が膨らんだらLambda側（コールドスタート含む。[コールドスタート最適化](/blog/aws-lambda-cold-start-snapstart-provisioned-concurrency-performance-guide)）。

---

## 8. まとめ：サーバーレスAPIの入口チートシート

- **入口の既定はHTTP API**：安い（$1.00/100万）・速い（REST比 最大60%低レイテンシ）・JWTオーソライザー内蔵。
- **REST APIを選ぶのは機能要件があるときだけ**：リクエスト検証・APIキー/使用量プラン・WAF・X-Ray・プライベートAPI。
- **Function URLs はプロトタイプ/単純な入口**、**ALBは既存ALBへの相乗り**。
- **ペイロード形式2.0**：オブジェクトを返すだけで200/JSON推論。1.0は封筒必須。型（V2 vs V1）を取り違えない。
- **認証は入口で強制**：HTTP APIのJWTオーソライザー（iss/aud/Cognito）か、サービス間はIAM(SigV4)。
- **検証は境界で**：REST APIはモデル＋バリデータ、HTTP APIは関数入口でZod。二重管理しない。
- **数字を知る**：統合タイムアウト29秒(REST)/30秒(HTTP)、ペイロード10MB(GW)/6MB(Lambda同期)、コスト上限は予約済み同時実行＋WAFで。
- **CORSは設定で一元管理、エラーは正しいステータスで情報を漏らさず、可観測性はHTTP/RESTの差を踏まえて。**

私は経済産業大臣賞を受賞した木材流通B2B SaaSで、`API Gateway → NLB → ALB` 構成の上に **221本のAPIエンドポイント**を本番運用しています。入口の選定・認証の一元化・境界での検証・症状ベースのアラートを徹底することで、フロントの体感品質とセキュリティを両立させてきました。

**「自社のサーバーレスAPIを、安く・速く・安全な入口設計で本番に出したい」——入口の選定から認証・検証・可観測性まで、一人 × 生成AI（Claude Code）の速さで一気通貫に伴走します。** 既存APIの棚卸し（REST APIで無駄を払っていないか）からでも、お気軽にご相談ください。

---

### 参考（公式ドキュメント）

- [Choose between REST APIs and HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html) — 機能比較表（検証・APIキー・WAF・X-Ray・JWT等）
- [Working with Lambda proxy integrations for HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html) — ペイロード形式1.0/2.0、レスポンス自動推論
- [Function URLs vs. Amazon API Gateway](https://docs.aws.amazon.com/lambda/latest/dg/furls-http-invoke-decision.html) — Function URLsとAPI Gatewayの使い分け
- [Using AWS Lambda with an Application Load Balancer](https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html) — ALBターゲット、レスポンス形式
- [JWT authorizers for HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html) / [Lambda authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) — iss/aud検証、TOKEN/REQUEST、キャッシュTTL
- [Request validation for REST APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-request-validation.html) — モデル/バリデータ
- [Amazon API Gateway quotas](https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html) / [HTTP API quotas](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-quotas.html) — 統合タイムアウト・ペイロード・スロットル
- [CORS for HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html) / [REST APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html) — CORSの一元設定
- [Gateway responses / error handling](https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html) — Lambdaエラーのマッピング、502の原因
- [Amazon API Gateway pricing](https://aws.amazon.com/api-gateway/pricing/) — HTTP API / REST API の料金
