目次
18 章

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 がすること #

API Gateway がすること
クライアント (ブラウザ、アプリ)
   │ HTTPS
API Gateway
   ├─ TLS 終端
   ├─ ルーティング (path → バックエンド)
   ├─ 認証 / 権限
   ├─ Rate limiting / Throttling
   ├─ リクエスト変換
   └─ キャッシング
バックエンド (Lambda / ECS / 外部 HTTP)

ALB (第13章 ALB / NLB と ACM) と機能が似ていますが強調点が異なります。

ALBAPI 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 関数の準備 #

lambda_function.py
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 の「構築」で進めます。

  1. 統合の追加: Lambda → リージョン / 関数 (hello-fn)
  2. API 名: hello-api
  3. ルート設定: GET /hellohello-fn
  4. ステージ: $default (自動デプロイ)
  5. 作成

完了すると URL が出ます: https://abc123.execute-api.ap-northeast-2.amazonaws.com/hello?name=AWS

CLI で作る #

HTTP API 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-routecreate-integration を別々に使います。

Lambda 統合 — Proxy vs Non-proxy #

API Gateway → Lambda 呼び出しの2つのモードです。

Proxy 統合 (HTTP API のデフォルト) #

リクエストをほぼそのまま Lambda に伝えます。Lambda が statusCode / headers / body を標準レスポンス形式で返します。

proxy 統合の入出力
# 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) で定義します。

VTL マッピング
{
  "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) でルーティングします。

FastAPI on Lambda
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 handler

API 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 アカウントの中でサービス間の呼び出しに適しています。

IAM 認証された呼び出し (Python)
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 の強みです。

JWT Authorizer を作る
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 が担います。最大の柔軟性を提供します。

lambda authorizer
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 はコンソール操作だけで済みます。

HTTP API CORS
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 #

明示的なデプロイとステージを使います。devstagingprod のようなステージに同時に別のバージョンを運用できます。

aws apigateway create-deployment --rest-api-id $REST_ID --stage-name prod

ステージごとにキャッシング、ロギング、throttling、変数を別々に置きます。

Throttling — トラフィックの制限 #

基本の上限は次のとおりです。

  • アカウント単位: 10,000 req/s、burst 5,000
  • ルート単位: 別途設定可能
ルートあたり 100 req/s に制限
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 に連携 → そのユーザーのリクエストに自動で上限を適用します。

API Key + 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 #

リクエスト単位です — 運用 / ビジネス分析に使います。

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 と連携します。

Custom Domain
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 です。

練習問題 #

  1. 自分が作る API について §「REST API vs HTTP API」の決定ツリーをたどって HTTP API と REST API のどちらを使うかを決め、その決定を分けた1つの機能(WebSocket / API Key / キャッシング / コスト)を1行で書いておきましょう。
  2. 小さな API を1つの Lambda で FastAPI + Mangum で処理する方式と、ルートごとに Lambda を分離する方式の長所短所を、§「ルートとメソッド」と 第17章 Lambda 基礎 のコールドスタートを根拠に1段落で比較してみましょう。
  3. §「権限」の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 のメッセージインフラを整理します。

X