Skip to main content

EventBridge Event Handler

As mentioned in several of the other messaging quick starts, the publisher/subscriber (pub/sub) pattern is extremely common in a serverless architecture. Severless encourages micro and someties even nano-sized components that are assembled together by way of contracts as opposed to building everything into a single binary.

AWS' EventBridge is a service that describes itself like this:

Amazon EventBridge Event Bus is a serverless event bus that helps you receive, filter, transform, route, and deliver events. - AWS

It provides a Default Bus or you are able to add Custom Event Buses to fit your need. This article will look to showcase how to create a Lambda function that handles an event from an EventBridge custom bus. It also takes the publishing component from the article on Event Bridge Put Events to give a cohesive pub/sub experience.

Video Walkthrough

If video is more your thing, then check out this walkthrough on YouTube. Otherwise, keep reading for the written documentation.

How It Works

The sample in this tutorial builds upon a Lambda that listens on a Function URL and then generates an EventBridge PutEvent with a custom domain model. A Rule is defined by the subscriber on the custom event bus that sends any matching messages to a Lambda function. The subscriber Lambda function will deserialize and process the message.

The EventBridge -> Lambda integration is an example of an async invoke. Internally inside the Lambda service the events are queued up onto an SQS queue managed by Lambda, and your function is invoked from here.

An important note, the sample application deploys the publisher, subscriber and event bus using the same infrastructure as code template. This is for ease of demonstration. Typically the 3 components would be deployed as 3 independent stacks.

Project Structure

A Lambda and EventBridge Event handler template is found under the ./templates directory in the GitHub repo. You can use template to get started building with EventBridge and Lambda.

The template is simple, and is based upon the following structure.


lambdas
- event-handler
- publisher
- shared

Lambda Code

Main

When handling messages from EventBridge with Lambda your Lambda code will look much like the other services covered as part of the messaging patterns. The main function sets up the logging framework, and then starts the Lambda runtime. Passing in the function to use as the handler.


#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.without_time()
.init();
run(service_fn(function_handler)).await
}

Handler Code

The handler code in this sample is deserializing the detail of the event that comes from EventBridge and using a handler from your custom business logic to actually process the message.

The LambdaEvent that comes into your handler function is of type CloudWatchEvent. Before EventBridge became it's own service, it was called CloudWatch events. Hence the name of the struct being CloudWatchEvent.

For re-usability, a custom InternalMessage struct is used as a wrapper around the CloudWatchEvent type that comes from the Lambda events Crate. This allows the try_into() function to be used to handle the conversion from the custom CloudWatchEvent type into the Payload type used by the application.

Note that if the try_into() call fails or the handle() function call fails the handler returns an error. This will return an error back to the Lambda runtime.


async fn function_handler(event: LambdaEvent<CloudWatchEvent>) -> Result<(), Error> {
let payload: Result<Payload, MessageParseError> = InternalMessage(event.payload)
.try_into();
match payload {
Ok(payload) => {
let _handle_res = PayloadHandler::handle(&payload).await;
match _handle_res {
Ok(_) => Ok(()),
Err(e) => Err(e.into())
}
}
Err(err) => {
Err(err.into())
}
}
}

By default, EventBridge retries sending the event for 24 hours and up to 185 times with an exponential back off and jitter, or randomized delay. You can control this retry behavior, and the routing to a dead letter queue, using your Lambda event source configuration. This example uses AWS SAM to automatically create an SQS queue for failures, and route any failed messages to the DLQ after only one retry.


EventHandlerFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Metadata:
BuildMethod: rust-cargolambda
BuildProperties:
Binary: event-bridge-handler
Properties:
FunctionName: serverless-rust-EventHandler
CodeUri: .
Handler: bootstrap
Runtime: provided.al2023
EventInvokeConfig:
MaximumRetryAttempts: 1
DestinationConfig:
OnFailure:
Type: SQS
Architectures:
- arm64
Events:
Trigger:
Type: CloudWatchEvent
Properties:
EventBusName: !GetAtt RustDemoEventBus.Name
Pattern:
source:
- RustDemo

Deploy

You can deploy this example directly to your own AWS account using the provided template. Simply clone the repo, and then run the below CLI commands from the repo root.


cd templates/patterns/messaging-patterns/eventbridge-handler/
sam build --beta-features
sam deploy

Once deployed, you can send a POST request to the Lambda function URL endpoint with the below body:


{
"name": "James",
"message": "Hello YouTube"
}

After running the POST request and getting back a 200 response, you can use the sam logs CLI command to retrieve the logs for your EventBridge handler function.


sam logs --profile sandbox --stack-name event-bridge-rust

Congrats

And that's all there is to it. This was a simple example but highlights how you can use Rust, Lambda and EventBridge to build high performance event driven systems.