API Gateway Setup (AWS API Gateway) for Web Application
AWS API Gateway is a managed service for creating, publishing, and monitoring HTTP and WebSocket APIs. Two types available: REST API (more feature-rich) and HTTP API (cheaper, faster for simple cases).
REST API vs HTTP API
| Feature | REST API | HTTP API |
|---|---|---|
| Lambda integration | + | + |
| JWT authorizer | + | + |
| Custom authorizer | + | + |
| Usage plans / API keys | + | — |
| Request/Response mapping | + | — |
| WAF integration | + | — |
| Private endpoints | + | — |
| Price (per million requests) | $3.50 | $1.00 |
HTTP API is suitable for simple backend→Lambda proxy. REST API is needed for complex request transformation and throttling by API keys.
Creation via Terraform
# main.tf
resource "aws_api_gateway_rest_api" "main" {
name = "myapp-api"
description = "Main application API"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_resource" "users" {
rest_api_id = aws_api_gateway_rest_api.main.id
parent_id = aws_api_gateway_rest_api.main.root_resource_id
path_part = "users"
}
resource "aws_api_gateway_method" "users_get" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.users.id
http_method = "GET"
authorization = "COGNITO_USER_POOLS"
authorizer_id = aws_api_gateway_authorizer.cognito.id
request_parameters = {
"method.request.querystring.page" = false
"method.request.querystring.limit" = false
}
}
resource "aws_api_gateway_integration" "users_get" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.users.id
http_method = aws_api_gateway_method.users_get.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.users_handler.invoke_arn
}
Cognito Authorizer
resource "aws_api_gateway_authorizer" "cognito" {
name = "cognito-authorizer"
rest_api_id = aws_api_gateway_rest_api.main.id
type = "COGNITO_USER_POOLS"
provider_arns = [aws_cognito_user_pool.main.arn]
identity_source = "method.request.header.Authorization"
}
Lambda Authorizer (Custom Authentication)
# lambda_authorizer.py
import json
def handler(event, context):
token = event.get('authorizationToken', '')
# Verify token against your database or service
principal_id = verify_token(token)
if not principal_id:
raise Exception('Unauthorized')
return {
'principalId': principal_id,
'policyDocument': {
'Version': '2012-10-17',
'Statement': [{
'Action': 'execute-api:Invoke',
'Effect': 'Allow',
'Resource': event['methodArn']
}]
},
'context': {
'user_id': principal_id,
'tenant_id': get_tenant(principal_id)
}
}
resource "aws_api_gateway_authorizer" "lambda" {
name = "lambda-authorizer"
rest_api_id = aws_api_gateway_rest_api.main.id
authorizer_uri = aws_lambda_function.authorizer.invoke_arn
authorizer_result_ttl_in_seconds = 300 # cache for 5 minutes
type = "TOKEN"
identity_source = "method.request.header.Authorization"
}
Usage Plans and API Keys
resource "aws_api_gateway_usage_plan" "standard" {
name = "standard-plan"
api_stages {
api_id = aws_api_gateway_rest_api.main.id
stage = aws_api_gateway_stage.prod.stage_name
}
throttle_settings {
burst_limit = 100
rate_limit = 50 # requests per second
}
quota_settings {
limit = 10000
period = "DAY"
}
}
resource "aws_api_gateway_api_key" "partner_app" {
name = "partner-app-key"
}
resource "aws_api_gateway_usage_plan_key" "partner" {
key_id = aws_api_gateway_api_key.partner_app.id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.standard.id
}
Request and Response Mapping
# Transform request: add tenant-id from authorizer context
resource "aws_api_gateway_integration" "users_post" {
# ...
request_templates = {
"application/json" = <<EOF
{
"body": $input.json('$'),
"tenant_id": "$context.authorizer.tenant_id",
"user_id": "$context.authorizer.user_id"
}
EOF
}
}
# Response mapping
resource "aws_api_gateway_method_response" "users_200" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.users.id
http_method = aws_api_gateway_method.users_get.http_method
status_code = "200"
response_parameters = {
"method.response.header.Access-Control-Allow-Origin" = true
}
}
Stages and Deployment
resource "aws_api_gateway_deployment" "main" {
rest_api_id = aws_api_gateway_rest_api.main.id
triggers = {
redeployment = sha1(jsonencode([
aws_api_gateway_resource.users.id,
aws_api_gateway_method.users_get.id,
aws_api_gateway_integration.users_get.id,
]))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "prod" {
deployment_id = aws_api_gateway_deployment.main.id
rest_api_id = aws_api_gateway_rest_api.main.id
stage_name = "prod"
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_gateway.arn
format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
httpMethod = "$context.httpMethod"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
latency = "$context.responseLatency"
})
}
}
resource "aws_api_gateway_method_settings" "prod" {
rest_api_id = aws_api_gateway_rest_api.main.id
stage_name = aws_api_gateway_stage.prod.stage_name
method_path = "*/*"
settings {
throttling_burst_limit = 500
throttling_rate_limit = 200
logging_level = "INFO"
metrics_enabled = true
}
}
Custom Domain + ACM Certificate
resource "aws_api_gateway_domain_name" "api" {
domain_name = "api.company.com"
regional_certificate_arn = aws_acm_certificate.api.arn
endpoint_configuration { types = ["REGIONAL"] }
}
resource "aws_route53_record" "api" {
zone_id = aws_route53_zone.main.zone_id
name = "api.company.com"
type = "A"
alias {
name = aws_api_gateway_domain_name.api.regional_domain_name
zone_id = aws_api_gateway_domain_name.api.regional_zone_id
evaluate_target_health = true
}
}
Timeline
REST API setup with Cognito/Lambda authorizer, usage plans, and Terraform configuration — 3–5 business days.







