Several years ago I was tinkering with AWS API Gateway and reading through the documentation on the Apache Velocity Template Language (VTL) that is used for data transformations. I noticed that one of the built-in variables was $context.identity.sourceIp which contained the client’s IP address.1 It occurred to me that I could make one of those “what’s my IP” services by pointing the root of the API gateway at a MOCK integration with a response of type text/plain containing only that variable. No Lambda function was needed. I tried it and it worked as expected. It has been running in one of my accounts for years and I use it in many of my build scripts (there isn’t much point to this since AWS has their own at https://checkip.amazonaws.com/).
In Terraform (or OpenTofu), the relevant resources look like this:
# Return caller IP as plain text via API Gateway (no backend)
resource "aws_api_gateway_rest_api" "ip" {
name = "ip"
description = "My IP"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_method" "ip_method_get" {
rest_api_id = aws_api_gateway_rest_api.ip.id
resource_id = aws_api_gateway_rest_api.ip.root_resource_id
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "ip_get_integration" {
rest_api_id = aws_api_gateway_rest_api.ip.id
resource_id = aws_api_gateway_rest_api.ip.root_resource_id
http_method = aws_api_gateway_method.ip_method_get.http_method
type = "MOCK"
passthrough_behavior = "WHEN_NO_MATCH"
request_templates = {
"application/json" = "{ \"statusCode\": 200 }"
"application/xml" = "{ \"statusCode\": 200 }"
"application/x-www-form-urlencoded" = "{ \"statusCode\": 200 }"
"text/plain" = "{ \"statusCode\": 200 }"
}
}
resource "aws_api_gateway_method_response" "ip_get_method_response_200" {
rest_api_id = aws_api_gateway_rest_api.ip.id
resource_id = aws_api_gateway_rest_api.ip.root_resource_id
http_method = aws_api_gateway_method.ip_method_get.http_method
status_code = 200
response_models = {
"text/plain" = "Empty"
}
response_parameters = {
"method.response.header.Content-Type" = true
}
}
resource "aws_api_gateway_integration_response" "ip_get_integration_response_200" {
rest_api_id = aws_api_gateway_rest_api.ip.id
resource_id = aws_api_gateway_rest_api.ip.root_resource_id
http_method = aws_api_gateway_method.ip_method_get.http_method
status_code = aws_api_gateway_method_response.ip_get_method_response_200.status_code
response_templates = {
"text/plain" = "$context.identity.sourceIp\n"
}
response_parameters = {
"method.response.header.Content-Type" = "'text/plain'"
}
}
The IP example is trivial, but the underlying pattern is more broadly useful. API gateway’s mapping layer can read request metadata and generate responses without a backend. In the spirit of serverless computing, I called this computeless servering.2 There are probably a lot of opportunities to provide similarly trivial but useful services using only the intermediary building blocks in various cloud services.
