AWS API Gateway: Choosing REST, HTTP, or WebSocket APIs

AWS offers three distinct API Gateway types, and choosing incorrectly leads to either unnecessary costs, missing features, or architectural dead ends. After building dozens of production APIs, this guide provides a decision framework based on real requirements, performance benchmarks, and cost analysis. We will deep-dive into when REST APIs justify their premium, when HTTP APIs are the clear winner, and how WebSocket APIs enable real-time bidirectional communication.

Feature Comparison Matrix

FeatureREST APIHTTP APIWebSocket API
Pricing (per million)$3.50$1.00$1.00 + $0.25/M minutes
Latency (p99)~29ms~10msN/A (persistent)
Request Validation✅ Built-in
API Keys & Usage Plans
Caching✅ (per stage)N/A
WAF Integration✅ (limited)
Private Integrations✅ VPC Link✅ VPC Link
JWT AuthorizersLambda only✅ NativeLambda only

Decision Tree

flowchart TB
    Start["API Gateway Needed"] --> Realtime{Real-time bidirectional?}
    
    Realtime -->|Yes| WebSocket["WebSocket API"]
    Realtime -->|No| Features{Need caching, API keys, or request validation?}
    
    Features -->|Yes| REST["REST API"]
    Features -->|No| JWT{Using OAuth/JWT auth?}
    
    JWT -->|Yes| HTTP["HTTP API (Native JWT)"]
    JWT -->|No| Cost{Cost sensitive?}
    
    Cost -->|Yes| HTTP
    Cost -->|No| REST
    
    style REST fill:#FFF3E0,stroke:#E65100
    style HTTP fill:#C8E6C9,stroke:#2E7D32
    style WebSocket fill:#E1F5FE,stroke:#0277BD

REST API: When Features Justify Cost

REST APIs cost 3.5x more than HTTP APIs. This is justified when you need:

Request Validation

REST APIs can validate request bodies against JSON Schema before invoking Lambda:

# SAM template
CreateOrderValidator:
  Type: AWS::ApiGateway::RequestValidator
  Properties:
    RestApiId: !Ref Api
    ValidateRequestBody: true
    ValidateRequestParameters: true

CreateOrderModel:
  Type: AWS::ApiGateway::Model
  Properties:
    RestApiId: !Ref Api
    ContentType: application/json
    Schema:
      type: object
      required: [customerId, items]
      properties:
        customerId:
          type: string
          pattern: "^CUST-[0-9]+$"
        items:
          type: array
          minItems: 1

API Caching

Enable caching to reduce Lambda invocations for read-heavy endpoints:

resource "aws_api_gateway_method_settings" "cache" {
  rest_api_id = aws_api_gateway_rest_api.main.id
  stage_name  = aws_api_gateway_stage.prod.stage_name
  method_path = "products/GET"
  
  settings {
    caching_enabled      = true
    cache_ttl_in_seconds = 300
  }
}

HTTP API: The Default Choice

For most APIs, HTTP API is the right choice. It is cheaper, faster, and has native JWT support.

Native JWT Authorization

HttpApi:
  Type: AWS::Serverless::HttpApi
  Properties:
    Auth:
      DefaultAuthorizer: OAuth2Authorizer
      Authorizers:
        OAuth2Authorizer:
          AuthorizationScopes:
            - orders.read
            - orders.write
          JwtConfiguration:
            issuer: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxx
            audience:
              - 1234567890abcdef

The JWT is validated at the API Gateway layer—no Lambda cold start for auth. This alone can reduce p99 latency by 100ms+.

WebSocket API: Real-Time Applications

WebSocket APIs maintain persistent connections, enabling bidirectional real-time communication. Use cases: chat, live dashboards, gaming, IoT telemetry.

# Lambda handler for WebSocket $connect route
def connect_handler(event, context):
    connection_id = event['requestContext']['connectionId']
    
    # Store connection in DynamoDB
    table.put_item(Item={
        'connectionId': connection_id,
        'connectedAt': datetime.now().isoformat()
    })
    
    return {'statusCode': 200}

# Broadcasting to connected clients
def broadcast(message):
    apigw_management = boto3.client('apigatewaymanagementapi',
        endpoint_url=f"https://{api_id}.execute-api.{region}.amazonaws.com/{stage}")
    
    connections = table.scan()['Items']
    
    for conn in connections:
        try:
            apigw_management.post_to_connection(
                ConnectionId=conn['connectionId'],
                Data=json.dumps(message)
            )
        except apigw_management.exceptions.GoneException:
            # Connection closed, clean up
            table.delete_item(Key={'connectionId': conn['connectionId']})

Cost Analysis Example

Scenario: 10 million API requests/month

API TypeMonthly Cost
REST API$35.00
HTTP API$10.00
Savings$25.00/month (71%)

At scale (1 billion requests), the difference is $25,000/month.

Key Takeaways

  • Default to HTTP API unless you specifically need REST features
  • Use REST API for caching, request validation, API keys, and usage plans
  • Use WebSocket API for real-time bidirectional communication
  • HTTP API has native JWT support—no Lambda authorizer needed
  • At scale, HTTP API saves 71% compared to REST API

Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.