A neat feature of Amazon Web Service's API Gateway service is that it can integrate directly with other AWS services. The most common use of API Gateway is to integrate directly with a Lambda function, typically to perform an action like update a DynamoDB table or send a message to an SQS queue. But, sometimes it is not actually necessary to use a Lambda function at all - by taking advantage of API Gateway's AWS service integration, we can avoid this intermediary step altogether and build a much more efficient and resilient architecture.
I recently had a need to provision a simple API endpoint that would accept a JSON payload and store the data to be processed later. Typically I would look to use API Gateway backed by a basic Lambda function to accept the JSON data and store it in an SQS queue. Instead, for this project I opted to use the API Gateway AWS service integration with the SQS queue.
By using the AWS service integration, we benefit in a number of ways:
There are a number of blog posts that do an excellent job of describing how to set up this API Gateway to SQS architecture, but none that explain how to do the same using CloudFormation, Amazon's "Infrastructure as Code" solution.
After a bit of trial and error, I managed to come up with the below Cloudformation template which does the following:
Description: API Gateway integration with SQS Outputs: ApiEndpoint: Description: Endpoint for this stage of the api Value: !Join - '' - - https:// - !Ref 'APIGateway' - .execute-api. - !Ref 'AWS::Region' - .amazonaws.com/ - prod QueueArnSQS: Description: ARN of SQS Queue Value: !GetAtt 'DestQueue.Arn' Parameters: queueName: Description: The name of the SQS queue to create. Type: String Resources: APIGateway: Properties: Description: API Endpoint to receive JSON payloads and queue in SQS Name: APIGateway Type: AWS::ApiGateway::RestApi APIGatewayRole: Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - apigateway.amazonaws.com Version: '2012-10-17' Path: / Policies: - PolicyDocument: Statement: - Action: sqs:SendMessage Effect: Allow Resource: !GetAtt 'DestQueue.Arn' - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: '*' Version: '2012-10-17' PolicyName: apig-sqs-send-msg-policy RoleName: apig-sqs-send-msg-role Type: AWS::IAM::Role DestQueue: Properties: DelaySeconds: 0 MaximumMessageSize: 262144 MessageRetentionPeriod: 1209600 QueueName: !Ref 'queueName' ReceiveMessageWaitTimeSeconds: 0 VisibilityTimeout: 30 Type: AWS::SQS::Queue PolicySQS: Properties: PolicyDocument: Statement: - Action: SQS:* Effect: Allow Principal: '*' Resource: !GetAtt 'DestQueue.Arn' Sid: Sid1517269801413 Version: '2012-10-17' Queues: - !Ref 'DestQueue' Type: AWS::SQS::QueuePolicy PostMethod: Properties: AuthorizationType: NONE HttpMethod: POST Integration: Credentials: !GetAtt 'APIGatewayRole.Arn' IntegrationHttpMethod: POST IntegrationResponses: - StatusCode: '200' PassthroughBehavior: NEVER RequestParameters: integration.request.header.Content-Type: '''application/x-www-form-urlencoded''' RequestTemplates: application/json: Action=SendMessage&MessageBody=$input.body Type: AWS Uri: !Join - '' - - 'arn:aws:apigateway:' - !Ref 'AWS::Region' - :sqs:path/ - !Ref 'AWS::AccountId' - / - !Ref 'queueName' MethodResponses: - ResponseModels: application/json: Empty StatusCode: '200' ResourceId: !Ref 'enqueueResource' RestApiId: !Ref 'APIGateway' Type: AWS::ApiGateway::Method enqueueResource: Properties: ParentId: !Ref 'v1Resource' PathPart: enqueue RestApiId: !Ref 'APIGateway' Type: AWS::ApiGateway::Resource prodDeployment: DependsOn: PostMethod Properties: RestApiId: !Ref 'APIGateway' Type: AWS::ApiGateway::Deployment prodStage: Properties: DeploymentId: !Ref 'prodDeployment' RestApiId: !Ref 'APIGateway' StageName: prod Type: AWS::ApiGateway::Stage v1Resource: Properties: ParentId: !GetAtt 'APIGateway.RootResourceId' PathPart: v1 RestApiId: !Ref 'APIGateway' Type: AWS::ApiGateway::Resource