「Lambda でAPIを作りたい。でも前段に API Gateway を置くのか、Function URL で十分なのか、そもそも REST API と HTTP API は何が違うのか」——サーバーレスでHTTP APIを本番に出すとき、最初に必ず立ち止まる分岐です。ここを間違えると、払わなくていい料金を払い続けるか、後から必要になった機能(WAF・リクエスト検証・APIキー)が足りずに作り直すことになります。
この記事は、AWS Lambda で本番品質のHTTP/REST APIを作るための実装ガイドです。4つの入口の選定から、ペイロード形式・認証・検証・CORS・エラー設計・スロットリングまでを一気通貫で解説します。題材として、経済産業大臣賞を受賞した木材流通B2B SaaS(木材流通業界のDX)での API Gateway → … → 221本のAPIエンドポイント という本番運用の判断も交えます。Lambda 本体の実行モデル・冪等性・可観測性は姉妹記事 AWS Lambda 本番運用ガイド に委ね、本稿は**「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内 |
選定ロジックはシンプルです。
- WAF・リクエスト検証・APIキー(使用量プラン)・X-Ray・プライベートAPIのどれかが要る? → REST API。
- 要らない(大半のAPIはこちら)? → HTTP API。安くて速い。
- そもそもAPI Gatewayの機能が要らない単純な入口? → Function URLs。
- 既存の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: 戻り値 を自動で推論します。つまりハンドラがオブジェクトをそのまま返すだけでよくなります。
// 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を選ぶ最大の理由のひとつです。
# 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)検証の正しい実装 に詳しくまとめています。
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 等のスキーマを境界に置きます。
// 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 の選定 を見直す。
- 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等)を使う。手書きヘッダより設定を推奨。
# 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)だけ返し、詳細は構造化ログへ(可観測性は本番運用ガイド参照)。
// 例外を「クライアント向けの安全なレスポンス」に翻訳する薄い層
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側(コールドスタート含む。コールドスタート最適化)。
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 — 機能比較表(検証・APIキー・WAF・X-Ray・JWT等)
- Working with Lambda proxy integrations for HTTP APIs — ペイロード形式1.0/2.0、レスポンス自動推論
- Function URLs vs. Amazon API Gateway — Function URLsとAPI Gatewayの使い分け
- Using AWS Lambda with an Application Load Balancer — ALBターゲット、レスポンス形式
- JWT authorizers for HTTP APIs / Lambda authorizers — iss/aud検証、TOKEN/REQUEST、キャッシュTTL
- Request validation for REST APIs — モデル/バリデータ
- Amazon API Gateway quotas / HTTP API quotas — 統合タイムアウト・ペイロード・スロットル
- CORS for HTTP APIs / REST APIs — CORSの一元設定
- Gateway responses / error handling — Lambdaエラーのマッピング、502の原因
- Amazon API Gateway pricing — HTTP API / REST API の料金