API Gateway + Lambda
Lambda を HTTP で公開する標準パターンを整理します。REST API と HTTP API の違い、Lambda 統合(proxy / non-proxy)、ルート / メソッド、権限(IAM / Cognito / Lambda authorizer)、CORS、ステージ / デプロイ、Throttling、使用量プラン、キャッシング、Custom Domain、コストまでを扱います。
第17章 Lambda 基礎 の関数を HTTP で呼ぶには入り口が必要です。AWS の標準は API Gateway です。Lambda (または ECS、EC2、外部バックエンド) の前に置くマネージドゲートウェイです。
本章は API Gateway の2つの形態(REST / HTTP)、Lambda 統合パターン、権限オプション、ステージ / デプロイ、使用量プランまでをまとめて整理します。直前の第17章で固めた同期呼び出しモデルがここで実際の HTTP エンドポイントになり、次の 第19章 EventBridge / SQS / SNS ではその反対側である非同期通信に移ります。
API Gateway がすること #
クライアント (ブラウザ、アプリ)
│ HTTPS
▼
API Gateway
├─ TLS 終端
├─ ルーティング (path → バックエンド)
├─ 認証 / 権限
├─ Rate limiting / Throttling
├─ リクエスト変換
└─ キャッシング
│
▼
バックエンド (Lambda / ECS / 外部 HTTP)ALB (第13章 ALB / NLB と ACM) と機能が似ていますが強調点が異なります。
| ALB | API Gateway | |
|---|---|---|
| 価格モデル | 時間 + LCU | リクエスト + データ |
| 0 トラフィックのコスト | 時間あたり (~$20/月) | 0 |
| Lambda 直接統合 | 可能 | 自然 |
| API key / Usage Plan | なし | あり |
| キャッシング | なし | あり (REST API) |
| Cognito 統合 | 別途 | 自然 |
| 適する | 常に回る大きな ECS | 変動が大きい / サーバーレス / API key が必要 |
REST API vs HTTP API #
API Gateway は2つの形態で提供されます。
HTTP API (v2) — 新型 #
- 70% 安く て速いです。
- JWT トークン (Cognito / Auth0 / 独自 OIDC) を直接検証します。
- CORS 設定が単純です。
- Lambda proxy 統合がデフォルトです。
- WebSocket 非対応、API key / Usage Plan 非対応、リクエスト変換 / キャッシングなしです。
新規プロジェクトのデフォルト候補です。単純な REST API + JWT なら十分です。
REST API (v1) — 旧型 + 豊富な機能 #
- キャッシング (ステージ単位)
- リクエスト / レスポンスのマッピング変換
- WAF 直接統合
- API Key + Usage Plan
- WebSocket サポート
複雑な変換 / 使用量の分離 / WebSocket が必要なときに使います。
どう選ぶか #
WebSocket 必要? ─Yes→ REST API (または WebSocket API)
│ No
▼
API Key / Usage Plan 必要? ─Yes→ REST API
│ No
▼
複雑な変換 / キャッシング 必要? ─Yes→ REST API
│ No
▼
HTTP API (安い + 速い)本章は HTTP API を基準に進め、REST API のみの機能は別途に表示します。
最初の API — Hello, World #
Lambda 関数の準備 #
import json
def handler(event, context):
name = event.get("queryStringParameters", {}).get("name", "world")
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": f"Hello, {name}!"})
}すでに 第17章 Lambda 基礎 で扱った関数です。この関数を hello-fn としてデプロイしておきます。
HTTP API を作る (コンソール) #
コンソール → API Gateway → 「API の作成」→ HTTP API の「構築」で進めます。
- 統合の追加: Lambda → リージョン / 関数 (
hello-fn) - API 名:
hello-api - ルート設定:
GET /hello→hello-fn - ステージ:
$default(自動デプロイ) - 作成
完了すると URL が出ます: https://abc123.execute-api.ap-northeast-2.amazonaws.com/hello?name=AWS
CLI で作る #
# API 作成
API_ID=$(aws apigatewayv2 create-api \
--name hello-api \
--protocol-type HTTP \
--target arn:aws:lambda:ap-northeast-2:123456789012:function:hello-fn \
--query ApiId --output text)
# Lambda が API Gateway から呼び出される権限
aws lambda add-permission \
--function-name hello-fn \
--statement-id apigw-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:ap-northeast-2:123456789012:$API_ID/*/*"
echo "https://$API_ID.execute-api.ap-northeast-2.amazonaws.com/"--target オプションがルートと統合を自動でしてくれるショートカットです。精密な制御が必要なら create-route、create-integration を別々に使います。
Lambda 統合 — Proxy vs Non-proxy #
API Gateway → Lambda 呼び出しの2つのモードです。
Proxy 統合 (HTTP API のデフォルト) #
リクエストをほぼそのまま Lambda に伝えます。Lambda が statusCode / headers / body を標準レスポンス形式で返します。
# Event
{
"version": "2.0",
"routeKey": "POST /users",
"rawPath": "/users",
"queryStringParameters": {"limit": "10"},
"headers": {...},
"body": "{\"name\":\"Alice\"}",
"isBase64Encoded": false
}
# Lambda レスポンス
{
"statusCode": 201,
"headers": {"Content-Type": "application/json"},
"body": "{\"id\":42}"
}運用の標準です。コードが自分のルーティング / レスポンス形成を担えば、変更時に API Gateway を触らなくて済みます。
Non-proxy (REST API のみ) #
API Gateway がリクエストを変換して Lambda が望む形で送り、レスポンスも変換します。マッピングテンプレート (Velocity Template Language) で定義します。
{
"name": "$input.path('$.name')",
"user_id": "$context.authorizer.claims.sub"
}既存の Lambda が raw event を受け取らず、きれいな入力だけを受け取るようにします。学習曲線があり、モニタリングが厄介なので新規プロジェクトはほぼ常に Proxy を使います。
ルートとメソッド #
ルート = path + method の組み合わせです。
# Catch-all
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'ANY /'
# 特定のメソッド
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'GET /users'
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'POST /users'
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'GET /users/{userId}'{userId} のようなパラメータは event の pathParameters に入ってきます。
1つの Lambda が複数のルート #
小さな API なら1つの Lambda がすべてのルートを処理します。Lambda の中で FastAPI / Flask のようなフレームワークと adapter (例: Mangum) でルーティングします。
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI()
@app.get("/users")
def list_users():
return [{"id": 1, "name": "Alice"}]
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "Alice"}
handler = Mangum(app) # Lambda handlerAPI Gateway のルートは ANY /{proxy+} 1行で終わります。
ルートごとに別の Lambda #
大きな API はルートごとに分離します。権限 / 資源 / デプロイの単位が明確になります。
GET /users → list-users-fn
GET /users/{id} → get-user-fn
POST /users → create-user-fn分離するとコールドスタートも分散されます — 1関数ごとにウォーミング / 同時実行を別々に管理します。
権限 — 誰が呼び出せるか #
API Gateway で最もよく出会う設定です。4つのオプションがあります。
1) Open (認証なし) #
テスト / 公開 API です。運用にはほとんど使いません。
2) IAM 認証 #
リクエストに SigV4 署名 (AWS SDK が自動) が必要です。AWS アカウントの中でサービス間の呼び出しに適しています。
import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
import requests
session = boto3.Session()
creds = session.get_credentials()
url = "https://abc.execute-api.ap-northeast-2.amazonaws.com/hello"
req = AWSRequest(method="GET", url=url)
SigV4Auth(creds, "execute-api", "ap-northeast-2").add_auth(req)
resp = requests.get(url, headers=dict(req.headers))ブラウザから直接呼び出すのは難しいです (署名の露出)。バックエンド ↔ バックエンド用途です。
3) JWT Authorizer (HTTP API) #
Cognito User Pool / Auth0 / 独自 OIDC の ID トークンを API Gateway が直接検証します。HTTP API の強みです。
aws apigatewayv2 create-authorizer \
--api-id $API_ID \
--authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration '{
"Audience": ["my-app"],
"Issuer": "https://cognito-idp.ap-northeast-2.amazonaws.com/<UserPoolId>"
}' \
--name jwt-authルートに付けると Authorization: Bearer eyJ... ヘッダーが自動検証され、claims が Lambda event の requestContext.authorizer.jwt.claims に入ってきます。
4) Lambda Authorizer (Custom) #
検証ロジック自体を自分で書いた Lambda が担います。最大の柔軟性を提供します。
def handler(event, context):
token = event["headers"]["authorization"].replace("Bearer ", "")
# 独自の検証ロジック (DB 照会 / 外部呼び出しなど)
user = verify_token_somehow(token)
if not user:
return {"isAuthorized": False}
return {
"isAuthorized": True,
"context": {"user_id": user.id, "role": user.role}
}キャッシングが可能です (TTL の間は同じトークンは検証を飛ばす)。外部認証システムとの連携に自然です。
5) Cognito User Pool Authorizer (REST API 専用) #
REST API で Cognito を直接統合します。HTTP API の JWT Authorizer と似た機能ですが REST API で使います。
CORS #
ブラウザから呼び出すには CORS 設定が必須です。HTTP API はコンソール操作だけで済みます。
aws apigatewayv2 update-api \
--api-id $API_ID \
--cors-configuration "AllowOrigins=https://myapp.com,AllowMethods=GET,POST,AllowHeaders=*"REST API は各ルートに OPTIONS レスポンスを直接作らなければなりません — コンソールに「CORS 有効化」ボタンが自動生成されます。
ステージとデプロイ #
HTTP API #
自動デプロイオプションがデフォルトです。ルートの変更が即座に反映されます。ステージは $default 1つです。
別の環境 (dev / staging / prod) の分離は API 自体を別々に作る方向です。
REST API #
明示的なデプロイとステージを使います。dev、staging、prod のようなステージに同時に別のバージョンを運用できます。
aws apigateway create-deployment --rest-api-id $REST_ID --stage-name prodステージごとにキャッシング、ロギング、throttling、変数を別々に置きます。
Throttling — トラフィックの制限 #
基本の上限は次のとおりです。
- アカウント単位: 10,000 req/s、burst 5,000
- ルート単位: 別途設定可能
aws apigatewayv2 update-stage \
--api-id $API_ID --stage-name '$default' \
--route-settings '{
"GET /heavy": {
"ThrottlingRateLimit": 100,
"ThrottlingBurstLimit": 50
}
}'悪性 / 重いルートだけを強く制限します — 他のルートは影響がありません。
API Key + Usage Plan (REST API) #
外部ユーザー / パートナーに API を露出する用途です。
API Key (ユーザーごとの固有文字列)
│
▼
Usage Plan (上限、例: "月 10万 req、秒あたり 50 req")
│
▼
Stage (実際の API)ユーザー登録時に API Key 発行 → Usage Plan に連携 → そのユーザーのリクエストに自動で上限を適用します。
KEY_ID=$(aws apigateway create-api-key \
--name customer-A --enabled --query id --output text)
PLAN_ID=$(aws apigateway create-usage-plan \
--name basic-tier \
--throttle 'rateLimit=50,burstLimit=100' \
--quota 'limit=100000,period=MONTH' \
--query id --output text)
aws apigateway create-usage-plan-key \
--usage-plan-id $PLAN_ID --key-id $KEY_ID --key-type API_KEY
aws apigateway update-usage-plan \
--usage-plan-id $PLAN_ID \
--patch-operations 'op=add,path=/apiStages,value=<rest-api-id>:prod'呼び出し側はヘッダー x-api-key: <キー> を一緒に送ります。
HTTP API は API Key / Usage Plan をサポートしません。必要なら REST API を使います。
キャッシング (REST API) #
ステージ単位でレスポンスをキャッシングします。GET リクエストに自然です。
aws apigateway update-stage \
--rest-api-id $REST_ID --stage-name prod \
--patch-operations 'op=replace,path=/cacheClusterEnabled,value=true' \
--patch-operations 'op=replace,path=/cacheClusterSize,value=0.5'- キャッシュサイズ: 0.5 GB ~ 237 GB
- TTL: ルートごとに設定
- 無効化: ヘッダー
Cache-Control: max-age=0(必要な権限を持つ呼び出し元のみ)
コストは時間あたりです — トラフィックが本当に多い場合にのみ価値があります。
ロギング — Access Log + Execution Log #
Access Log #
リクエスト単位です — 運用 / ビジネス分析に使います。
aws apigatewayv2 update-stage \
--api-id $API_ID --stage-name '$default' \
--access-log-settings '{
"DestinationArn": "arn:aws:logs:ap-northeast-2:123456789012:log-group:/aws/apigateway/hello-api",
"Format": "{ \"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"requestTime\":\"$context.requestTime\",\"routeKey\":\"$context.routeKey\",\"status\":\"$context.status\" }"
}'JSON で受け取って CloudWatch Logs Insights でクエリします。
Execution Log (REST API) #
内部の段階ごとのログです — デバッグ用です。運用ではコスト / ノイズのためあまり有効にしません。
ユーザードメイン — Custom Domain #
https://api.myapp.com/... で露出します。ACM 証明書 (第13章 ALB / NLB と ACM) と 第12章 Route 53 と連携します。
aws apigatewayv2 create-domain-name \
--domain-name api.myapp.com \
--domain-name-configurations \
"CertificateArn=arn:aws:acm:ap-northeast-2:...,EndpointType=REGIONAL"
# API → Domain マッピング
aws apigatewayv2 create-api-mapping \
--api-id $API_ID \
--domain-name api.myapp.com \
--stage '$default'
# Route 53 — A レコード alias
aws route53 change-resource-record-sets ...コスト #
HTTP API #
- リクエストあたり $1.00 / 100万 (~$0.000001)
- Data Transfer 別途
REST API #
- リクエストあたり $3.50 / 100万 (3.5 倍)
- キャッシング有効化時に時間あたり追加
月 100万リクエスト基準です。
- HTTP API: $1
- REST API: $3.50 (+ キャッシュ / Usage Plan 使用時に追加)
Lambda ほどではないにせよ非常に安いです。
よく出会う落とし穴 #
1) Lambda が呼び出されない / 権限拒否 #
lambda:InvokeFunction 権限が API Gateway に付与されていません。コンソール統合は自動で、CLI は aws lambda add-permission を直接します。
2) CORS が解けない #
ブラウザのコンソールに CORS エラーが出ます。2つを点検します。
- API Gateway の CORS 設定 (上を参照)
- Lambda レスポンスの
Access-Control-Allow-Originヘッダー (proxy 統合は Lambda が担う)
3) タイムアウト #
API Gateway の統合タイムアウトは最大 30秒 (REST)、30秒 (HTTP) です。Lambda は 15分ですが API Gateway が切ります。30秒を超える処理は非同期 (Lambda Destination、Step Functions、SQS) で回避します。
4) Body サイズの上限 #
API Gateway の payload の上限は 6 MB (両側) です。大きなファイルは S3 presigned URL パターンを使います。
5) 意図しない自動デプロイ (HTTP API) #
ルート / 統合の変更が即座に運用に反映されます。CI で IaC (Terraform / CDK) で管理すれば意図された変更ですが、コンソールでの直接修正は危険です。コンソールは dev / staging API だけ使います。
6) JWT Authorizer の audience / issuer の打ち間違い #
aud / iss が一文字でも違うと 401 です。Cognito User Pool ID の正確な issuer URL (https://cognito-idp.<リージョン>.amazonaws.com/<UserPoolId>) を確認します。
7) API Key がすべてのセキュリティの終わりではない #
API Key はクライアントに平文で残っています — 決済情報のような用途には単独の認証として使いません。使用量の分離と Throttling 用途です。本当の認証は JWT / IAM です。
練習問題 #
- 自分が作る API について §「REST API vs HTTP API」の決定ツリーをたどって HTTP API と REST API のどちらを使うかを決め、その決定を分けた1つの機能(WebSocket / API Key / キャッシング / コスト)を1行で書いておきましょう。
- 小さな API を1つの Lambda で FastAPI + Mangum で処理する方式と、ルートごとに Lambda を分離する方式の長所短所を、§「ルートとメソッド」と 第17章 Lambda 基礎 のコールドスタートを根拠に1段落で比較してみましょう。
- §「権限」の4つのオプションのうち、ブラウザの SPA から直接呼び出す API に適したものと、バックエンドどうしで呼び出す API に適したものをそれぞれ選んで理由を書いてみましょう。JWT Authorizer を使うなら、§「よく出会う落とし穴」で最もよくある 401 の原因が何かも併せて書いておきましょう。
一行まとめ: API Gateway は Lambda / ECS / 外部バックエンドの前に置くマネージドゲートウェイで、トラフィックが 0 ならコストもかかりません。HTTP API は REST API より 70% 安くて速く新規プロジェクトのデフォルトで、WebSocket · API Key · キャッシングが必要なら REST API を使います。Lambda 統合は Proxy が標準で、権限は Open / IAM / JWT / Lambda Authorizer に分かれ、HTTP API の JWT Authorizer が Cognito · Auth0 · OIDC トークンを直接検証します。最もよくある落とし穴は Lambda invoke 権限の漏れ、CORS、30秒タイムアウト、6MB payload の上限、JWT issuer の打ち間違いです。
次の章 #
API Gateway と Lambda の同期呼び出しは終わりました。次の 第19章 EventBridge / SQS / SNS では非同期 / イベント駆動の通信を扱います。3つのツールの役割比較、fan-out パターン、DLQ、冪等性(idempotency)まで AWS のメッセージインフラを整理します。