Swagger deployment on AWS API Gateway with Terraform

Suraj Batuwana
5 min readJul 3, 2023

Deploying a complex Swagger definition to AWS API Gateway can be a bit challenging, especially if you have a large and intricate API specification. While AWS API Gateway supports Swagger/OpenAPI definitions for API configuration, the process of deploying a complex Swagger file involves several steps and considerations.

Here are some factors that can make the deployment of a complex Swagger file to AWS API Gateway more challenging:

  1. Validation and compatibility: AWS API Gateway has specific requirements and restrictions regarding Swagger specifications. It’s essential to ensure your Swagger file is compliant with the AWS API Gateway’s supported features and constraints.
  2. Endpoint configuration: Complex APIs often have multiple endpoints, request/response models, headers, and query parameters. Properly mapping these elements in the AWS API Gateway configuration can be time-consuming, especially if your Swagger file is extensive.
  3. Security and authorization: If your API requires authentication and authorization mechanisms, such as API keys, IAM roles, or OAuth, you’ll need to configure these settings correctly in the AWS API Gateway to ensure secure access to your API endpoints.
  4. Deployment stages: AWS API Gateway allows you to manage multiple stages (e.g., development, production) for your API. Coordinating the deployment of a complex Swagger file across different stages and keeping them in sync can be a challenge.
  5. Integration with backend services: If your API interacts with various backend services, such as AWS Lambda functions, AWS DynamoDB, or other HTTP endpoints, you’ll need to configure the integration points correctly in the AWS API Gateway based on the definitions provided in your Swagger file.

To overcome these challenges, you can follow these general steps to deploy a complex Swagger file to AWS API Gateway:

  1. Validate and ensure your Swagger file adheres to AWS API Gateway’s specifications.
  2. Create an API Gateway instance in the AWS Management Console.
  3. Import the Swagger file into the API Gateway using the provided import functionality.
  4. Configure the API Gateway settings, including endpoint mappings, security options, and integration with backend services.
  5. Deploy your API to the desired deployment stage.
  6. Test and verify the functionality of your API through the API Gateway.

It’s worth noting that AWS API Gateway also offers a more intuitive alternative to Swagger, called the API Gateway REST API model. This model allows you to define your API directly within the AWS Management Console or through AWS CloudFormation templates, providing a simpler way to configure complex APIs.

While deploying a complex Swagger definition to AWS API Gateway may require careful attention to details, following the recommended steps and taking advantage of AWS documentation and resources can help you navigate the process more effectively.

Below, I will provide an example of a Swagger definition with a GET endpoint that can be used to deploy to AWS API Gateway:

openapi: 3.0.0
servers:
- url: http://test.com/api/v1
info:
version: 2.0.1
title: test
description: >-
API for definitions for p microservice
paths:
/api/v1/user/me:
get:
parameters:
- in: header
name: Authorization
schema:
type: string
required: true
description: Bearer with token
- in: header
name: x-correlation-id
schema:
type: string
required: true
description: correlation id
tags:
- Users
operationId: getUserProfile
responses:
'200':
headers:
Access-Control-Allow-Origin:
schema:
type: "string"
description: The response
content:
application/json:
schema:
$ref: '#/components/schemas/UserGetResponse'
#Error
'400':
description: "Bad Request"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
'401':
description: "Unauthorized"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
'403':
description: "Forbidden"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
'404':
description: "Not Found"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
'429':
description: "Too Many Requests"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
'500':
description: "Internal Server Error"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
'503':
description: "Service Unavailable"
content:
application/json:
schema:
$ref: "#/components/schemas/errorResponse"
x-amazon-apigateway-integration:
type: HTTP_PROXY
passthroughBehavior: "when_no_match"
httpMethod: "GET"
uri: ${backend_url}/api/v1/user/me
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
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,OPTIONS'''
method.response.header.Access-Control-Allow-Headers: '''
Content-Type,x-correlation-id,authorization'''
method.response.header.Access-Control-Allow-Origin: '''*'''
requestTemplates:
application/json: "{\"statusCode\": 200}"
passthroughBehavior: "when_no_match"
type: "mock"


components:
schemas:
UserGetResponse:
type: object
required:
- displayName
- firstName
- mobile
- apiKey
properties:
apiKey:
type: string
format: apiKey
example: d290f1ee-6c54-4b01-90e6-d701748f0851
description: Unique identifier for profile
firstName:
type: string
example: Mary
description: A personal name given to someone at birth or baptism and used before a family name.
lastName:
type: string
example: Smith
description: In some cultures, a surname, family name, or last name is the portion of one's personal name that indicates their family, tribe or community. .
displayName:
type: string
example: Harry
description: The Display Name is what shows up next to a user.
userType:
type: string
status:
type: string
email:
type: string
format: email
example: test@test.com
description: email.
mobile:
type: string
example: "61452601976"
description: Mobile Number
phone:
type: string
example: 3.28761986E8
description: Phone Number
description:
type: string
description: More details
dateOfBirth:
type: string
format: date
example: 01/02/1965
description: Date of Birth
lastLoginTime:
type: string
format: date-time
example: '2017-07-21T17:32:28Z'
errorResponse:
type: object
properties:
errorId:
type: string
description: "The applicable error code."
message:
type: string
description: "The applicable error message."
details:
type: array
items:
type: object
properties:
field:
type: string
value:
type: string
issue:
type: string
location:
type: string

If you carefully inspect the above swagger, you can see two important differences from a standard swagger file

  1. x-amazon-apigateway-integration TAG — https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-integration.html
  2. options end point — This is an mandatory one to avoid CORS issues even backend server enabled it

Teraform script

below is the sample terraform script to deploy swagger to AWS api gateway

provider "aws" {
.......
}

data "template_file" "user_api_swagger" {
template = file("swagger.yaml")

vars = {
backend_url = "${var.backend_url}"
}
}

resource "aws_api_gateway_rest_api" "api_gateway" {
name = var.api_gateway_name
description = "User API Gateway"
body = data.template_file.user_api_swagger.rendered
endpoint_configuration {
types = ["REGIONAL"]
}
}

resource "aws_api_gateway_deployment" "prod" {
rest_api_id = aws_api_gateway_rest_api.api_gateway.id
stage_name = "prod"
}

backend_url can be a url from ec2 or elasticbeanstalk or application load balancer. You need to update provider aws with your ownen details. You may upgrade this script to read swagger file from a git repo

We can incorporate this Swagger definition into any code pipeline.

Currently, I am exploring ways to achieve this without adding AWS-specific tags to the Swagger file. I am open to hearing your ideas and suggestions on how we can accomplish this. Please feel free to share your thoughts.

--

--

Suraj Batuwana

Technology Evangelist, Technical Blogger with multidisciplinary skills with experience in full spectrum of design, architecture and development