Skip to content

Latest commit

 

History

History
354 lines (260 loc) · 10.5 KB

File metadata and controls

354 lines (260 loc) · 10.5 KB

API Gateway

What Is It?

API Gateway is a fully managed API front-door. It handles request routing, auth, rate limiting, caching, and transformation — so your backend doesn't have to.

Real-World: Your mobile app calls https://api.example.com/orders. API Gateway receives it, validates the JWT token, rate-limits the user, transforms the request, forwards to Lambda, transforms the response, and returns it — all configured in JSON, no server maintained.


API Types

Type Protocol Use Case
REST API HTTP/HTTPS Traditional APIs, full feature set
HTTP API HTTP/HTTPS Simpler, cheaper (70% cost reduction), less features
WebSocket API WebSocket Real-time bidirectional (chat, live data)

REST API vs HTTP API — When to Use Which

Feature REST API HTTP API
Usage plans + API keys Yes No
Request/response transformation Yes No
AWS WAF integration Yes No
Resource policies Yes No
X-Ray tracing Yes No
Caching Yes No
Cost Higher 71% cheaper
Lambda Proxy Yes Yes
JWT Authorizer No Yes (native)
OIDC/OAuth2 No Yes (native)

Rule of thumb: Use HTTP API for simple Lambda/HTTP backends. Use REST API when you need caching, WAF, usage plans, or complex transformations.


Integration Types

Lambda Proxy Integration (Most Common)

API Gateway passes the entire request to Lambda as an event. Lambda must return the full HTTP response format.

// Lambda receives this event
{
  "httpMethod": "POST",
  "path": "/orders",
  "headers": {"Authorization": "Bearer eyJ..."},
  "pathParameters": {"orderId": "123"},
  "queryStringParameters": {"status": "pending"},
  "body": "{\"item\": \"laptop\"}",
  "requestContext": {...}
}
# Lambda must return this exact structure
def handler(event, context):
    return {
        'statusCode': 200,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps({'orderId': '123', 'status': 'created'})
    }

AWS Service Integration

API Gateway calls AWS services directly (no Lambda needed):

POST /orders → API Gateway → DynamoDB PutItem (directly)

Real-World: Simple data submission form → API Gateway → DynamoDB. No Lambda needed, reduces cost and latency.

HTTP Integration

Forward to existing HTTP backend (on-premise or other AWS services):

GET /products → API Gateway → ECS container service

Authorization

1. IAM Authorization

Uses AWS SigV4. Best for service-to-service communication within AWS.

AWS SDK (signs request) → API Gateway (validates SigV4) → Lambda

2. Cognito User Pool Authorizer

Validates JWT from Cognito. Built-in, no Lambda needed.

Mobile App → Cognito (login) → JWT token
           → API Gateway (validates JWT against Cognito) → Lambda

3. Lambda Authorizer (Custom Authorizer)

Lambda validates your custom token and returns an IAM policy.

Request with Bearer token → API Gateway → Lambda Authorizer → 
  IAM Policy {Allow/Deny} → API Gateway caches policy for N seconds → 
  Routes to backend
def authorizer_handler(event, context):
    token = event['authorizationToken']
    
    # Validate token with your auth server
    user = validate_token(token)
    
    if user:
        return generate_policy(user['id'], 'Allow', event['methodArn'])
    else:
        return generate_policy('unknown', 'Deny', event['methodArn'])

def generate_policy(principal_id, effect, resource):
    return {
        'principalId': principal_id,
        'policyDocument': {
            'Version': '2012-10-17',
            'Statement': [{
                'Action': 'execute-api:Invoke',
                'Effect': effect,
                'Resource': resource
            }]
        },
        'context': {  # Passed to Lambda backend
            'userId': principal_id
        }
    }

Lambda Authorizer caching: Set TTL (0-3600s). Cache key = authorization token. Reduces Lambda invocations and latency.


Stages and Deployments

API Gateway API
├── Stage: dev    → Lambda alias: dev
├── Stage: staging → Lambda alias: staging
└── Stage: prod   → Lambda alias: prod

Stage Variables: Like environment variables for stages.

Stage: prod
stageVariable: lambdaAlias = prod

Integration URI: arn:aws:lambda:...:function:myFunc:${stageVariables.lambdaAlias}

Changing lambdaAlias in stage variables changes which Lambda version is called — no redeployment of API needed.


Canary Deployments

API Gateway supports canary releases without needing CodeDeploy:

prod stage: 90% traffic → current deployment
            10% traffic → canary deployment

Test new API config with 10% of traffic, then promote or rollback.


Caching

REST API only. Cache responses to reduce Lambda invocations.

Client → API Gateway (cache hit? return cached) → Lambda (only on cache miss)
  • Cache size: 0.5GB – 237GB
  • TTL: 0 – 3600 seconds (default 300)
  • Cache key: method + path + optional query params/headers

Real-World: Product catalog API — same product details called millions of times. Cache for 5 minutes = 99%+ cache hit rate = 99% fewer Lambda calls.

Invalidation: Client sends Cache-Control: max-age=0 header (needs IAM permission execute-api:InvalidateCache).


Throttling & Usage Plans

Account-Level Limits

  • Default: 10,000 requests/second per account (soft limit)
  • Burst: 5,000 requests (Token Bucket algorithm)

Usage Plans (REST API only)

Control per API key:

Usage Plan: free-tier
  - Throttle: 10 req/sec
  - Quota: 1,000 req/day

Usage Plan: premium
  - Throttle: 100 req/sec
  - Quota: 100,000 req/day
Client → API Gateway → check API key → lookup usage plan → enforce limits

Real-World: SaaS product with free/paid tiers. Free users get 1,000 API calls/day. Paid users get unlimited.


Request/Response Transformation (REST API)

Mapping Templates (VTL — Apache Velocity) transform requests before hitting backend and responses before returning to client.

Use Case: API accepts one format, backend expects another.

## Transform frontend JSON to DynamoDB PutItem format
#set($inputRoot = $input.path('$'))
{
  "TableName": "orders",
  "Item": {
    "orderId": {"S": "$inputRoot.id"},
    "userId": {"S": "$inputRoot.user"},
    "total": {"N": "$inputRoot.total"}
  }
}

WebSocket API

Persistent bidirectional connections. API Gateway manages connection state.

Client connects → connectionId assigned
Client sends message → route based on routeKey 
Server sends message → API Gateway Management API 
Client disconnects

Route selection: Based on JSON body field (e.g., action).

{"action": "sendMessage", "data": "Hello"}
→ routes to $connect, $disconnect, sendMessage, $default

Sending to connected clients from Lambda:

apigw_client = boto3.client('apigatewaymanagementapi',
    endpoint_url=f"https://{domain}/{stage}")

apigw_client.post_to_connection(
    ConnectionId=connection_id,
    Data=json.dumps({"message": "Hello from server"})
)

Real-World: Collaborative editing, live chat, real-time dashboards, multiplayer gaming.


CORS Configuration

For browser-based clients:

{
  "allowOrigins": ["https://app.example.com"],
  "allowMethods": ["GET", "POST", "PUT", "DELETE"],
  "allowHeaders": ["Authorization", "Content-Type"],
  "maxAge": 3600
}

API Gateway handles OPTIONS preflight requests automatically when CORS is configured.


Good Practices

Practice Reason
Use Lambda Proxy integration Simpler, full request/response control
Cache with appropriate TTL Reduce Lambda invocations and cost
Use usage plans with API keys Rate limiting per client
Use Cognito authorizer for user auth Managed, no custom Lambda needed
Use Lambda Authorizer for custom auth Token from non-AWS providers
Enable X-Ray tracing End-to-end tracing across API → Lambda
Use stages with stage variables Avoid hardcoding Lambda ARN in integration
Set 4xx/5xx CloudWatch alarms Detect errors before users complain

Bad Practices

Anti-Pattern Impact Fix
No throttling One bad client takes down your API Enable usage plans + throttling
Caching sensitive/user-specific data User A sees User B's data Don't cache user-specific responses; or include user ID in cache key
Lambda Authorizer with TTL=0 Auth Lambda called every request = latency + cost Set reasonable TTL (300s)
Exposing internal error messages Information leakage Use Gateway Response to customize 4xx/5xx
Using REST API when HTTP API is enough 3x cost for no benefit Start with HTTP API

Exam Tips

  1. 29-second timeout — hard limit. If Lambda takes longer, API Gateway returns 504 (Gateway Timeout). Can't be increased.
  2. 10MB payload limit — max request/response body size.
  3. HTTP API is NOT the same as REST API — HTTP API lacks WAF, caching, usage plans.
  4. Lambda Proxy vs Lambda Custom: Proxy = entire HTTP request to Lambda. Custom = mapping template transforms.
  5. Edge-Optimized vs Regional vs Private:
    • Edge-Optimized: CloudFront in front, global users
    • Regional: deployed in specific region, same-region clients
    • Private: only accessible within VPC
  6. Stages need deployment — changes to API don't go live until you deploy to a stage.
  7. CORS must be enabled on API Gateway AND Lambda must return CORS headers (for Lambda Proxy).
  8. API Keys alone are NOT security — they're for throttling/monitoring, not auth. Use + IAM/Cognito/Lambda Authorizer for real auth.

Common Exam Scenarios

Q: API returns 504 error occasionally under heavy load? → Lambda timeout exceeded. Optimize Lambda or return response asynchronously (SQS pattern).

Q: How to build a real-time chat with API Gateway? → Use WebSocket API + Lambda + DynamoDB (store connection IDs).

Q: Different clients need different rate limits?Usage Plans + API Keys.

Q: Third-party OAuth2 provider for auth?Lambda Authorizer or HTTP API's native JWT Authorizer.

Q: Reduce costs on a read-heavy product catalog API? → Enable API Gateway caching with appropriate TTL.

Q: Call DynamoDB directly from API Gateway without Lambda? → Use AWS Service Integration with Mapping Templates.