AWS API Gateway and OpenAPI Configs - Initial Thoughts
I’ve recently been working on building some Terraform code to manage API Gateway instances, and ran into a fair amount of confusion on making an OpenAPI spec that did what I wanted.
My use-case was to proxy to Lambda functions for the backend, which is kind of cheating as you can skip a lot of the more tedious schema definitions. I’m doing this as a template in Terraform so there is some variable interpolation here.
Here’s a small example that I’ll dissect. I’m also using YAML instead of JSON here because I value my eyeballs.
openapi: "3.0.1"
info:
title: "${API_NAME}"
version: "${API_VERSION}"
servers:
- url: "https://${API_HOSTNAME}"
paths:
"/test":
get:
x-amazon-apigateway-integration:
type: "HTTP_PROXY"
httpMethod: "GET"
uri: "https://ip-ranges.amazonaws.com/ip-ranges.json"
"/echo":
get:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: ${INVOKE_ECHO_ARN}
credentials: ${APIGW_ROLE}
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
post:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: ${INVOKE_ECHO_ARN}
credentials: ${APIGW_ROLE}
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
delete:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: ${INVOKE_ECHO_ARN}
credentials: ${APIGW_ROLE}
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
delete:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: ${INVOKE_ECHO_ARN}
credentials: ${APIGW_ROLE}
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
patch:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: ${INVOKE_ECHO_ARN}
credentials: ${APIGW_ROLE}
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
options:
responses:
"200":
description: "200 response"
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
Access-Control-Allow-Methods:
schema:
type: "string"
Access-Control-Allow-Headers:
schema:
type: "string"
content: {}
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'GET,HEAD,OPTIONS,POST,PUT,DELETE,PATCH'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
requestTemplates:
application/json: "{\"statusCode\": 200}"
passthroughBehavior: "when_no_match"
type: "mock"
components:
schemas:
Empty:
title: "Empty Schema"
type: "object"
So that’s a lot of lines for what amounts to 2 endpoints and a handful of methods.
The first endpoint is the /test
one. This is just to make sure that the config works and is a simple HTTP proxy over to the AWS IP list.
The /echo
endpoint will route to a lambda that will just echo back the APIGW request. I’ve found this really helpful as a way to test to make sure that data is getting to the backend and to allow me to debug my requests.
There are the gotchas that people should be aware of…
Everything is a POST. Sort of.
Regardless of the HTTP method being sent, which is defined as the first level under /echo
, the httpMethod
of the integration will need to be a POST
. This is what the Lambda function will require. The AWS docs do cover this, but it is unintuitive at first so I wanted to mention it here. If you use something other than POST
, you’ll get a weird response back.
Quotes Matter
If you were to export an OpenAPI spec from your existing APIGW and try to use that file, you’d likely get failures. That’s because the paths, such as /echo
are missing quotes! You’ll need to make sure to quote anything that starts with a /
.
Use the Correct Role
Here I’m passing in the ARN of the APIGW as a variable. If you do this, it needs to be the ARN of the role that you assign to the APIGW. Seems obvious, but once you really get into the Terraform, there are many roles and ARNs for the APIGW and Lambdas and it can get overwhelming to use the right ones in the right places.
Use the Correct ARN
Similar to the above, the ARN for the target Lamba isn’t the Lambda ARN, but the invokation ARN, which isn’t the same thing.