AWS API Gateway

Fully managed API management service. Supports REST and WebSocket APIs, integrates auto-scaling, monitoring, and security features. Serverless compatible.

API GatewayAWSServerlessREST APIWebSocketHTTP API

Server

AWS API Gateway

Overview

AWS API Gateway is a fully managed service that enables developers to create, publish, maintain, monitor, and secure APIs at any scale, serving as the "front door" for applications to access data, business logic, or functionality from backend services. As a comprehensive API management platform, AWS API Gateway supports REST APIs, HTTP APIs, and WebSocket APIs, providing automatic scaling, built-in security features, and seamless integration with the broader AWS ecosystem. Designed for serverless architectures and cloud-native applications, API Gateway eliminates the operational overhead of managing API infrastructure while offering enterprise-grade features for authentication, authorization, traffic management, and monitoring.

Details

AWS API Gateway 2025 edition continues to evolve as a cornerstone service for modern application architectures, particularly in serverless and microservices environments. The platform offers three distinct API types: REST APIs for full-featured API management, HTTP APIs for cost-effective serverless workloads (up to 71% cheaper), and WebSocket APIs for real-time bidirectional communication. Built on AWS's global infrastructure, API Gateway provides automatic scaling to handle any traffic volume, regional and edge-optimized endpoints for low latency, and deep integration with AWS services like Lambda, DynamoDB, SNS, and Step Functions. The service includes comprehensive security features including IAM integration, custom authorizers, API keys, usage plans, and AWS WAF integration for advanced threat protection.

Key Features

  • Multiple API Types: REST, HTTP, and WebSocket APIs for different use cases
  • Serverless Integration: Native integration with AWS Lambda and serverless architectures
  • Automatic Scaling: Handles traffic spikes without manual intervention or capacity planning
  • Security and Authorization: IAM, Cognito, Lambda authorizers, and API key management
  • Traffic Management: Throttling, caching, CORS support, and request/response transformation
  • Monitoring and Analytics: CloudWatch integration, X-Ray tracing, and access logging

Advantages and Disadvantages

Advantages

  • Fully managed service eliminating infrastructure management and operational overhead
  • Automatic scaling and high availability with AWS global infrastructure and SLA guarantees
  • Pay-per-use pricing model with no upfront costs or minimum fees for cost-effective scaling
  • Seamless AWS ecosystem integration enabling easy connection to Lambda, DynamoDB, and other services
  • Enterprise-grade security features with IAM, WAF, and custom authentication mechanisms
  • Comprehensive monitoring and analytics through CloudWatch and X-Ray for operational insights

Disadvantages

  • AWS vendor lock-in limiting portability to other cloud providers or on-premises deployments
  • Cost accumulation with high-volume APIs potentially becoming expensive at scale
  • Limited customization compared to self-hosted solutions with restricted configuration options
  • Cold start latency issues when integrating with Lambda functions affecting response times
  • Regional limitations and compliance considerations for data residency requirements
  • Complex pricing model with multiple factors making cost prediction challenging for complex scenarios

Reference Links

Code Examples

REST API Creation and Deployment

# Create REST API using AWS CLI
aws apigateway create-rest-api \
    --name "ProductAPI" \
    --description "REST API for product management" \
    --endpoint-configuration types=REGIONAL

# Get API ID from response
API_ID="your-api-id"

# Get root resource ID
ROOT_ID=$(aws apigateway get-resources \
    --rest-api-id $API_ID \
    --query 'items[?path==`/`].id' \
    --output text)

# Create resource
aws apigateway create-resource \
    --rest-api-id $API_ID \
    --parent-id $ROOT_ID \
    --path-part "products"

# Get products resource ID
PRODUCTS_ID=$(aws apigateway get-resources \
    --rest-api-id $API_ID \
    --query 'items[?path==`/products`].id' \
    --output text)

# Create GET method
aws apigateway put-method \
    --rest-api-id $API_ID \
    --resource-id $PRODUCTS_ID \
    --http-method GET \
    --authorization-type "AWS_IAM"

CloudFormation Template for REST API

# template.yml - Complete REST API with Lambda integration
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  StageName:
    Type: String
    Default: prod
    Description: API Gateway stage name

Resources:
  # Lambda function
  ProductsFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: products.handler
      Runtime: python3.9
      Environment:
        Variables:
          TABLE_NAME: !Ref ProductsTable
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ProductsTable

  # DynamoDB table
  ProductsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH

  # API Gateway REST API
  ProductsApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref StageName
      Cors:
        AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigin: "'*'"
      Auth:
        DefaultAuthorizer: AWS_IAM
      GatewayResponses:
        DEFAULT_4XX:
          ResponseTemplates:
            "application/json": '{"message":$context.error.messageString}'
        DEFAULT_5XX:
          ResponseTemplates:
            "application/json": '{"message":"Internal server error"}'

  # API Gateway Resource
  ProductsResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref ProductsApi
      ParentId: !GetAtt ProductsApi.RootResourceId
      PathPart: products

  # GET method
  GetProductsMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref ProductsApi
      ResourceId: !Ref ProductsResource
      HttpMethod: GET
      AuthorizationType: AWS_IAM
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProductsFunction.Arn}/invocations'
      MethodResponses:
        - StatusCode: 200
          ResponseModels:
            application/json: Empty

  # POST method
  CreateProductMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref ProductsApi
      ResourceId: !Ref ProductsResource
      HttpMethod: POST
      AuthorizationType: AWS_IAM
      RequestValidatorId: !Ref RequestValidator
      RequestModels:
        application/json: !Ref ProductModel
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProductsFunction.Arn}/invocations'

  # Request validator
  RequestValidator:
    Type: AWS::ApiGateway::RequestValidator
    Properties:
      RestApiId: !Ref ProductsApi
      ValidateRequestBody: true
      ValidateRequestParameters: true

  # Model for request validation
  ProductModel:
    Type: AWS::ApiGateway::Model
    Properties:
      RestApiId: !Ref ProductsApi
      ContentType: application/json
      Schema:
        $schema: http://json-schema.org/draft-04/schema#
        type: object
        properties:
          name:
            type: string
            minLength: 1
          price:
            type: number
            minimum: 0
          description:
            type: string
        required:
          - name
          - price

Outputs:
  ApiUrl:
    Description: "API Gateway endpoint URL"
    Value: !Sub "https://${ProductsApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}"

HTTP API (Serverless-optimized)

# http-api.yml - Cost-effective HTTP API
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  # HTTP API (cheaper alternative to REST API)
  HttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: prod
      CorsConfiguration:
        AllowOrigins:
          - "https://myapp.example.com"
        AllowHeaders:
          - Content-Type
          - Authorization
        AllowMethods:
          - GET
          - POST
          - PUT
          - DELETE
        MaxAge: 600
        AllowCredentials: true
      Auth:
        Authorizers:
          JwtAuthorizer:
            JwtConfiguration:
              issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_example"
              audience:
                - "client-id-1"
                - "client-id-2"
            IdentitySource: "$request.header.Authorization"
        DefaultAuthorizer: JwtAuthorizer

  # Lambda function for HTTP API
  SimpleFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.9
      Events:
        GetProducts:
          Type: HttpApi
          Properties:
            ApiId: !Ref HttpApi
            Method: GET
            Path: /products
        CreateProduct:
          Type: HttpApi
          Properties:
            ApiId: !Ref HttpApi
            Method: POST
            Path: /products

WebSocket API Implementation

# websocket-api.yml - Real-time WebSocket API
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  # WebSocket API
  ChatApi:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: ChatWebSocketAPI
      ProtocolType: WEBSOCKET
      RouteSelectionExpression: "$request.body.action"

  # Connection handler
  ConnectFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: websocket/
      Handler: connect.handler
      Runtime: python3.9
      Environment:
        Variables:
          TABLE_NAME: !Ref ConnectionsTable
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ConnectionsTable

  # Disconnect handler
  DisconnectFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: websocket/
      Handler: disconnect.handler
      Runtime: python3.9
      Environment:
        Variables:
          TABLE_NAME: !Ref ConnectionsTable
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ConnectionsTable

  # Message handler
  MessageFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: websocket/
      Handler: message.handler
      Runtime: python3.9
      Environment:
        Variables:
          TABLE_NAME: !Ref ConnectionsTable
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ConnectionsTable
        - Statement:
            - Effect: Allow
              Action:
                - 'execute-api:ManageConnections'
              Resource: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ChatApi}/*'

  # DynamoDB table for connections
  ConnectionsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: connectionId
          AttributeType: S
      KeySchema:
        - AttributeName: connectionId
          KeyType: HASH

  # WebSocket routes
  ConnectRoute:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref ChatApi
      RouteKey: $connect
      AuthorizationType: NONE
      OperationName: ConnectRoute
      Target: !Join
        - '/'
        - - 'integrations'
          - !Ref ConnectInteg

  DisconnectRoute:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref ChatApi
      RouteKey: $disconnect
      AuthorizationType: NONE
      OperationName: DisconnectRoute
      Target: !Join
        - '/'
        - - 'integrations'
          - !Ref DisconnectInteg

  MessageRoute:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref ChatApi
      RouteKey: sendmessage
      AuthorizationType: NONE
      OperationName: MessageRoute
      Target: !Join
        - '/'
        - - 'integrations'
          - !Ref MessageInteg

  # Integrations
  ConnectInteg:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref ChatApi
      Description: Connect Integration
      IntegrationType: AWS_PROXY
      IntegrationUri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ConnectFunction.Arn}/invocations'

  DisconnectInteg:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref ChatApi
      Description: Disconnect Integration
      IntegrationType: AWS_PROXY
      IntegrationUri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${DisconnectFunction.Arn}/invocations'

  MessageInteg:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref ChatApi
      Description: Message Integration
      IntegrationType: AWS_PROXY
      IntegrationUri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MessageFunction.Arn}/invocations'

  # Deployment and Stage
  Deployment:
    Type: AWS::ApiGatewayV2::Deployment
    DependsOn:
      - ConnectRoute
      - DisconnectRoute
      - MessageRoute
    Properties:
      ApiId: !Ref ChatApi

  Stage:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      StageName: prod
      Description: Production Stage
      DeploymentId: !Ref Deployment
      ApiId: !Ref ChatApi

Outputs:
  WebSocketURI:
    Description: "WebSocket API URI"
    Value: !Sub "wss://${ChatApi}.execute-api.${AWS::Region}.amazonaws.com/prod"

Lambda Authorizer Implementation

# authorizer.py - Custom Lambda authorizer
import json
import jwt
from jwt.exceptions import InvalidTokenError

def lambda_handler(event, context):
    """
    Custom authorizer for API Gateway
    """
    token = event['authorizationToken']
    method_arn = event['methodArn']
    
    try:
        # Remove 'Bearer ' prefix
        if token.startswith('Bearer '):
            token = token[7:]
        
        # Verify JWT token (replace with your secret/public key)
        payload = jwt.decode(
            token, 
            'your-secret-key', 
            algorithms=['HS256']
        )
        
        principal_id = payload.get('sub', 'user')
        
        # Generate policy
        policy = generate_policy(principal_id, 'Allow', method_arn)
        
        # Add context information
        policy['context'] = {
            'userId': payload.get('sub'),
            'email': payload.get('email'),
            'role': payload.get('role', 'user')
        }
        
        return policy
        
    except InvalidTokenError:
        # Return 401 Unauthorized
        raise Exception('Unauthorized')
    
    except Exception as e:
        print(f"Error: {str(e)}")
        raise Exception('Unauthorized')

def generate_policy(principal_id, effect, resource):
    """
    Generate IAM policy for API Gateway
    """
    auth_response = {
        'principalId': principal_id
    }
    
    if effect and resource:
        policy_document = {
            'Version': '2012-10-17',
            'Statement': [
                {
                    'Action': 'execute-api:Invoke',
                    'Effect': effect,
                    'Resource': resource
                }
            ]
        }
        auth_response['policyDocument'] = policy_document
    
    return auth_response

Advanced Request/Response Transformation

# API Gateway with VTL transformations
ProductDetailMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    RestApiId: !Ref ProductsApi
    ResourceId: !Ref ProductDetailResource
    HttpMethod: GET
    AuthorizationType: AWS_IAM
    RequestParameters:
      method.request.path.id: true
    Integration:
      Type: AWS
      IntegrationHttpMethod: POST
      Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:dynamodb:action/GetItem'
      Credentials: !GetAtt ApiGatewayRole.Arn
      RequestTemplates:
        application/json: |
          {
            "TableName": "${ProductsTable}",
            "Key": {
              "id": {
                "S": "$input.params('id')"
              }
            }
          }
      ResponseTemplates:
        application/json: |
          #set($item = $input.path('$.Item'))
          #if($item.toString() != "{}")
          {
            "id": "$item.id.S",
            "name": "$item.name.S",
            "price": $item.price.N,
            "description": "$item.description.S",
            "createdAt": "$item.createdAt.S"
          }
          #else
          #set($context.responseOverride.status = 404)
          {
            "error": "Product not found"
          }
          #end
      IntegrationResponses:
        - StatusCode: 200
        - StatusCode: 404
          SelectionPattern: '.*"error".*'
    MethodResponses:
      - StatusCode: 200
        ResponseModels:
          application/json: !Ref ProductModel
      - StatusCode: 404
        ResponseModels:
          application/json: !Ref ErrorModel

Monitoring and Logging Setup

# CloudWatch and X-Ray configuration
ApiGatewayAccount:
  Type: AWS::ApiGateway::Account
  Properties:
    CloudWatchRoleArn: !GetAtt CloudWatchRole.Arn

CloudWatchRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            Service: apigateway.amazonaws.com
          Action: sts:AssumeRole
    Policies:
      - PolicyName: CloudWatchPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:DescribeLogGroups
                - logs:DescribeLogStreams
                - logs:PutLogEvents
                - logs:GetLogEvents
                - logs:FilterLogEvents
              Resource: "*"

# API Gateway stage with logging
ApiStage:
  Type: AWS::ApiGateway::Stage
  Properties:
    RestApiId: !Ref ProductsApi
    DeploymentId: !Ref ApiDeployment
    StageName: prod
    TracingConfig:
      TracingEnabled: true
    AccessLogSetting:
      DestinationArn: !Sub '${ApiLogGroup.Arn}'
      Format: |
        {
          "requestId": "$context.requestId",
          "ip": "$context.identity.sourceIp",
          "user": "$context.identity.user",
          "requestTime": "$context.requestTime",
          "httpMethod": "$context.httpMethod",
          "resourcePath": "$context.resourcePath",
          "status": "$context.status",
          "protocol": "$context.protocol",
          "responseLength": "$context.responseLength",
          "responseTime": "$context.responseTime",
          "error": "$context.error.message",
          "integrationError": "$context.integration.error"
        }
    MethodSettings:
      - ResourcePath: /*
        HttpMethod: "*"
        LoggingLevel: INFO
        DataTraceEnabled: true
        MetricsEnabled: true

ApiLogGroup:
  Type: AWS::Logs::LogGroup
  Properties:
    LogGroupName: !Sub '/aws/apigateway/${ProductsApi}'
    RetentionInDays: 14

Usage Plans and API Keys

# API Key and Usage Plan configuration
ApiKey:
  Type: AWS::ApiGateway::ApiKey
  Properties:
    Name: ProductAPIKey
    Description: API Key for Products API
    Enabled: true
    GenerateDistinctId: true

UsagePlan:
  Type: AWS::ApiGateway::UsagePlan
  Properties:
    UsagePlanName: ProductAPIUsagePlan
    Description: Usage plan for Products API
    ApiStages:
      - ApiId: !Ref ProductsApi
        Stage: prod
    Throttle:
      RateLimit: 100
      BurstLimit: 200
    Quota:
      Limit: 10000
      Period: MONTH

UsagePlanKey:
  Type: AWS::ApiGateway::UsagePlanKey
  Properties:
    KeyId: !Ref ApiKey
    KeyType: API_KEY
    UsagePlanId: !Ref UsagePlan