Creating & Managing Aws Rest Api Gateway Using Terraform
Tags: aws, terraform, openapi, devops
Published: 2023-01-09 16:44:08

We will be creating and managing an AWS rest api gateway using terraform

## Table of Contents

- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Rest Api](##rest-api)
- [Rest Api Body](##rest-api-body)
- [Open API server](#open-api-server)
- [Open API paths](#open-api-paths)
- [Method response](#method-response)
- [Method details](#method-details)
- [Terraform File structure](#terraform-file-structure)

## Introduction

We can create resources on cloud either manually or using automation tools.

Lets take an example of the following api endpoint our service is exposing either through lambda or the vpc link:

```shell 
 api/v1/test/get_tests -> an enpoint with POST AND GET requests
```

If we were to create then above method manually; we would have to the following:
- Create the rest api if one didnt exist
- Create the api,v1, test then the get_tests resources
- Create the GET method then and add the needed headers, add parameters the api may need, add authorization to the api and then repeat the same for the POST method.

This would be cumbersome if we had many api endpoints to create and managing the api endpoints would be very hard. 

In this post:
- We will focus on creating a simple AWS rest api which we will manage using terraform (an Infrastructure as Code tool). 
- We will be using the openapi specification for our apis
- This approach can be inegrated with a CI/CD tool i.e jenkins or gitlab ci or github actions to enable further automation of our rest api endpoint

## Prerequisites:

- A postman collection containing our api endpoints (swagger api documentation would work as well)
- An AWS account
- Terraform (The version in use is Terraform v1.2.6)
- We will focus on the rest apis where the underlying service is exposed using the vpc link

## Rest api

For our rest api, we will need to provide the name, description and the endpoint type. 

```hcl
resource "aws_api_gateway_rest_api" "test_rest_api"{
  name = "<<REST API NAME>>"
  endpoint_configuration {
    types = ["REGIONAL"]
  }
  description = "<<REST API DESCRIPTION>>"
  body        = data.template_file.testfile.rendered
}
```

## Rest api body
For the body we will use the code snippet below that utilizes terraform template files:

```hcl
data "template_file" "testfile"{
  template = file("./test.yaml")
  vars = {
    methodresponses = var.methodresponses
    methoddetails   = var.methoddetails
  }
}
```

## Open API server
The servers section contains the custom domain name we would use to expose our api using.

![Servers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ck4j30r0lxystdbop6yi.png)

Users would be able to access the service by sending api requests to the `"{domainName}/{basePath}"` endpoint

## Open API paths

The paths section contains the paths for our api endpoints. We only need to specify the following:
- Api endpoint e.g `/api/v1/test/get_tests`
- The absolute path of the api endpoint being exposed via the vpc link (`"https://$${stageVariables.url}/api/v1/test/get_tests"`)
  - The first part of the url is the url of the underlying domain exposing the service i.e `"https://$${stageVariables.url}/`.
  - The latter part is the actual path the service expects to expose the endpoint i.e `"/api/v1/test/get_tests"`

- The type of the http method e.g POST

## Method response

The method reponses contains a of method reponse e.g `200` and the response headers for the reponse.

```hcl 
variable "methodresponses"{
  default = <<EOT
responses:
        "200":
          description: "200 response"
          headers:
            Access-Control-Allow-Headers:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Empty"
EOT
}
```


## Method details
The method detail are as per the template below. We can add any headers the method needs under the response parameters section as shown.

```hcl
variable "methoddetails"{
  default = <<EOT
responses:
  default:
    statusCode: "200"
    responseParameters:
      method.response.header.Access-Control-Allow-Origin: "<<VALUE>>"
passthroughBehavior: "when_no_match"
connectionType: "VPC_LINK"
type: "http_proxy"
EOT
}

```

---

## Terraform File structure 

![Terraform File structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h629wsmj538zppxob957.png)

Our `main.tf` file will contain the following structure:

```hcl
resource "aws_api_gateway_rest_api" "test_rest_api"{
  name = "<<REST API NAME>>"
  endpoint_configuration {
    types = ["REGIONAL"]
  }
  description = "<<REST API DESCRIPTION>>"
  body        = data.template_file.testfile.rendered
} 

data "template_file" "testfile"{
  template = file("./test.yaml")
  vars = {
    methodresponses = var.methodresponses
    methoddetails   = var.methoddetails
  }
}
```

Our `locals.tf` will contain the following structure:
```hcl 
variable "methodresponses"{
  default = <<EOT
responses:
        "200":
          description: "200 response"
          headers:
            Access-Control-Allow-Headers:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Empty"
EOT
}

variable "methoddetails"{
  default = <<EOT
responses:
  default:
    statusCode: "200"
    responseParameters:
      method.response.header.Access-Control-Allow-Origin: "<<VALUE>>"
passthroughBehavior: "when_no_match"
connectionType: "VPC_LINK"
type: "http_proxy"
EOT
}

```

Our `test.yaml` file will contain the following openapi specification:

```yaml
openapi: "3.0.1"
servers:
  - url: "{domainName}/{basePath}"
    variables:
      domainName:
        default: "https://test.com"
      basePath:
        default: "/testpath"
paths:
  /api/v1/test/get_tests:
    post:
      ${methodresponses}
      x-amazon-apigateway-integration:
        uri: "https://$${stageVariables.url}/api/v1/test/get_tests"
        connectionId: "$${stageVariables.vpcLink}"
        httpMethod: "POST"
        ${methoddetails}
    get:
      ${methodresponses}
      x-amazon-apigateway-integration:
        uri: "https://$${stageVariables.url}/api/v1/test/get_tests"
        connectionId: "$${stageVariables.vpcLink}"
        httpMethod: "GET"
        ${methoddetails}
  /api/v1/test/tests:
    get:
      ${methodresponses}
      x-amazon-apigateway-integration:
        uri: "https://$${stageVariables.url}//api/v1/test/tests"
        connectionId: "$${stageVariables.vpcLink}"
        httpMethod: "GET"
        ${methoddetails}
```
To manage/add a new api endpoint we only need to update/change the `test.yaml` file.

We only need to do `terraform plan` and `terraform apply` for our api to be created.