API Gateway REST API 実践ハンズオン

REST API の認証・使用量プラン・カスタムドメイン・WAF・VTL 統合まで踏み込む発展編

API Gateway (REST) API キー / 使用量プラン カスタムドメイン AWS WAF VTL マッピング マネコン操作 所要時間 75〜90 分 v1.0

📋 概要

このハンズオンは、入門編「Lambda + API Gateway(HTTP API)」の発展版です。HTTP API がシンプルで低コストなのに対し、REST API はエンタープライズ向けの高度な機能(API キー認証、使用量プラン、リクエスト/レスポンス変換、WAF 連携、ステージ管理など)を備えています。ここでは REST API ならではの実践的な機能を一通り体験します。

項目内容
対象サービスAPI Gateway (REST)、Lambda、AWS WAF、ACM、Route 53、DynamoDB、CloudWatch
主な学習内容ステージ管理・API キー・使用量プラン(スロットリング/クォータ)・カスタムドメイン・WAF・VTL マッピング
所要時間75〜90 分
難易度★★★★☆(中〜上級者向け)
前提知識Lambda・API Gateway の基本(入門編相当)、REST/HTTP の基礎
費用目安約 1〜2 USD(WAF Web ACL 月額 + リクエスト課金。カスタムドメインは独自ドメイン必要)
ℹ️ REST API と HTTP API の使い分け
機能HTTP API(入門編)REST API(本編)
料金安い(約 1/3)高い
レイテンシ低いやや高い
API キー / 使用量プラン非対応対応 ✅
リクエスト/レスポンス変換 (VTL)非対応対応 ✅
AWS WAF 連携非対応対応 ✅
プライベート API非対応対応 ✅
AWS サービス直接統合限定的豊富 ✅

「とにかく安くシンプルに公開したい」なら HTTP API、「API 製品として認証・課金・保護をきめ細かく管理したい」なら REST API という棲み分けです。

🏗️ アーキテクチャ

👤 API クライアント
x-api-key ヘッダー付きリクエスト
↓ https://api.example.com/v1/...
🛡️ AWS WAF(Web ACL)
IP 制限・レート制限・マネージドルール
🌐 カスタムドメイン → REST API
ステージ: dev / prod
↓ API キー検証 + 使用量プラン(スロットル/クォータ)
🔀 メソッド統合
/items → Lambda プロキシ / /raw → DynamoDB 直接(VTL)
⚡ Lambda 関数 / 🗃️ DynamoDB テーブル

作成する API のリソース構成

パスメソッド統合タイプ説明
/itemsGET / POSTLambda プロキシLambda 経由でアイテム取得・登録
/rawGETAWS 統合(VTL)Lambda を介さず DynamoDB を直接呼ぶ

✅ 前提条件

⚠️ リージョン確認

このハンズオンは ap-northeast-1(東京) で進めます。ただし カスタムドメイン用の ACM 証明書は、エッジ最適化エンドポイントの場合 us-east-1(バージニア北部)で発行する必要があります(Step3 で解説)。リージョナルエンドポイントなら同一リージョンで可。

🧩 ステップ 1 ― REST API とステージを作成する

バックエンド Lambda を用意し、REST API を構築して dev / prod の 2 ステージを作成します。

1-1. バックエンド Lambda を作成する

Lambda コンソール →「関数の作成」→ 一から作成。ランタイム Python 3.12、関数名 apigw-items-handler で作成し、以下のコードを貼り付けて「Deploy」します。

import json

def lambda_handler(event, context):
    method = event.get("httpMethod", "GET")
    stage = event.get("requestContext", {}).get("stage", "unknown")
    if method == "POST":
        body = json.loads(event.get("body") or "{}")
        msg = f"created item: {body.get('name', 'noname')}"
    else:
        msg = "list of items"
    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"message": msg, "stage": stage, "method": method})
    }
1-2. REST API を作成する

API Gateway コンソール →「API を作成」→ 「REST API」(「REST API プライベート」ではない方)の「構築」をクリックします。

API 名handson-rest-api
エンドポイントタイプリージョナル

「API を作成」をクリックします。

1-3. /items リソースとメソッドを作成する

「リソースを作成」→ リソース名 items で作成します。作成した /items を選択し「メソッドを作成」で GET を追加します。

設定項目
統合タイプLambda 関数
Lambda プロキシ統合オン
Lambda 関数apigw-items-handler

同様に POST メソッドも同じ Lambda で作成します。

1-4. dev ステージにデプロイする

「API をデプロイ」をクリック →「新しいステージ」→ ステージ名 dev でデプロイします。発行された 呼び出し URL(例:https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev)をメモします。

curl https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev/items # => {"message":"list of items","stage":"dev","method":"GET"}
1-5. prod ステージを作成しステージ変数を使う

もう一度「API をデプロイ」→ 新しいステージ prod を作成します。これで同じ API を dev / prod で別々に運用できます。各ステージの「ステージ変数」タブで env=production のような変数を設定でき、統合先 Lambda のエイリアスやバックエンド URL をステージごとに切り替えられます。

ℹ️ ステージの実務的な使い方
  • ステージ変数${stageVariables.lambdaAlias} で統合先を動的に変更
  • ステージごとのスロットリング:prod は緩め、dev は厳しめ等
  • キャッシュ:prod のみレスポンスキャッシュを有効化
  • ログ/トレース:CloudWatch Logs・X-Ray をステージ単位で設定
✅ 確認ポイント

dev / prod 両ステージの URL で /items が叩け、レスポンスの stage フィールドがそれぞれ dev / prod になっていれば成功です。

🔑 ステップ 2 ― API キーと使用量プランで保護する

REST API の代表機能である API キー認証とスロットリング/クォータ制御を設定します。

2-1. メソッドに API キーを必須化する

/itemsGET メソッド →「メソッドリクエスト」の設定 →「API キーの必須」を true にします。POST も同様に設定し、API を再デプロイ(dev)します。

この状態でキー無しアクセスすると 403 Forbidden になります。

curl -i https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev/items # => HTTP/1.1 403 Forbidden {"message":"Forbidden"}
2-2. 使用量プランを作成する

API Gateway 左メニュー →「使用量プラン」→「使用量プランを作成」。

設定項目意味
名前handson-basic-planプラン名
スロットリング - レート10 リクエスト/秒定常的な秒間上限
スロットリング - バースト20瞬間的な許容上限
クォータ1000 リクエスト/日1 日あたりの総量上限
2-3. API キーを作成しプランに紐付ける

「API キー」→「API キーを作成」→ 名前 handson-key で作成します。次に使用量プランの「関連付けられた API ステージ」で handson-rest-api:dev を追加し、「API キー」タブで handson-key を追加します。

2-4. API キー付きでアクセスする

作成した API キーの値を「表示」でコピーし、x-api-key ヘッダーに付けて再アクセスします。

curl -H "x-api-key: YOUR_API_KEY_VALUE" \ https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev/items # => 200 OK {"message":"list of items",...}
2-5. スロットリングを体感する(任意)

短時間に大量リクエストを送るとレート上限を超え 429 Too Many Requests が返ります。

for i in $(seq 1 50); do \ curl -s -o /dev/null -w "%{http_code}\n" \ -H "x-api-key: YOUR_API_KEY_VALUE" \ https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev/items; done # 途中から 429 が混ざる
✅ 確認ポイント

キー無し → 403、キー有り → 200、過剰リクエスト → 429 と、使用量プランによるアクセス制御が機能していれば成功です。

🌐 ステップ 3 ― カスタムドメインを設定する

デフォルトの execute-api URL を、独自ドメイン(例:api.example.com)で公開します。

⚠️ 独自ドメインが必要なステップです

ドメインを所有していない場合はこのステップをスキップして Step4 へ進んでください(他ステップに影響しません)。

3-1. ACM で証明書をリクエストする

ACM コンソール →「証明書をリクエスト」→ パブリック証明書。ドメイン名に api.example.com を入力し、DNS 検証で発行します。

  • リージョナルエンドポイントの場合 → API と同じ ap-northeast-1 で発行
  • エッジ最適化エンドポイントの場合 → us-east-1 で発行(CloudFront 配下のため)

Route 53 を使っていれば「Route 53 にレコードを作成」ボタンで検証 CNAME を自動追加できます。状態が「発行済み」になるまで待ちます。

3-2. カスタムドメイン名を作成する

API Gateway 左メニュー →「カスタムドメイン名」→「作成」。

ドメイン名api.example.com
エンドポイントタイプリージョナル
ACM 証明書api.example.com の証明書
3-3. API マッピングを設定する

作成したカスタムドメインの「API マッピング」→「マッピングを設定」で、パスとステージを紐付けます。

パスAPIステージ
v1handson-rest-apiprod

これで https://api.example.com/v1/itemsprod ステージにマッピングされます。

3-4. Route 53 で エイリアス / CNAME を作成する

カスタムドメインに表示される「API Gateway ドメイン名」(d-xxxx.execute-api...)を、Route 53 で api.example.comA レコード(エイリアス)として登録します。数分後に名前解決され、独自ドメインでアクセス可能になります。

curl -H "x-api-key: YOUR_API_KEY_VALUE" https://api.example.com/v1/items
✅ 確認ポイント

https://api.example.com/v1/items で API が応答すればカスタムドメイン設定は成功です。

🛡️ ステップ 4 ― AWS WAF で API を保護する

REST API のステージに AWS WAF の Web ACL をアタッチし、IP 制限やレートベースのブロックを設定します。

4-1. Web ACL を作成する

WAF & Shield コンソール →「Web ACLs」→「Create web ACL」。

設定項目
名前handson-api-waf
リージョンAsia Pacific (Tokyo) ※ API と同リージョン
Resource typeRegional resources (API Gateway)

「Add AWS resources」で handson-rest-apidev(または prod)ステージを関連付けます。

4-2. レートベースルールを追加する

「Add rules」→「Add my own rules」→ Rule type Rate-based rule

設定項目
Namerate-limit-rule
Rate limit100(5 分あたり同一 IP の上限)
ActionBlock
4-3. IP セット制限ルールを追加する(任意)

「IP sets」で自分のグローバル IP を含む IP セットを作成し、Web ACL に「その IP セット以外を Block」または「特定 IP を Block」するルールを追加できます。AWS マネージドルール(AWSManagedRulesCommonRuleSet 等)を追加すると一般的な攻撃パターンも自動防御できます。

4-4. デフォルトアクションを設定して作成

Default action を Allow にして Web ACL を作成します(ルールにマッチしたものだけ Block)。数分後、レート上限を超えるアクセスは 403(WAF ブロック)が返るようになります。

# 大量アクセスでWAFのレート制限に到達させる(429ではなく403が返る) for i in $(seq 1 200); do \ curl -s -o /dev/null -w "%{http_code} " \ -H "x-api-key: YOUR_API_KEY_VALUE" \ https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev/items; done
ℹ️ WAF と 使用量プランの違い
  • 使用量プラン(Step2):API キー単位のスロットル/クォータ。429 を返す。課金・契約管理向け
  • WAF(Step4):IP・ヘッダー・ボディなどリクエスト内容での防御。403 を返す。セキュリティ向け

両者は目的が異なり、組み合わせて多層防御を構成します。

✅ 確認ポイント

WAF の「Sampled requests」やメトリクスでブロックされたリクエストが確認できれば成功です。

🔀 ステップ 5 ― VTL マッピングで DynamoDB を直接呼ぶ

REST API ならではの「AWS サービス統合」を使い、Lambda を介さずに API Gateway から DynamoDB を直接呼び出します。リクエスト/レスポンスの変換には VTL(Velocity Template Language)マッピングテンプレートを使います。

5-1. DynamoDB テーブルを作成する

DynamoDB →「テーブルの作成」。

テーブル名handson-items
パーティションキーid (String)

作成後、項目を 1 つ手動追加します(例:{ "id": "1", "name": "apple" })。

5-2. API Gateway 用 IAM ロールを作成する

API Gateway が DynamoDB を呼ぶための実行ロールを作成します。信頼されたエンティティ apigateway.amazonaws.com、ポリシーに dynamodb:GetItem(対象テーブル)を許可した apigw-dynamodb-role を作成します。

5-3. /raw リソースと GET メソッドを作成する

API Gateway で /raw リソースを作成し、GET メソッドを以下で設定します。

設定項目
統合タイプAWS サービス
AWS リージョンap-northeast-1
AWS サービスDynamoDB
HTTP メソッドPOST
アクションGetItem
実行ロールapigw-dynamodb-role の ARN
5-4. リクエストマッピングテンプレートを設定する

「統合リクエスト」→「マッピングテンプレート」→ Content-Type application/json を追加し、クエリ文字列 ?id=1 を DynamoDB の GetItem リクエストへ変換します。

{
  "TableName": "handson-items",
  "Key": {
    "id": { "S": "$input.params('id')" }
  }
}
5-5. レスポンスマッピングテンプレートを設定する

「統合レスポンス」→「マッピングテンプレート」で、DynamoDB の生レスポンスをクライアント向けの素直な JSON に整形します。

#set($item = $input.path('$.Item'))
{
  "id": "$item.id.S",
  "name": "$item.name.S"
}

API を再デプロイ(dev)して動作確認します。

curl -H "x-api-key: YOUR_API_KEY_VALUE" \ "https://abc123.execute-api.ap-northeast-1.amazonaws.com/dev/raw?id=1" # => {"id":"1","name":"apple"}
ℹ️ VTL 直接統合のメリット・注意点
  • メリット:Lambda の起動・実行コストが不要、コールドスタートなし、低レイテンシ
  • 注意:VTL はデバッグしづらく複雑なロジックには不向き。単純な CRUD のパススルー向き
  • 複雑な処理が必要なら Lambda 統合(/items)、単純な転送なら直接統合(/raw)と使い分ける
✅ 確認ポイント

/raw?id=1 が Lambda を経由せず DynamoDB の項目を返せば、VTL マッピングによる AWS サービス直接統合が成功です。

🧹 クリーンアップ

⚠️ 課金防止のためクリーンアップを必ず実施してください

WAF Web ACL は作成時から月額固定費(約 $5/月 + ルール課金)が発生します。使い終わったら必ず削除してください。API Gateway・Lambda・DynamoDB はほぼ無料枠ですが、念のためすべて削除します。

削除手順

  1. WAF Web ACL を削除
    WAF → Web ACLs → handson-api-waf → 関連リソースの関連付けを解除してから削除(IP セットも削除)
  2. カスタムドメイン・Route 53 レコードを削除(作成した場合)
    Route 53 のエイリアスレコード削除 → API Gateway のカスタムドメイン名を削除
  3. ACM 証明書を削除(再利用しない場合)
    ACM → 対象証明書を削除
  4. 使用量プラン・API キーを削除
    API Gateway → 使用量プラン handson-basic-plan 削除 → API キー handson-key 削除
  5. REST API を削除
    API Gateway → handson-rest-api →「API を削除」
  6. Lambda 関数を削除
    Lambda → apigw-items-handler を削除
  7. DynamoDB テーブルを削除
    DynamoDB → handson-items を削除
  8. IAM ロールを削除
    apigw-dynamodb-role および Lambda 実行ロールを削除
✅ クリーンアップ完了の確認
  • WAF Web ACL 一覧が空になった(← 月額費の停止に重要)
  • API Gateway に handson-rest-api が無い
  • DynamoDB テーブル・Lambda 関数が削除された

学習のまとめ

習得したスキル実践内容
REST API とステージ管理dev / prod ステージ・ステージ変数の運用
API キー / 使用量プランキー認証 + スロットリング + クォータで API を製品化
カスタムドメインACM 証明書 + Route 53 で独自ドメイン公開
WAF 連携レート制限・IP 制限・マネージドルールで多層防御
VTL 直接統合Lambda レスで DynamoDB を直接呼び出すマッピング