Use an OpenAPI specification to define API Gateway APIs. This is good practice because:
- The specification forces you to think about the design upfront before touching code which is expensive to correct later
- This contract first approach allows frontend, backend, and test developers to work in parallel
- Endpoints can be mocked for developers to test against
- 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
- Create an OpenAPI specification with API Gateway Extensions to OpenAPI
- Copy the file to an S3 bucket
- Define an API Gateway (AWS::Serverless::Api) resource in your SAM template
- Use the AWS::Include transform to include and transform the OpenAPI specification from S3
- Assign the AWS::Include transform function to the DefinitionBody property of the API Gateway resource
- 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.