AWS上級 #4 API Gateway + Lambda
#3 Lambda 基礎 の関数を HTTP で呼ぶには入口が必要です。AWS の標準は API Gateway — Lambda (または ECS、EC2、外部バックエンド) の前に置くマネージドゲートウェイ。
今回は API Gateway の 2 つの形 (REST / HTTP)、Lambda 統合パターン、権限オプション、ステージ / デプロイ、使用量プランまで一気に整理します。
API Gateway がすること #
クライアント (ブラウザ、アプリ)
│ HTTPS
▼
API Gateway
├─ TLS 終端
├─ ルーティング (path → バックエンド)
├─ 認証 / 認可
├─ Rate limiting / Throttling
├─ リクエスト変換
└─ キャッシング
│
▼
バックエンド (Lambda / ECS / 外部 HTTP)ALB (中級 #6) と機能が似ていますが強調点が違います。
| 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}!"})
}すでに #3 で扱った関数です。この関数を 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分離するとコールドスタートも分散 — 関数毎にウォーミング / 並行性を別々に管理。
権限 — 誰が呼べるか #
API Gateway で最もよく出会う設定の 1 つです。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 は コンソール 1 度 で完了。
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 (上限、e.g. "月 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) #
内部段階別ログ — デバッグ用。運用では費用 / ノイズで滅多に on にしません。
カスタムドメイン — Custom Domain #
https://api.myapp.com/... で公開。ACM 証明書 (中級 #6) + Route 53 (中級 #5) と連携。
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 が 1 文字でも違えば 401。Cognito User Pool ID の正確な issuer URL (https://cognito-idp.<リージョン>.amazonaws.com/<UserPoolId>) を確認。
7) API Key がすべてのセキュリティの終わりではない #
API Key は クライアントに平文で生きている — 決済情報のような用途で単独認証 X。使用量分離 + Throttling の用途。本当の認証は JWT / IAM。
まとめ #
今回つかんだもの:
- API Gateway の役割 — Lambda / ECS / 外部バックエンドの前のマネージドゲートウェイ。0 トラフィック費用 0
- HTTP API vs REST API — HTTP は 70% 安い + 速い。WebSocket / API Key / キャッシングが必要なら REST
- Lambda 統合 — Proxy (標準) vs Non-proxy (変換)。新規プロジェクトは Proxy
- ルート / メソッド —
GET /users/{id}のようなパターン。1 つの Lambda 全ルーティング (Mangum 等) vs ルート別分散 - 権限 4 オプション — Open / IAM / JWT / Lambda Authorizer / (REST の) Cognito User Pool
- JWT Authorizer (HTTP API) — Cognito / Auth0 / OIDC ID トークンを直接検証
- CORS — HTTP API は 1 行
- ステージ — HTTP API は自動デプロイ / REST は明示的デプロイ + 環境分離
- Throttling — ルート別上限
- API Key + Usage Plan (REST のみ) — 外部ユーザー別上限
- キャッシング (REST のみ) — ステージ単位
- Access Log — JSON で CloudWatch Logs に
- Custom Domain — ACM + Route 53
- 費用 — HTTP API リクエスト当たり $0.000001、非常に安い
- 落とし穴 — Lambda 権限、CORS、30 秒タイムアウト、6MB Payload、HTTP API 自動デプロイ、JWT issuer タイポ、API Key の役割
次回 — メッセージインフラ #
API Gateway / Lambda の同期呼び出しはここまでです。次は 非同期 / イベント駆動の通信 を整理する番です。
#5 EventBridge / SQS / SNS では 3 つの違いの比較、fan-out パターン、DLQ、idempotency まで — AWS のメッセージインフラを一気に整理します。