API Gateway

【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が実行ロールを引き受ける典型的な連携パターン
実行ロールを介して連携するサービス実行ロールにアタッチするポリシー
S3s3:ListBucket
s3:GetObject
s3:PutObject
s3:DeleteObject
DynamoDBdynamodb:GetItem
dynamodb:PutItem
dynamodb:UpdateItem
dynamodb:DeleteItem
dynamodb:Query
dynamodb:Scan
SQS(送信)sqs:SendMessage
sqs:SendMessageBatch
SNSsns:Publish
EventBridgeevents:PutEvents
Step Functionsstates:StartExecution
Kinesis Data Streamskinesis:PutRecord
kinesis:PutRecords
Lambdalambda:InvokeFunction
※)権限設計の原則

信頼ポリシー: API Gateway

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

ログ・監視

ログ出力先ログの種類
CloudWatch Logsアクセスログ/実行ログ
CloudWatch Logs優先の原則
標準メトリクス
メトリクス名説明
CacheHitCountキャッシュヒット数
CacheMissCountキャッシュミス数
ConnectCountWebSocket接続数
Countリクエスト総数
IntegrationLatency統合レイテンシ
Latencyレイテンシ
MessageCountWebSocketメッセージ数
4XXError4xxエラー数
5XXError5xxエラー数
ClientErrorクライアントエラー数
ExecutionError実行エラー数
IntegrationError統合エラー数
DataProcessed処理データ量

制限値(固定値/ハードリミット/ソフトリミット)

ハードリミット制限値
統合タイムアウト29秒
ペイロードサイズ10 MB
メソッド数/リソース20

ソフトリミット制限値
REST APIのスロットル10,000 RPS
WebSocket接続500,000(同時)、3,000メッセージ/秒
ステージ数/API10
リソース数/API300
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 APIaws apigatewayv2RESTful API 使用可
コストが安い
APIキー使用不可
キャッシュ使用不可
ステージ変数使用不可
JWT認証使用可
REST APIaws apigatewayRESTful 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

ルートを作成する
操作メソッドルート説明
CreatePOST/usersユーザ作成
ReadGET/usersユーザ一覧取得
ReadGET/users/{id}ユーザ取得
UpdatePATCH/users/{id}ユーザ情報更新
DeleteDELETE/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"

操作メソッドルート説明
CreatePOST/posts投稿作成
ReadGET/posts投稿一覧取得
ReadGET/posts/{id}投稿取得
UpdatePATCH/posts/{id}投稿更新
DeleteDELETE/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 APIAPI Gateway ごと分離ステージ変数が使えない
REST APIAPI Gateway を複数ステージに分離ステージ変数が使える
環境分離は行わない

CloudFormationのサンプルコード

Terraformのサンプルコード

料金計算

課金項目説明
APIコール数受信したAPIコール数(百万件単位)
データ転送API Gatewayからのデータ転送量
キャッシングAPI応答のキャッシング(時間課金)
WebSocketWebSocket接続の接続時間とメッセージ数
料金計算ツール

公式ページ

AWSドキュメント API Gateway