
【VPC外リージョンサービス】
API Gateway は、フルマネージド型の API フロントサービスです。REST、HTTP、WebSocket の各プロトコルに対応しており、APIを通じてアプリケーションとバックエンド間の通信を安全かつ効率的に管理できます。
API Gateway では、APIリクエストのルーティングや認証(Cognito、JWT、Lambda オーソライザーなど)を柔軟に設定できるほか、スロットリング(スループット制御)機能によって、過剰なリクエストからシステムを保護します。また、WAF(Web Application Firewall)との連携により、不正アクセスや攻撃から API を守ることができます。
デプロイ面では、API のバージョンや環境ごとに Stage(ステージ) を管理し、Deploy(デプロイ) を行うことで、開発・検証・本番環境を明確に分離できます。さらに、Canary(カナリア)リリース機能を利用することで、リクエストの一部を新しい API バージョンに段階的に切り替え、安全にリリースを進めることができます。
バックエンドとしては、Lambda、ECS/EKS、ALB(Application Load Balancer)、および VPC (Virtual Private Cloud)内の HTTP エンドポイント などを指定でき、幅広いアーキテクチャに対応します。また、カスタムドメインと ACM(AWS Certificate Manager) を統合することで、HTTPS化された安全な API エンドポイントを提供できます。
API のメトリクスやアクセスログは Amazon CloudWatch に送信され、モニタリングや可視化が容易です。
重要用語
ユースケース
| Web・モバイルアプリ向けREST APIの公開 | フロントエンドからのHTTPリクエストを受け付ける入り口としてAPI Gatewayを配置し、バックエンドのLambdaやEC2に処理を渡すことで、認証やレート制限付きのAPIを提供する。 |
| マイクロサービスAPIの集約(BFF) | 複数のバックエンドサービスを1つのAPIエンドポイントに集約し、クライアントごと(Web/モバイル)に最適化されたAPIレスポンスを返すBFF(Backend For Frontend)として利用する。 |
| 外部パートナー向けAPI提供 | パートナー企業に公開する業務APIに対して、APIキーや利用制限、認証をAPI Gateway側で管理し、安全に外部公開する。 |
ベストプラクティス
| ステージとデプロイ管理 | 開発・ステージング・本番などステージを分けてデプロイを管理する。 |
| WAFや認証の統合 | CognitoやLambdaオーソライザー、WAFと連携してAPIを保護する。 |
| スロットリングとクォータ設定 | 乱用やスパイクアクセスからバックエンドを守るためにレート制限を設定する。 |
高可用性・バックアップ・リトライ
| 高可用性・バックアップ・リトライ設計のポイント |
|---|
| 【デフォルト】AWS内部で冗長化 ・リクエスト処理エンジン ・API定義・設定データ ・認証・認可メカニズム ・レスポンスキャッシュ(有効時) ・スロットリング・クォータ管理 ・ログ・メトリクス収集 |
| 【自動リトライ】デフォルト(バックオフ後、1~3回) APIを実行した際に、AWS内部で通信エラー、一時的な障害を検知すると、自動的にリトライを行う |
セキュリティ
| 関連サービス | 設定内容 |
|---|---|
| WAF(アプリケーション層の脅威に対する防御) | 【CloudFrontを使用しない場合】 WAFをアタッチすることを推奨 |
| Shield(DDoS攻撃からのリソース保護) | Shield Standardは常時有効 【CloudFrontを使用しない場合】 Shield Advancedの有効化を推奨 |
| ACM(SSL/TLS証明書の自動管理) | HTTPS利用時はACM証明書が必須 リージョン:VPCのあるリージョン |
| KMS(データの暗号化と鍵の安全管理) | - |
| Secrets Manager(機密情報の安全管理) | シークレット(秘密情報)の作成が推奨 バックエンド連携に使うAPIキーや認証トークン |
| SSM Parameter Store(設定情報の一元管理) | - |
| CloudTrail(操作履歴の記録・監査・追跡) | 【自動記録】 作成・更新・削除・設定変更は自動記録される。(コントロールプレーンAPI) データ操作は追跡できない(データプレーンAPI) |
| Config(リソースの構成状態・設定変更を記録) | 【Configが有効な場合】 ステージ/ログ/認証/エンドポイント種別の変更履歴・準拠評価 |
| GuardDuty(脅威を自動検出) | 【GuardDutyが有効な場合】 エンドポイント設定変更のAPI異常検知 |
API Gatewayが実行ロールを引き受ける典型的な連携パターン
| 実行ロールを介して連携するサービス | 実行ロールにアタッチするポリシー |
|---|---|
| S3 | s3:ListBucket s3:GetObject s3:PutObject s3:DeleteObject |
| DynamoDB | dynamodb:GetItem dynamodb:PutItem dynamodb:UpdateItem dynamodb:DeleteItem dynamodb:Query dynamodb:Scan |
| SQS(送信) | sqs:SendMessage sqs:SendMessageBatch |
| SNS | sns:Publish |
| EventBridge | events:PutEvents |
| Step Functions | states:StartExecution |
| Kinesis Data Streams | kinesis:PutRecord kinesis:PutRecords |
| Lambda | lambda:InvokeFunction |
信頼ポリシー: API Gateway
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
ログ・監視
| ログ出力先 | ログの種類 |
|---|---|
| CloudWatch Logs | アクセスログ/実行ログ |
標準メトリクス
| メトリクス名 | 説明 |
|---|---|
| CacheHitCount | キャッシュヒット数 |
| CacheMissCount | キャッシュミス数 |
| ConnectCount | WebSocket接続数 |
| Count | リクエスト総数 |
| IntegrationLatency | 統合レイテンシ |
| Latency | レイテンシ |
| MessageCount | WebSocketメッセージ数 |
| 4XXError | 4xxエラー数 |
| 5XXError | 5xxエラー数 |
| ClientError | クライアントエラー数 |
| ExecutionError | 実行エラー数 |
| IntegrationError | 統合エラー数 |
| DataProcessed | 処理データ量 |
制限値(固定値/ハードリミット/ソフトリミット)
| ハードリミット | 制限値 |
|---|---|
| 統合タイムアウト | 29秒 |
| ペイロードサイズ | 10 MB |
| メソッド数/リソース | 20 |
| ソフトリミット | 制限値 |
|---|---|
| REST APIのスロットル | 10,000 RPS |
| WebSocket接続 | 500,000(同時)、3,000メッセージ/秒 |
| ステージ数/API | 10 |
| リソース数/API | 300 |
| APIキー数/アカウント | 500 |
| 使用量プラン数/アカウント | 300 |
| カスタムドメイン名数 | 120 |
| VPCリンク数/リージョン | 20 |
AWS CLIのサンプルコード
DynamoDB の Users テーブルを作成する
aws dynamodb create-table \
--table-name Users \
--attribute-definitions \
AttributeName=userId,AttributeType=S \
--key-schema \
AttributeName=userId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region ap-northeast-1
DynamoDB の Posts テーブルを作成する
aws dynamodb create-table \
--table-name Posts \
--attribute-definitions \
AttributeName=postId,AttributeType=S \
--key-schema \
AttributeName=postId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region ap-northeast-1
DynamoDB のテーブルを表示する(テーブル名 指定)
aws dynamodb describe-table \
--table-name Users \
--region ap-northeast-1
DynamoDB のテーブルを削除する(テーブル名 指定)
aws dynamodb delete-table \
--table-name Users \
--region ap-northeast-1
実行ロールを作成する(信頼ポリシーのファイル名 指定)
lambda-trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
aws iam create-role \
--role-name lambda-api-execution-role \
--assume-role-policy-document file://lambda-trust-policy.json
実行ロールを表示する
aws iam list-roles
実行ロールを削除する(実行ロール名 指定)
aws iam list-attached-role-policies \
--role-name lambda-api-execution-role
実行ロールにAWS管理ポリシーをアタッチする(実行ロール名、ポリシーのARN 指定)
aws iam attach-role-policy \
--role-name lambda-api-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
実行ロールからAWS管理ポリシーをデタッチする(実行ロール名、ポリシーのARN 指定)
aws iam detach-role-policy \
--role-name lambda-api-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
カスタム管理ポリシーを作成する(ポリシーファイル名 指定)
lambda-dynamodb-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": [
"arn:aws:dynamodb:ap-northeast-1:123456789012:table/Users",
"arn:aws:dynamodb:ap-northeast-1:123456789012:table/Users/index/*",
"arn:aws:dynamodb:ap-northeast-1:123456789012:table/Posts",
"arn:aws:dynamodb:ap-northeast-1:123456789012:table/Posts/index/*"
]
}
]
}
aws iam create-policy \
--policy-name DynamoDBAccessPolicy \
--policy-document file://lambda-dynamodb-policy.json
実行ロールにカスタム管理ポリシーをアタッチする(実行ロール名、ポリシーのARN 指定)
aws iam attach-role-policy \
--role-name lambda-api-execution-role \
--policy-arn arn:aws:iam::123456789012:policy/DynamoDBAccessPolicy
Lambda 関数作成(Lambda関数ファイル名 指定)
lambda_function.py
import json
import boto3
from decimal import Decimal
from datetime import datetime
from boto3.dynamodb.conditions import Key
# DynamoDB クライアント初期化
dynamodb = boto3.resource('dynamodb')
users_table = dynamodb.Table('Users')
posts_table = dynamodb.Table('Posts')
def lambda_handler(event, context):
"""
API Gateway HTTP API からのイベントを処理
"""
print('Event:', json.dumps(event))
route_key = event.get('routeKey', '')
path_parameters = event.get('pathParameters', {})
body = event.get('body', '{}')
try:
# Users API
if route_key == 'GET /users':
return get_users()
elif route_key == 'GET /users/{id}':
return get_user(path_parameters['id'])
elif route_key == 'POST /users':
return create_user(json.loads(body))
elif route_key == 'PATCH /users/{id}':
return update_user(path_parameters['id'], json.loads(body))
elif route_key == 'DELETE /users/{id}':
return delete_user(path_parameters['id'])
# Posts API
elif route_key == 'GET /posts':
return get_posts()
elif route_key == 'GET /posts/{id}':
return get_post(path_parameters['id'])
elif route_key == 'POST /posts':
return create_post(json.loads(body))
elif route_key == 'PATCH /posts/{id}':
return update_post(path_parameters['id'], json.loads(body))
elif route_key == 'DELETE /posts/{id}':
return delete_post(path_parameters['id'])
else:
return response(404, {'message': 'Not Found'})
except Exception as e:
print(f'Error: {str(e)}')
return response(500, {'message': 'Internal Server Error', 'error': str(e)})
# ========== Users 関数 ==========
def get_users():
"""ユーザー一覧取得"""
result = users_table.scan()
items = convert_decimal(result.get('Items', []))
return response(200, items)
def get_user(user_id):
"""ユーザー取得"""
result = users_table.get_item(Key={'userId': user_id})
if 'Item' not in result:
return response(404, {'message': 'User not found'})
item = convert_decimal(result['Item'])
return response(200, item)
def create_user(data):
"""ユーザー作成"""
user_id = f"user-{int(datetime.now().timestamp() * 1000)}"
item = {
'userId': user_id,
'name': data.get('name', ''),
'email': data.get('email', ''),
'createdAt': datetime.now().isoformat()
}
users_table.put_item(Item=item)
return response(201, item)
def update_user(user_id, data):
"""ユーザー更新"""
# 既存ユーザーの確認
result = users_table.get_item(Key={'userId': user_id})
if 'Item' not in result:
return response(404, {'message': 'User not found'})
# 更新
existing_item = result['Item']
existing_item.update(data)
existing_item['updatedAt'] = datetime.now().isoformat()
users_table.put_item(Item=existing_item)
item = convert_decimal(existing_item)
return response(200, item)
def delete_user(user_id):
"""ユーザー削除"""
users_table.delete_item(Key={'userId': user_id})
return response(204, '')
# ========== Posts 関数 ==========
def get_posts():
"""投稿一覧取得"""
result = posts_table.scan()
items = convert_decimal(result.get('Items', []))
return response(200, items)
def get_post(post_id):
"""投稿取得"""
result = posts_table.get_item(Key={'postId': post_id})
if 'Item' not in result:
return response(404, {'message': 'Post not found'})
item = convert_decimal(result['Item'])
return response(200, item)
def create_post(data):
"""投稿作成"""
post_id = f"post-{int(datetime.now().timestamp() * 1000)}"
item = {
'postId': post_id,
'userId': data.get('userId', ''),
'title': data.get('title', ''),
'content': data.get('content', ''),
'createdAt': datetime.now().isoformat()
}
posts_table.put_item(Item=item)
return response(201, item)
def update_post(post_id, data):
"""投稿更新"""
# 既存投稿の確認
result = posts_table.get_item(Key={'postId': post_id})
if 'Item' not in result:
return response(404, {'message': 'Post not found'})
# 更新
existing_item = result['Item']
existing_item.update(data)
existing_item['updatedAt'] = datetime.now().isoformat()
posts_table.put_item(Item=existing_item)
item = convert_decimal(existing_item)
return response(200, item)
def delete_post(post_id):
"""投稿削除"""
posts_table.delete_item(Key={'postId': post_id})
return response(204, '')
# ========== ユーティリティ関数 ==========
def response(status_code, body):
"""HTTP レスポンス生成"""
return {
'statusCode': status_code,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps(body, ensure_ascii=False) if body != '' else ''
}
def convert_decimal(obj):
"""Decimal を int/float に変換(DynamoDB の数値型対応)"""
if isinstance(obj, list):
return [convert_decimal(item) for item in obj]
elif isinstance(obj, dict):
return {key: convert_decimal(value) for key, value in obj.items()}
elif isinstance(obj, Decimal):
return int(obj) if obj % 1 == 0 else float(obj)
else:
return obj
zip function.zip lambda_function.py
Lambda 関数デプロイ(実行ロールのARN、ハンドラ名、Lambda関数ZIPファイル名 指定)
aws lambda create-function \
--function-name my-api-function \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/lambda-api-execution-role \
--handler lambda_function.lambda_handler \
--zip-file fileb://function.zip \
--timeout 30 \
--memory-size 256 \
--region ap-northeast-1
API GatewayがLambda関数を呼び出すことを許可するリソースベースポリシーを作成する
(Lambda関数名、API GatewayのARN 指定)
aws lambda add-permission \
--function-name my-api-function \
--statement-id apigateway-invoke-permission \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:ap-northeast-1:123456789012:abc123xyz/*/*" \
--region ap-northeast-1
API Gateway を作成する
| プロトコルタイプ | API Gateway 作成コマンド | 特徴 |
|---|---|---|
| HTTP API | aws apigatewayv2 | RESTful API 使用可 コストが安い APIキー使用不可 キャッシュ使用不可 ステージ変数使用不可 JWT認証使用可 |
| REST API | aws apigateway | RESTful API 使用可 コストが高い APIキー使用可 キャッシュ使用可 ステージ変数使用可 JWT認証使用不可 |
aws apigatewayv2 create-api \
--name my-http-api \
--protocol-type HTTP
API Gateway を表示する
aws apigatewayv2 get-apis
API Gateway を削除する
aws apigatewayv2 delete-api \
--api-id xxxxxxxx
統合を作成する(API ID、LambdaのARN 指定)
| 統合方式 | 統合先 |
|---|---|
| AWS_PROXY(Lambda プロキシ統合) | Lambda |
| HTTP_PROXY (HTTP プロキシ統合) | ALB、NLB、EC2、外部API |
| AWS(非プロキシ統合) | DynamoDB、SQS、EventBridge、Step Functions |
aws apigatewayv2 create-integration \
--api-id xxxxxxxx \
--integration-type AWS_PROXY \
--integration-uri arn:aws:lambda:ap-northeast-1:123456789012:function:my-lambda-function \
--payload-format-version 2.0
統合を表示する(API ID 指定)
aws apigatewayv2 get-integrations \
--api-id xxxxxxxx
統合を削除する(API ID、 Integration ID 指定)
aws apigatewayv2 delete-integration \
--api-id xxxxxxxx \
--integration-id int123abc
ルートを作成する
| 操作 | メソッド | ルート | 説明 |
|---|---|---|---|
| Create | POST | /users | ユーザ作成 |
| Read | GET | /users | ユーザ一覧取得 |
| Read | GET | /users/{id} | ユーザ取得 |
| Update | PATCH | /users/{id} | ユーザ情報更新 |
| Delete | DELETE | /users/{id} | ユーザ削除 |
ユーザ作成 (POST /users)(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "POST /users" \
--target "integrations/int123abc"
ユーザ一覧取得 (GET /users)(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "GET /users" \
--target "integrations/int123abc"
ユーザ取得 (GET /users/{id})(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "GET /users/{id}" \
--target "integrations/int123abc"
ユーザ情報更新 (PATCH /users/{id})(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "PATCH /users/{id}" \
--target "integrations/int123abc"
ユーザ削除 (DELETE /users/{id})(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "DELETE /users/{id}" \
--target "integrations/int123abc"
| 操作 | メソッド | ルート | 説明 |
|---|---|---|---|
| Create | POST | /posts | 投稿作成 |
| Read | GET | /posts | 投稿一覧取得 |
| Read | GET | /posts/{id} | 投稿取得 |
| Update | PATCH | /posts/{id} | 投稿更新 |
| Delete | DELETE | /posts/{id} | 投稿削除 |
投稿作成 (POST /posts)(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "POST /posts" \
--target "integrations/int123abc"
投稿一覧取得 (GET /posts)(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "GET /posts" \
--target "integrations/int123abc"
投稿取得 (GET /posts/{id})(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "GET /posts/{id}" \
--target "integrations/int123abc"
投稿情報更新 (PATCH /posts/{id})(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "PATCH /posts/{id}" \
--target "integrations/int123abc"
投稿削除 (DELETE /posts/{id})(API ID、統合 ID 指定)
aws apigatewayv2 create-route \
--api-id abc123xyz \
--route-key "DELETE /posts/{id}" \
--target "integrations/int123abc"
ルートを表示する(API ID)
aws apigatewayv2 get-routes \
--api-id abc123xyz
ルートを削除する(API ID、 ルート ID 指定)
aws apigatewayv2 delete-route \
--api-id abc123xyz \
--route-id rrrrrrrr
ステージを作成する
| プロトコルタイプ | 環境分離のベストプラクティス | 理由 |
|---|---|---|
| HTTP API | API Gateway ごと分離 | ステージ変数が使えない |
| REST API | API Gateway を複数ステージに分離 | ステージ変数が使える |
環境分離は行わない
CloudFormationのサンプルコード
Terraformのサンプルコード
料金計算
| 課金項目 | 説明 |
|---|---|
| APIコール数 | 受信したAPIコール数(百万件単位) |
| データ転送 | API Gatewayからのデータ転送量 |
| キャッシング | API応答のキャッシング(時間課金) |
| WebSocket | WebSocket接続の接続時間とメッセージ数 |