How to Use Non-Proxy Integration with Amazon API Gateway: OpenAPI Specification

How to Use Non-Proxy Integration with Amazon API Gateway: OpenAPI Specification

In the article How to Use Non-Proxy Integration with Amazon API Gateway, we define resources such as AWS::ApiGateway::RestApi, AWS::ApiGateway::Resource, AWS::Lambda::Permission, and AWS::ApiGateway::Method in our AWS SAM template file instead of AWS::Serverless::Function and AWS::Serverless::Api due to their limitation in implementing the non-proxy integration. However, this method introduces a significant downside: redeploying the API to the corresponding stage must be done using an AWS CLI command. This necessity arises from the lack of this feature in CloudFormation resources, at least for Rest APIs. Fortunately, this feature is already included when using AWS::Serverless::Api resources.

So, in this article, we will explore an alternative to continue using only serverless resources. As we discuss in Linking an OpenAPI Specification to AWS Lambda Functions using AWS SAM, an OpenAPI specification can define an API. Run the following command:

aws apigateway get-export --parameters extensions='apigateway' --rest-api-id <rest_api_id>--stage-name prod --export-type oas30 openapi_with_extensions.yaml --accepts application/yaml

The resulting file will be the starting point for creating a file like this:

openapi: "3.0.1"
info:
  title: "apilambdanonproxy"
  description: "API with lambda non proxy integration"
paths:
  /pets:
    post:
      parameters:
      - name: "version"
        in: "header"
        required: true
        schema:
          type: "string"
      responses:
        "500":
          description: "500 response"
          headers:
            version:
              schema:
                type: "string"
          content: {}
        "200":
          description: "200 response"
          headers:
            id:
              schema:
                type: "string"
            version:
              schema:
                type: "string"
          content: {}
      x-amazon-apigateway-integration:
        httpMethod: "POST"
        uri: 
          Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyApiFunction.Arn}/invocations"
        responses:
          ".*An error was thrown.*":
            statusCode: "500"
            responseParameters:
              method.response.header.version: "'v1'"
            responseTemplates:
              application/json: " #set($inputRoot = $input.path('$')) { \"error\"\
                : \"$inputRoot.errorType\" }"
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.id: "integration.response.body.PetId"
              method.response.header.version: "'v1'"
            responseTemplates:
              application/json: " #set($inputRoot = $input.path('$')) { \"petId\"\
                : \"$inputRoot.PetId\" }"
        requestParameters:
          integration.request.header.version: "method.request.header.version"
        requestTemplates:
          application/json: " #set($inputRoot = $input.path('$')) { \"name\": \"$inputRoot.name\"\
            , \"throwError\":\"$input.params('throwerror')\" }"
        passthroughBehavior: "when_no_templates"
        type: "aws"
components: {}

As we can see, the section under the property x-amazon-apigateway-integration contains all the integration setup and can easily match what we did. For more information about the OpenAPI extensions for Amazon API Gateway, click here. Update the template.yml file as follows:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  SAM

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
     OpenApiVersion: '3.0.1'
     DefinitionBody:
       'Fn::Transform':
         Name: 'AWS::Include'
         Parameters:
           Location: openapi.yaml
     StageName: prod

  MyApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      Timeout: 60
      MemorySize: 512
      Tracing: Active
      Runtime: dotnet8
      Architectures:
        - x86_64    
      Handler: MyLambda::MyLambda.Function::FunctionHandler
      CodeUri: ./src/MyLambda/
      Events:
        Post:
          Type: Api
          Properties:
            RestApiId: !Ref MyApi
            Path: /pets
            Method: post

Outputs:
  MyApiEndpoint:
    Description: "API endpoint"
    Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/prod/pets"
  RestApiId:
    Description: "REST API id"
    Value: !Ref MyApi

Run the following commands to deploy the resources to AWS:

sam build
sam deploy --guided

As a result, we have the same API but using serverless resources only. You can find the final code here. Thanks, and happy coding.