DEV Community

Maxime Guilbert
Maxime Guilbert

Posted on • Edited on

Declare a simple REST API Gateway - Terraform

Recently, I had to create a REST API Gateway in AWS with Terraform. The documentation is good but I had to took some time, navigating pages to understand what should I do. So, here is a resume of that research.

Note : For HTTP and Websockets API Gateway, there is another of the Terraform scripts.


The base - API Gateway Rest API

Terraform Documentation

With it's minimal form, the Gateway is quite simple.

resource "aws_api_gateway_rest_api" "example" {
  name = "example"
}
Enter fullscreen mode Exit fullscreen mode

But you have a lot of optional elements like :

resource "aws_api_gateway_rest_api" "example" {
  body = jsonencode({
    openapi = "3.0.1"
    info = {
      title   = "example"
      version = "1.0"
    }
    paths = {
      "/path1" = {
        get = {
          x-amazon-apigateway-integration = {
            httpMethod           = "GET"
            payloadFormatVersion = "1.0"
            type                 = "HTTP_PROXY"
            uri                  = "https://ip-ranges.amazonaws.com/ip-ranges.json"
          }
        }
      }
    }
  })

  description = "Example of an API Gateway"
  name        = "example"

  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

Enter fullscreen mode Exit fullscreen mode

Ingress Policy - API Gateway Rest API Policy

Terraform documentation

The definition of the Ingress Policy can help you to manage ingress access. It can be really helpful because Security Groups can't be applied to the API Gateway.

In this example, we only open the API Gateway to the IP Ranges: 10.0.0.0/24 and 10.10.0.0/24

resource "aws_api_gateway_rest_api_policy" "test" {
  rest_api_id = aws_api_gateway_rest_api.test.id

  policy = <<EOF
{
    Version = "2012-10-17",
    Statement = [{
        Effect = "Deny",
        Principal = "*",
        Action = "execute-api:Invoke",
        Resource = "execute-api:/*/*/*"
      },
      {
        Effect = "Allow",
        Principal = "*",
        Action = "execute-api:Invoke",
        Resource = "execute-api:/*/*/*",
        Condition = {
          NotIpAddress = {
            "aws:SourceIp" = ["10.0.0.0/24", "10.10.0.0/24"]
          }
        }
      }
    ]
  }
EOF
Enter fullscreen mode Exit fullscreen mode

Define the exposed resources

Declare paths - API Gateway Resource

Terraform documentation

The Resource element will define all the path you want to expose.

Let's take an example.

You have the following paths :

  • /users
    • /add
    • /list
  • /cards
    • /play
    • /send

Both /users path expose a method of the same service, but each of /card expose a different one.

To do it, first we have to create a resource for /users and one for /cards.

resource "aws_api_gateway_resource" "usersResource" {
  rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
  parent_id   = aws_api_gateway_rest_api.MyDemoAPI.root_resource_id # In this case, the parent id should the gateway root_resource_id.
  path_part   = "users"
}

resource "aws_api_gateway_resource" "cardsResource" {
  rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
  parent_id   = aws_api_gateway_rest_api.MyDemoAPI.root_resource_id
  path_part   = "cards"
}
Enter fullscreen mode Exit fullscreen mode

Then, we had to create another resource for each subpath which expose differents endpoints. (You can do others resources for each users path)

resource "aws_api_gateway_resource" "playCardResource" {
  rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
  parent_id   = aws_api_gateway_resource.cardsResource.id # In this case, the parent id should be the parent aws_api_gateway_resource id.
  path_part   = "play"
}

resource "aws_api_gateway_resource" "sendCardResource" {
  rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
  parent_id   = aws_api_gateway_resource.cardsResource.id
  path_part   = "send"
}
Enter fullscreen mode Exit fullscreen mode

Declare Methods - API Gateway Method

Terraform documentation

Declare all methods available for each path.

Example where we only allow GET methods for /cards/send

resource "aws_api_gateway_method" "MyDemoMethod" {
  rest_api_id   = aws_api_gateway_rest_api.MyDemoAPI.id
  resource_id   = aws_api_gateway_resource.sendCardResource.id
  http_method   = "GET"
  authorization = "NONE"
}
Enter fullscreen mode Exit fullscreen mode

If you want, you can define a way to check autorization with Cognito or IAM.


Integrate endpoints - API Gateway Integration

Terraform Documentation

Now, we integrate endpoints with differents services. Here is differents examples.

Lambda

resource "aws_api_gateway_integration" "integration" {
  rest_api_id             = aws_api_gateway_rest_api.api.id
  resource_id             = aws_api_gateway_resource.resource.id
  http_method             = aws_api_gateway_method.method.http_method
  integration_http_method = "POST"
  type                    = "AWS_PROXY"
  uri                     = aws_lambda_function.lambda.invoke_arn
}
Enter fullscreen mode Exit fullscreen mode

HTTP

resource "aws_api_gateway_integration" "test" {
  rest_api_id = aws_api_gateway_rest_api.api_gateway_pmt_apis.id
  resource_id = aws_api_gateway_resource.api_gateway_ressource_all.id
  http_method = aws_api_gateway_method.aws_api_gateway_method_root.http_method

  type                    = "HTTP"
  uri                     = "https://www.google.de"
  integration_http_method = "GET"
}
Enter fullscreen mode Exit fullscreen mode

Mock

This element can be really useful if you want to quickly expose a method for a frontend service. But to define completly a mock, we will need the two next elements.

resource "aws_api_gateway_integration" "MyDemoIntegration" {
  rest_api_id          = aws_api_gateway_rest_api.MyDemoAPI.id
  resource_id          = aws_api_gateway_resource.MyDemoResource.id
  http_method          = aws_api_gateway_method.MyDemoMethod.http_method
  type                 = "MOCK"
}
Enter fullscreen mode Exit fullscreen mode

Declare Mock HTTP Response - API Gateway Method Response

Terraform Documentation

When you want to mock a integration, you have to have to declare its response.

resource "aws_api_gateway_method_response" "response_200" {
  rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
  resource_id = aws_api_gateway_resource.MyDemoResource.id
  http_method = aws_api_gateway_method.MyDemoMethod.http_method
  status_code = "200"
}
Enter fullscreen mode Exit fullscreen mode

Declare Mock HTTP Response Body - API Gateway Integration Response

Terraform Documentation

resource "aws_api_gateway_integration_response" "MyDemoIntegrationResponse" {
  rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
  resource_id = aws_api_gateway_resource.MyDemoResource.id
  http_method = aws_api_gateway_method.MyDemoMethod.http_method
  status_code = aws_api_gateway_method_response.response_200.status_code

  # Transforms the backend JSON response to XML
  response_templates = {
    "application/xml" = <<EOF
#set($inputRoot = $input.path('$'))
<?xml version="1.0" encoding="UTF-8"?>
<message>
    $inputRoot.body
</message>
EOF
  }
}
Enter fullscreen mode Exit fullscreen mode

I hope it will help you!

Top comments (0)