AWS CDK: API Gateway Service Integrations for Amazon EventBridge

October 31st, 2023 535 Words

The previous post described how to create an Amazon EventBridge Event Bus with Amazon CloudWatch Log forwarding using the AWS CDK. Publishing events works fine with the AWS Management Console or the AWS CLI, but you can also use Service Integrations for Amazon API Gateway; this works for HTTP and Rest API Gateways.

AWS Command Line

Quick recap: You can use the AWS CLI to post events to Amazon EventBridge. Create a local payload.json file with the events and run the command:

[
  {
    "Source": "cdk.application.local",
    "Detail": "{ \"message\": \"Welcome!\" }",
    "DetailType": "message",
    "EventBusName": "cdk-application-main"
  }
]
$ > aws events put-events --entries file://payload.json

{
    "FailedEntryCount": 0,
    "Entries": [
        {
            "EventId": "f9209ca7-2546-d5ea-3d42-76cee1392359"
        }
    ]
}

With Amazon API Gateway, you can wrap calling the Amazon EventBridge API for posting events. This allows simple integrations with other systems and distributed components; the AWS API is nice, and a clean HTTPS interface is even better.

Cloud Development Kit

Based on the TypeScript examples for creating the CloudFormation Stack, you can add a custom Amazon API Gateway service integration for Event Bridge next.

Rest API Gateway

To get started, create a new Rest API Gateway:

const api = new RestApi(this, 'api', {
  restApiName: 'api-rest',
});

As usual, create an IAM Role next. This role needs to allow access to the events:PutEvents action for your EventBridge Event Bus:

const credentialsRole = new Role(scope, 'role', {
  assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
  inlinePolicies: {
    'PutEvents': new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: [ 'events:PutEvents' ],
          effect: Effect.ALLOW,
          resources: [ bus.eventBusArn ],
        }),
      ]
    })
  }
});

Now, the painful Velocity Template mappings need to be created. Based on the service integration for SQS and API Gateway, this can be adapted to a configuration like this:

const integration = new AwsIntegration({
  service: "events",
  action: 'PutEvents',
  integrationHttpMethod: "POST",
  options: {
    credentialsRole,
    integrationResponses: [
      {
        statusCode: '200',
        responseTemplates: {
              'application/json': `{ "requestId": "$context.requestId" }`,
        },
      },
    ],
    requestTemplates: {
      'application/x-www-form-urlencoded': `
        #set($context.requestOverride.header.X-Amz-Target = "AWSEvents.PutEvents")
        #set($context.requestOverride.header.Content-Type = "application/x-amz-json-1.1")            
        { 
          "Entries": [
            {
              "Detail": "{\\"message\\": \\"$util.escapeJavaScript($input.body).replaceAll("\\'","'")\\"}",
              "DetailType": "message",
              "EventBusName": "${bus.eventBusName}",
              "Source":"cdk.application.api.rest"
            }
          ]
        }
      `,
    },
  }
})

const events = this.api.root.addResource("events");
const options = { methodResponses: [{ statusCode: '200' }] };

events.addMethod("POST", integration, options);

After using cdk deploy , you can use cURL to submit events for your API:

$ > curl -X POST \
    https://a1b2c3d4.execute-api.eu-central-1.amazonaws.com/prod/events \
    -d 'Welcome!'

HTTP API Gateway

Same as for the Rest API Gateway, start with creating the API and IAM Role:

const api = new HttpApi(this, 'api', {
  apiName: 'api-http'
});

const role = new Role(this, 'role', {
  assumedBy: new ServicePrincipal("apigateway.amazonaws.com"),
  inlinePolicies: {
    'PutEvents': new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: [ "events:PutEvents" ],
          effect: Effect.ALLOW,
          resources: [ bus.eventBusArn ],
        }),
      ]
    })
  }
})

Again, you need to configure a Service integration; this time using the provided HttpIntegration construct.

const integration = new HttpIntegration(scope, `integration`, {
  integrationType: HttpIntegrationType.AWS_PROXY,
  httpApi: this.api,
  integrationSubtype: HttpIntegrationSubtype.EVENTBRIDGE_PUT_EVENTS,
  credentials: IntegrationCredentials.fromRole(role),
  payloadFormatVersion: PayloadFormatVersion.VERSION_1_0,
  parameterMapping: new ParameterMapping()
    .custom('Source', 'cdk.application.api.http')
    .custom('DetailType', `message`)
    .custom('Detail', "{\"message\":\"\${request.body}\"}")
    .custom('EventBusName', bus.eventBusName),
})

const apiRoute = new CfnRoute(scope, `route`, {
  apiId: this.api.apiId,
  routeKey: `POST /events`,
  target: `integrations/${integration.integrationId}`,
})

Using cURL , as for the HTTP API Gateway, you can use a simple HTTPS request to submit events to your EventBridge Event Bus. That’s nice! 🎉