How to use OpenAPI to deploy an API Gateway HTTP API
Import and initialize an HTTP API using an OpenAPI document
OpenAPI with HTTP APIs
OpenAPI is a standard format that describes an API. It defines operations using paths and HTTP methods, as well as input parameters and responses for them. As its structure is well-defined, tools can use it for all sorts of purposes.
API Gateway HTTP API can consume an OpenAPI document and create the API based on its configuration. This can be done when you create the API or later to update an existing one.
The OpenAPI document provides an "init document" that creates the subresources under the API. These are the same resources you need to create if you don't use the API import. Because of this, using OpenAPI does not provide any extra functionality, just a way to reuse a format you might use in other places.
Structure
An OpenAPI document defines paths and operations, such as this one that creates a user:
paths:
/user:
post:
operationId: createUser
summary: Create user
requestBody:
# ...
responses:
default:
description: Success
When you import this definition, API Gateway creates a route at POST /user
among other operations that the document defines.
Integrations
What is missing from the document is how to handle the request, for example, which Lambda functions to call. This is not part of the OpenAPI standard, but
it allows vendor extensions that are properties starting with x-
.
API Gateway supports several extensions, all start
with x-amazon-apigateway-
.
HTTP APIs support referencing the x-amazon-apigateway-integration
using the standard $ref
syntax (as opposed to REST APIs, which does not support
this). This allows a Lambda integration setup to be easily shared between operations.
To define an integration, add an x-amazon-apigateway-integrations
element under the components
:
components:
x-amazon-apigateway-integrations:
users:
type: aws_proxy
uri: arn:aws:lambda:us-east-2:123456789012:function:my-function
httpMethod: POST
passthroughBehavior: when_no_match
contentHandling: CONVERT_TO_TEXT
payloadFormatVersion: 2.0
The above example uses the aws_proxy
integration type and uses the 2.0
payload format version.
The httpMethod
is how API Gateway calls the function and that has nothing to do with how users call the API. It is always POST for a Lambda function,
no matter how the API was called.
To associate this integration with an operation, use the x-amazon-apigateway-integration
with a reference:
paths:
/user:
post:
x-amazon-apigateway-integration:
$ref: '#/components/x-amazon-apigateway-integrations/users'
operationId: createUser
summary: Create user
requestBody:
# ...
responses:
default:
description: Success
This configures an integration with the above parameters to this operation:
Using the global integrations object to define the targets adds just a few lines of overhead in the OpenAPI document.
Backend
When the request reaches the backend, it needs to know which operation was called. Unfortunately, the operationId
is not available in the event object.
If you have a separate Lambda function for each operation then it's not a problem but it's usually not the case.
If you use a separate Lambda function for each path then you can discriminate using the HTTP method.
const method = event.requestContext.http.method;
const body = event.body;
if (method === "GET") {
// handle GET
}else if (method === "POST") {
// handle POST
}
Otherwise, you need to check the request path.
Path parameters are supported, so at least there is no need to extract them from the request path. Path parameters are placeholders in the path:
paths:
'/user/{userid}':
parameters:
- name: userid
in: path
required: true
schema:
type: string
delete:
operationId: deleteUser
summary: Delete user
responses:
200:
description: Success
And these are available under the pathParameters
property in the event object:
const userid = event.pathParameters.userid;
Lack of validation
As importing an OpenAPI document creates the same resources as you'd create without it, it can not provide more functionality. Even though the API document specifies parameter and request body validation, API Gateway won't run them.
This means you need to implement these validations on the backend.
If you need validation on the API Gateway level you need to use REST APIs that provide this. On the other hand, a REST API requires a more complex configuration, it's a bit slower, and a bit more expensive.
Using Terraform
To wire a Lambda function that Terraform manages to an OpenAPI document, you can use the templatefile
function. First, define the placeholders in the yaml:
components:
x-amazon-apigateway-integrations:
users:
type: aws_proxy
uri: ${users_lambda_arn}
httpMethod: POST
passthroughBehavior: when_no_match
contentHandling: CONVERT_TO_TEXT
payloadFormatVersion: 2.0
The uri
will be whatever is passed as the users_lambda_arn
. To define this variable, template the body
, which is the OpenAPI document that is
imported to the API:
resource "aws_apigatewayv2_api" "api" {
name = "api-${random_id.id.hex}"
protocol_type = "HTTP"
body = templatefile("api.yml", {users_lambda_arn = aws_lambda_function.users_lambda.arn})
}
This also makes an implicit dependency between the Lambda function and the API. Terraform creates the function first, then when it's ready deploys the API.
Then you need to add a stage, which is usually taken care of in other quick-start configurations, but not when the body
is used. The $default
name
means the stage will be available under the root.
resource "aws_apigatewayv2_stage" "stage" {
api_id = aws_apigatewayv2_api.api.id
name = "$default"
auto_deploy = true
}
Finally, you need to add the necessary permissions to the Lambda function so that the API Gateway can invoke it:
resource "aws_lambda_permission" "apigw" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.users_lambda.arn
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.api.execution_arn}/*/*"
}
Conclusion
API Gateway HTTP APIs support an init document that is in standard OpenAPI format. This document defines the paths and the operations the API needs to handle and with vendor extensions, it defines how API Gateway forwards the request.
This offers a way to reuse the standard document that other tools can also use with API Gateway. But even though OpenAPI supports a feature, such as input validation
or the operationId
, API Gateway might not.