How to use OpenAPI with API Gateway

Use an OpenAPI specification to define API Gateway APIs. This is good practice because:

  1. The specification forces you to think about the design upfront before touching code which is expensive to correct later
  2. This contract first approach allows frontend, backend, and test developers to work in parallel
  3. Endpoints can be mocked for developers to test against
  4. Serverless Application Model (SAM) uses the specification to configure the API Gateway API, this makes it living documentation, evolving alongside code and infrastructure.

TL;DR

  1. Create an OpenAPI specification with API Gateway Extensions to OpenAPI
  2. Copy the file to an S3 bucket
  3. Define an API Gateway (AWS::Serverless::Api) resource in your SAM template
  4. Use the AWS::Include transform to include and transform the OpenAPI specification from S3
  5. Assign the AWS::Include transform function to the DefinitionBody property of the API Gateway resource
  6. Package and deploy your SAM application

Source Code

The source code and instructions to build and deploy this example to AWS can be found here: https://github.com/karlkyck/api-gateway-openapi. Running this example on AWS will incur costs so be sure to delete the CloudFormation stacks when you are finished experimenting.

OpenAPI Definition

Start by creating your OpenAPI specification in a dedicated file.

The following OpenAPI specification defines one RESTful endpoint with two operations on that endpoint.

---
openapi: 3.0.0
info:
  title: API Gateway OpenAPI Example
  version: 1.0.0

paths:
  /api/posts:
    get:
      summary: List Posts
      operationId: listPosts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              '$ref': '#/components/schemas/CreatePostRequestBody'
      responses:
        '200':
          description: Retrieve the list of Posts
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/ListPostsResponseBody'
      x-amazon-apigateway-integration:
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListPostsFunction.Arn}/invocations
        httpMethod: POST
        type: aws_proxy
    post:
      summary: Create a new Post
      operationId: createPost
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                '$ref': '#/components/schemas/Post'
      x-amazon-apigateway-integration:
        uri:
          Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${CreatePostFunction.Arn}/invocations
        httpMethod: POST
        type: aws_proxy

components:
  schemas:
    BasePost:
      type: object
      required:
        - title
        - description
        - publishedDate
        - content
      properties:
        title:
          type: string
        description:
          type: string
        publishedDate:
          type: string
          format: date-time
        content:
          type: string
    Post:
      allOf:
        - $ref: '#/components/schemas/BasePost'
        - type: object
          required:
            - id
            - createdDate
            - updatedDate
          properties:
            id:
              type: string
            createdDate:
              type: string
              format: date-time
            updatedDate:
              type: string
              format: date-time
    CreatePostRequestBody:
      allOf:
        - $ref: '#/components/schemas/BasePost'
    ListPostsResponseBody:
      type: array
      items:
        $ref: '#/components/schemas/Post'

The two x-amazon-apigateway-integration API Gateway Extensions to OpenAPI references allow us to refer to the relevant Lambda function in our SAM template.

By using CloudFormation Intrinsic Functions in your OpenAPI specification such as the Fn::Sub (variable substitution) function calls in the example above, you can refer to the relevant Lambdas backing your API in your SAM template.

SAM Definition

The following SAM template creates an API Gateway API and two Lambdas, each with an API Gateway event defined.

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

Parameters:
  DeploymentBucket:
    Type: String

Globals:
  Function:
    Runtime: nodejs8.10
    Timeout: 180

Resources:

  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: live
      EndpointConfiguration: REGIONAL
      DefinitionBody:
        'Fn::Transform':
          Name: AWS::Include
          Parameters:
            Location: !Sub s3://${DeploymentBucket}/openapi.yaml

  ListPostsFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: ListPostsHandler.handler
      CodeUri: ./dist
      Events:
        ApiGatewayApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGatewayApi
            Path: /api/posts
            Method: get

  CreatePostFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: CreatePostHandler.handler
      CodeUri: ./dist
      Events:
        ApiGatewayApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGatewayApi
            Path: /api/posts
            Method: post

The important part to focus on here is the DefinitionBody parameter of the ApiGatewayApi resource. We are using the AWS::Include transform to include the OpenAPI specification from its S3 location.

The AWS::Include transform allows you to use CloudFormation Intrinsic Functions in your OpenAPI specification such as the Fn::Sub (variable substitution) function calls in the example OpenAPI specification.

Deployment

When deploying your SAM project CloudFormation will load the OpenAPI specification from the specified S3 location performing an AWS::Include transform. Intrinsic Function calls within the OpenAPI specification will be executed, thereby transforming your OpenAPI specification accordingly.

API Documentation

The API documentation is now linked to the API Gateway API. The OpenAPI specification is also bound to your SAM template by way of references to Lambda functions and other resources and parameters. Therefore the specification and SAM template evolve together.

The OpenAPI specification is also made available in the AWS console for other developers and teams to export and consume.