Setting up AWS API Gateway with Terraform
A comprehensive guide to deploying and managing API Gateway using Terraform Infrastructure as Code
Setting up AWS API Gateway with Terraform
Amazon API Gateway is a fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs. This guide shows how to set up API Gateway using Terraform.
Video Tutorial
Learn more about managing AWS API Gateway with Terraform in this comprehensive video tutorial:
Related Guides:
- For Lambda integration examples, see AWS Lambda with API Gateway
- For Lambda-specific setup, see AWS Lambda
Prerequisites
- AWS CLI configured
- Terraform installed
- Basic understanding of REST APIs
Project Structure
aws-api-gateway-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Basic API Gateway Configuration
provider "aws" {
region = var.aws_region
}
# HTTP API
resource "aws_apigatewayv2_api" "main" {
name = "${var.project_name}-api"
protocol_type = "HTTP"
description = "HTTP API Gateway"
cors_configuration {
allow_origins = ["*"]
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_headers = ["content-type"]
max_age = 300
}
}
# API Stage
resource "aws_apigatewayv2_stage" "main" {
api_id = aws_apigatewayv2_api.main.id
name = var.environment
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_gateway.arn
format = jsonencode({
requestId = "$context.requestId"
ip = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
httpMethod = "$context.httpMethod"
routeKey = "$context.routeKey"
status = "$context.status"
protocol = "$context.protocol"
responseTime = "$context.responseLatency"
responseLength = "$context.responseLength"
})
}
}
# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "api_gateway" {
name = "/aws/api-gateway/${var.project_name}"
retention_in_days = 14
}
API Routes and Integrations
# Mock Integration
resource "aws_apigatewayv2_integration" "mock" {
api_id = aws_apigatewayv2_api.main.id
integration_type = "MOCK"
request_parameters = {
"overwrite:statusCode" = "200"
}
response_parameters {
status_code = "200"
mappings = {
"append:header.Content-Type" = "application/json"
}
}
}
# Route
resource "aws_apigatewayv2_route" "mock" {
api_id = aws_apigatewayv2_api.main.id
route_key = "GET /test"
target = "integrations/${aws_apigatewayv2_integration.mock.id}"
}
Advanced Features
1. Custom Domain
resource "aws_apigatewayv2_domain_name" "main" {
domain_name = "api.${var.domain_name}"
domain_name_configuration {
certificate_arn = var.certificate_arn
endpoint_type = "REGIONAL"
security_policy = "TLS_1_2"
}
}
resource "aws_apigatewayv2_api_mapping" "main" {
api_id = aws_apigatewayv2_api.main.id
domain_name = aws_apigatewayv2_domain_name.main.id
stage = aws_apigatewayv2_stage.main.id
}
resource "aws_route53_record" "api" {
name = aws_apigatewayv2_domain_name.main.domain_name
type = "A"
zone_id = var.hosted_zone_id
alias {
name = aws_apigatewayv2_domain_name.main.domain_name_configuration[0].target_domain_name
zone_id = aws_apigatewayv2_domain_name.main.domain_name_configuration[0].hosted_zone_id
evaluate_target_health = false
}
}
2. Request Validation
resource "aws_apigatewayv2_model" "request" {
api_id = aws_apigatewayv2_api.main.id
content_type = "application/json"
name = "RequestModel"
schema = jsonencode({
type = "object"
properties = {
name = {
type = "string"
}
email = {
type = "string"
format = "email"
}
}
required = ["name", "email"]
})
}
resource "aws_apigatewayv2_route" "validated" {
api_id = aws_apigatewayv2_api.main.id
route_key = "POST /users"
target = "integrations/${aws_apigatewayv2_integration.mock.id}"
request_model_selection_expression = "$request.method"
model_selection_expression = "$request.body"
model_name = aws_apigatewayv2_model.request.name
}
3. API Key and Usage Plans
resource "aws_api_gateway_api_key" "main" {
name = "${var.project_name}-key"
}
resource "aws_api_gateway_usage_plan" "main" {
name = "${var.project_name}-usage-plan"
api_stages {
api_id = aws_apigatewayv2_api.main.id
stage = aws_apigatewayv2_stage.main.id
}
quota_settings {
limit = 1000
period = "MONTH"
}
throttle_settings {
burst_limit = 100
rate_limit = 50
}
}
resource "aws_api_gateway_usage_plan_key" "main" {
key_id = aws_api_gateway_api_key.main.id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.main.id
}
Monitoring Configuration
# CloudWatch Dashboard
resource "aws_cloudwatch_dashboard" "api_gateway" {
dashboard_name = "${var.project_name}-api-gateway"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/ApiGateway", "4XXError", "ApiId", aws_apigatewayv2_api.main.id],
["AWS/ApiGateway", "5XXError", "ApiId", aws_apigatewayv2_api.main.id],
["AWS/ApiGateway", "Count", "ApiId", aws_apigatewayv2_api.main.id],
["AWS/ApiGateway", "Latency", "ApiId", aws_apigatewayv2_api.main.id]
]
period = 300
stat = "Sum"
region = var.aws_region
title = "API Gateway Metrics"
}
}
]
})
}
# CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "api_5xx" {
alarm_name = "${var.project_name}-5xx-errors"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "5XXError"
namespace = "AWS/ApiGateway"
period = "300"
statistic = "Sum"
threshold = "0"
alarm_description = "This metric monitors API Gateway 5XX errors"
alarm_actions = [var.sns_topic_arn]
dimensions = {
ApiId = aws_apigatewayv2_api.main.id
}
}
Best Practices
-
Security
- Use API keys for authentication
- Implement proper CORS settings
- Enable WAF protection
- Use SSL/TLS for all endpoints
-
Performance
- Enable caching when appropriate
- Use proper throttling settings
- Monitor API latency
- Implement proper timeouts
-
Monitoring
- Set up detailed logging
- Configure alarms for errors
- Monitor API usage
- Track latency metrics
-
Cost Optimization
- Use appropriate caching strategies
- Implement proper throttling
- Monitor API usage
- Clean up unused resources
Common Use Cases
- Public API
resource "aws_apigatewayv2_api" "public" {
name = "${var.project_name}-public-api"
protocol_type = "HTTP"
cors_configuration {
allow_origins = ["*"]
allow_methods = ["*"]
allow_headers = ["*"]
}
}
- Private API
resource "aws_apigatewayv2_api" "private" {
name = "${var.project_name}-private-api"
protocol_type = "HTTP"
disable_execute_api_endpoint = true
vpc_endpoint_ids = [aws_vpc_endpoint.api_gateway.id]
}
resource "aws_vpc_endpoint" "api_gateway" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.execute-api"
vpc_endpoint_type = "Interface"
subnet_ids = var.subnet_ids
security_group_ids = [aws_security_group.api_gateway.id]
}
Variables
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Project name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "domain_name" {
description = "Domain name for API"
type = string
}
variable "certificate_arn" {
description = "ACM certificate ARN"
type = string
}
variable "hosted_zone_id" {
description = "Route53 hosted zone ID"
type = string
}
variable "vpc_id" {
description = "VPC ID for private API"
type = string
}
variable "subnet_ids" {
description = "Subnet IDs for private API"
type = list(string)
}
variable "sns_topic_arn" {
description = "SNS Topic ARN for alarms"
type = string
}
Outputs
output "api_endpoint" {
description = "API Gateway endpoint URL"
value = aws_apigatewayv2_api.main.api_endpoint
}
output "api_id" {
description = "API Gateway ID"
value = aws_apigatewayv2_api.main.id
}
output "stage_name" {
description = "API Gateway stage name"
value = aws_apigatewayv2_stage.main.name
}
output "custom_domain" {
description = "Custom domain name"
value = aws_apigatewayv2_domain_name.main.domain_name
}
Conclusion
This setup provides a comprehensive foundation for deploying API Gateway using Terraform. Remember to:
- Implement proper security measures
- Set up monitoring and alerting
- Configure appropriate throttling
- Use proper logging and tracing
For integrating this API Gateway with Lambda functions, see our guide on AWS Lambda with API Gateway.