API Gateway
API Gateway is a fully managed service that creates, publishes, and manages APIs at any scale. It’s the front door for serverless applications — sitting between clients and your Lambda functions, HTTP backends, or AWS services.
API Types
Section titled “API Types”| Type | Protocol | Best For | Cost |
|---|---|---|---|
| HTTP API | HTTP | Simple Lambda/HTTP proxy, low latency, low cost | ~$1.00 per million requests |
| REST API | HTTP | Full feature set (usage plans, API keys, request validation, caching) | ~$3.50 per million requests |
| WebSocket API | WebSocket | Real-time bidirectional communication (chat, live updates) | Per message + connection minutes |
HTTP API is the recommended default — it’s cheaper, faster, and covers most use cases. Use REST API only when you need features like request validation, caching, or usage plans.
How It Works
Section titled “How It Works”Client ──HTTPS──► API Gateway ──► Integration (Lambda, HTTP, AWS service) │ ├── Authentication (IAM, Cognito, JWT, API key) ├── Request validation ├── Throttling / rate limiting ├── Caching (REST API only) └── Logging (CloudWatch)Creating an HTTP API with Lambda
Section titled “Creating an HTTP API with Lambda”The Lambda Function
Section titled “The Lambda Function”import json
def handler(event, context): method = event['requestContext']['http']['method'] path = event['requestContext']['http']['path']
if method == 'GET' and path == '/users': return { 'statusCode': 200, 'body': json.dumps([ {'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'} ]) }
return {'statusCode': 404, 'body': json.dumps({'error': 'Not found'})}Creating the API (CLI)
Section titled “Creating the API (CLI)”# Create HTTP API with Lambda integrationaws apigatewayv2 create-api \ --name my-api \ --protocol-type HTTP \ --target arn:aws:lambda:us-east-1:123456789012:function:my-function
# The output includes the API endpoint:# https://abc123.execute-api.us-east-1.amazonaws.comRoutes
Section titled “Routes”Routes map HTTP methods and paths to integrations:
# Create a routeaws apigatewayv2 create-route \ --api-id abc123 \ --route-key "GET /users"
# Route examples:# GET /users → list users# POST /users → create user# GET /users/{id} → get user by ID# DELETE /users/{id} → delete user# ANY /{proxy+} → catch-all (proxy everything to Lambda)The {proxy+} greedy path parameter forwards all requests to a single Lambda, which handles routing internally (common with frameworks like Express, FastAPI, Flask).
Stages
Section titled “Stages”Stages represent different environments for your API:
https://abc123.execute-api.us-east-1.amazonaws.com/dev → dev stagehttps://abc123.execute-api.us-east-1.amazonaws.com/staging → staging stagehttps://abc123.execute-api.us-east-1.amazonaws.com/prod → production stageEach stage can have:
- Stage variables — Key-value pairs (like environment variables for the API).
- Different Lambda aliases —
devstage →$LATEST,prodstage →productionalias. - Different throttling limits — Lower limits for dev, higher for prod.
HTTP APIs have an $default stage that serves at the root URL (no stage in the path).
Authentication and Authorization
Section titled “Authentication and Authorization”IAM Authorization
Section titled “IAM Authorization”Requests must be signed with AWS credentials (SigV4). Best for service-to-service calls within AWS.
JWT Authorizer (HTTP API)
Section titled “JWT Authorizer (HTTP API)”Validate JWTs from Cognito, Auth0, or any OIDC provider:
aws apigatewayv2 create-authorizer \ --api-id abc123 \ --authorizer-type JWT \ --name cognito-auth \ --identity-source '$request.header.Authorization' \ --jwt-configuration '{ "Audience": ["my-app-client-id"], "Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_abc123" }'Lambda Authorizer
Section titled “Lambda Authorizer”A custom Lambda function that validates tokens and returns an IAM policy:
def handler(event, context): token = event['headers'].get('authorization', '')
if validate_token(token): return { 'isAuthorized': True, 'context': {'userId': 'user_123', 'role': 'admin'} } return {'isAuthorized': False}The context object is passed to the backend Lambda — useful for sharing user info without re-validating.
API Keys and Usage Plans (REST API Only)
Section titled “API Keys and Usage Plans (REST API Only)”API keys identify callers; usage plans set rate limits per key:
# Create a usage planaws apigateway create-usage-plan --name "Basic" \ --throttle burstLimit=100,rateLimit=50 \ --quota limit=10000,period=MONTH
# Create an API keyaws apigateway create-api-key --name "partner-a" --enabled
# Associate key with usage planaws apigateway create-usage-plan-key \ --usage-plan-id abc123 --key-id xyz789 --key-type API_KEYClients pass the key in the x-api-key header. Note: API keys are for throttling and tracking, not authentication — they’re not a security mechanism.
Throttling and Rate Limiting
Section titled “Throttling and Rate Limiting”API Gateway has built-in throttling to protect backends:
| Level | What It Controls |
|---|---|
| Account level | 10,000 requests/second across all APIs (soft limit) |
| Stage level | Per-stage throttle (e.g. 5,000 rps for prod) |
| Route level | Per-route throttle (e.g. 100 rps for /expensive-operation) |
| Usage plan | Per-API-key throttle |
When throttled, clients receive a 429 Too Many Requests response.
Request and Response Transformation
Section titled “Request and Response Transformation”Request Validation (REST API)
Section titled “Request Validation (REST API)”Validate request bodies and parameters before they reach your backend:
{ "type": "object", "required": ["name", "email"], "properties": { "name": {"type": "string", "minLength": 1}, "email": {"type": "string", "format": "email"} }}Invalid requests get a 400 Bad Request — your Lambda never runs.
Response Mapping
Section titled “Response Mapping”Map Lambda output to HTTP responses:
Lambda returns: {"statusCode": 200, "body": "...", "headers": {...}} ↓API Gateway: HTTP 200 with body and headersFor HTTP APIs, Lambda must return the statusCode/body/headers structure. API Gateway passes it through directly.
Cross-Origin Resource Sharing configuration for browser-based clients:
# HTTP API — configure CORSaws apigatewayv2 update-api --api-id abc123 \ --cors-configuration '{ "AllowOrigins": ["https://myapp.com"], "AllowMethods": ["GET", "POST", "PUT", "DELETE"], "AllowHeaders": ["Authorization", "Content-Type"], "MaxAge": 86400 }'HTTP APIs handle CORS automatically. REST APIs require manual OPTIONS method setup or enabling CORS in the console.
Custom Domain Names
Section titled “Custom Domain Names”Map your API to a custom domain instead of the auto-generated URL:
api.example.com → API Gateway → Lambda# Create custom domain (needs an ACM certificate)aws apigatewayv2 create-domain-name \ --domain-name api.example.com \ --domain-name-configurations CertificateArn=arn:aws:acm:...:cert
# Map to API stageaws apigatewayv2 create-api-mapping \ --domain-name api.example.com \ --api-id abc123 \ --stage '$default'Then create a Route 53 alias record pointing api.example.com to the API Gateway domain.
Caching (REST API Only)
Section titled “Caching (REST API Only)”REST APIs can cache responses to reduce Lambda invocations and latency:
| Setting | What It Does |
|---|---|
| Cache capacity | 0.5 GB – 237 GB |
| TTL | 0 – 3600 seconds (default 300) |
| Cache key | By path, query strings, headers |
| Per-method | Enable/disable per method |
Cache is charged per hour by capacity size.
Monitoring
Section titled “Monitoring”| Signal | Where |
|---|---|
| Metrics | CloudWatch: Count, Latency, 4XXError, 5XXError, IntegrationLatency |
| Access logs | CloudWatch Logs or Kinesis Firehose (request/response details) |
| Execution logs | CloudWatch Logs (detailed debug — method request/response, integration) |
| X-Ray tracing | End-to-end trace through API Gateway → Lambda → downstream |
Common Patterns
Section titled “Common Patterns”Serverless REST API
Section titled “Serverless REST API”Client ──► API Gateway (HTTP API) ──► Lambda ──► DynamoDB │ └── JWT authorizer (Cognito)Microservices Gateway
Section titled “Microservices Gateway”Client ──► API Gateway ──► /users/* ──► User service (ALB) ──► /orders/* ──► Orders Lambda ──► /payments/* ──► Payment service (NLB)WebSocket (Real-Time)
Section titled “WebSocket (Real-Time)”Client ◄──WebSocket──► API Gateway ──► Lambda ($connect, $disconnect, $default) │ ▼ DynamoDB (connection table)Key Takeaways
Section titled “Key Takeaways”- HTTP API is cheaper and faster — use it by default. Use REST API only for caching, request validation, or usage plans.
- API Gateway handles authentication (JWT, IAM, Lambda authorizer), throttling, and CORS so your backend doesn’t have to.
- Use
{proxy+}catch-all routes with a framework (FastAPI, Express) for simpler routing inside Lambda. - Stages separate dev/staging/prod with different configs and throttle limits.
- API keys are for tracking and throttling, not security — use JWT or IAM for authentication.
- Enable CloudWatch access logs and X-Ray for observability.