API Gateway & Step Functions
Checking access...
Amazon API Gateway creates, publishes, and secures APIs at any scale. AWS Step Functions coordinates multiple AWS services into serverless workflows.
API Gateway — API Types
| Type | Features | Use Case |
|---|---|---|
| REST API | Full feature set — usage plans, API keys, caching, transformations, WAF | Production APIs requiring granular control |
| HTTP API | Simplified, lower cost, lower latency (~50% cheaper, ~30% faster) | Modern APIs, Lambda proxies, simpler auth |
| WebSocket API | Bidirectional communication, persistent connections | Real-time apps, chat, streaming data |
REST API with Lambda Integration
resource "aws_api_gateway_rest_api" "api" { name = "my-api" description = "Serverless API"}
resource "aws_api_gateway_resource" "items" { rest_api_id = aws_api_gateway_rest_api.api.id parent_id = aws_api_gateway_rest_api.api.root_resource_id path_part = "items"}
resource "aws_api_gateway_method" "get_items" { rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.items.id http_method = "GET" authorization = "NONE"}
resource "aws_api_gateway_integration" "get_items_lambda" { rest_api_id = aws_api_gateway_rest_api.api.id resource_id = aws_api_gateway_resource.items.id http_method = aws_api_gateway_method.get_items.http_method type = "AWS_PROXY" uri = aws_lambda_function.get_items.invoke_arn}# Lambda handler for API Gatewayimport jsonimport boto3
dynamodb = boto3.resource('dynamodb')table = dynamodb.Table('Items')
def lambda_handler(event, context): try: response = table.scan() return { 'statusCode': 200, 'headers': { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, 'body': json.dumps(response['Items']) } except Exception as e: return { 'statusCode': 500, 'body': json.dumps({'error': str(e)}) }Usage Plans and API Keys
Protect your API with throttling and quota limits:
resource "aws_api_gateway_usage_plan" "basic" { name = "basic-plan"
api_stages { api_id = aws_api_gateway_rest_api.api.id stage = "prod" }
throttle_settings { burst_limit = 100 rate_limit = 50 }
quota_settings { limit = 10000 period = "MONTH" }}
resource "aws_api_gateway_api_key" "client1" { name = "client-1-key"}
resource "aws_api_gateway_usage_plan_key" "client1_plan" { key_id = aws_api_gateway_api_key.client1.id key_type = "API_KEY" usage_plan_id = aws_api_gateway_usage_plan.basic.id}# Test with the API keycurl -H "x-api-key: $(aws apigateway get-api-key --api-key <key-id> --include-value --query 'value' --output text)" \ https://api-id.execute-api.us-east-1.amazonaws.com/prod/itemsStep Functions — State Machines
Step Functions coordinate Lambda, ECS, SQS, DynamoDB, and other services into workflows defined as JSON state machines:
{ "Comment": "Order processing workflow", "StartAt": "ValidateOrder", "States": { "ValidateOrder": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:validate-order", "Next": "ProcessPayment" }, "ProcessPayment": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:process-payment", "Next": "CheckInventory", "Retry": [ { "ErrorEquals": ["States.ALL"], "IntervalSeconds": 5, "MaxAttempts": 3, "BackoffRate": 2 } ], "Catch": [ { "ErrorEquals": ["PaymentFailed"], "Next": "CancelOrder" } ] }, "CheckInventory": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:check-inventory", "Next": "ShipOrder" }, "ShipOrder": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:ship-order", "End": true }, "CancelOrder": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:cancel-order", "End": true } }}State Types
| State Type | Purpose | Example |
|---|---|---|
| Task | Execute a unit of work (Lambda, ECS, SQS, etc.) | "Resource": "arn:aws:lambda:..." |
| Choice | Branch based on input values | Switch on order status |
| Parallel | Execute branches concurrently | Process payment + check inventory simultaneously |
| Map | Iterate over a list of items | Process each line item in an order |
| Wait | Pause execution for a duration or until a timestamp | Delay before sending reminder |
| Pass | Pass input to output, inject data | Transform payload format |
| Succeed/Fail | Terminal states | Mark workflow as successful or failed |
resource "aws_sfn_state_machine" "order_workflow" { name = "order-processing-workflow" role_arn = aws_iam_role.step_functions.arn
definition = jsonencode({ Comment = "Order processing workflow" StartAt = "ValidateOrder" States = { ValidateOrder = { Type = "Task" Resource = aws_lambda_function.validate_order.arn Next = "ProcessPayment" } ProcessPayment = { Type = "Task" Resource = aws_lambda_function.process_payment.arn Next = "NotifyCustomer" Retry = [ { ErrorEquals = ["States.ALL"] IntervalSeconds = 5 MaxAttempts = 3 BackoffRate = 2 } ] } NotifyCustomer = { Type = "Task" Resource = aws_sns_topic.order_notifications.arn End = true } } })}Standard vs Express Workflows
Standard workflows have exactly-once execution, cost per state transition, and 1-year max duration — ideal for long-running, auditable workflows. Express workflows have at-least-once execution, cost per execution/duration, and 5-minute max duration — ideal for high-volume, short-lived tasks.
Key Takeaways
- REST APIs are full-featured (usage plans, caching, WAF); HTTP APIs are simpler, cheaper, faster (~50% less cost); WebSocket APIs support real-time bidirectional communication
- Usage plans enforce throttling (burst/rate) and quotas (daily/weekly/monthly) per API key — essential for protecting backend services from abuse
- Step Functions define workflows as JSON state machines with Task (execute service), Choice (branch), Parallel (concurrent), Map (iterate), Wait (delay), Pass (transform), Succeed/Fail (terminal) states
- Retry with exponential backoff handles transient failures; Catch with error routing handles business logic failures
- Standard workflows (exactly-once, long-running, auditable) vs Express workflows (at-least-once, high-volume, short-lived under 5 min)