Build Serverless WebSockets with AWS AppSync Events and Powertools for AWS Lambda
- Ran Isenberg
- 4 minutes ago
- 7 min read

AWS AppSync Events API is a serverless WebSocket service with the ability to broadcast real-time events to millions of subscribers.
With its latest update, you can connect channel namespaces directly to AWS Lambda, DynamoDB, Aurora, and more.
However, getting everything running isn't smooth sailing, especially when using a Lambda function. That's where Powertools for AWS Lambda library comes in!
In this post, you'll learn how to build an AppSync Events API with AWS CDK, connect a Lambda event handler to a namespace, and process every publish and subscription event using Powertools for AWS Lambda.
Table of Contents
AppSync Events API Recap
AWS AppSync Events lets you create secure and performant serverless WebSocket APIs that can broadcast real-time event data to millions of subscribers, without you having to manage connections or resource scaling. - AWS
AppSync Events is AWS's new interpretation of serverless websockets—no, it's not related to GraphQL. It's a new thing that, unfortunately, shares the AppSync name and probably internal implementation from AppSync websockets.
It removes the need to store connections in a database (I'm looking at you API Gateway Websocket APIs) and allows you to send messages to potentially millions of clients with one HTTP or SDK call.
The concept is simple. Once clients connect to the AppSync namespace, they can try to subscribe to a channel. The cool trick here is connecting to multiple channels on the same web socket. It gives the illusion of numerous "pub-sub" topics/channels, but it's using the same single abstracted web socket underneath. In addition, you can publish to either a WebSocket endpoint or an HTTP endpoint, opening endless use cases for event-driven architectures.
If you want to learn more about this exciting new service, check out my previous blog post, "AWS AppSync Events—Serverless WebSockets Done Right or Just Different?"
On April 24th, 2025, AWS introduced new data source integrations.
AWS AppSync Events Data Source Integrations
With this new capability you can associate AWS Lambda functions, Amazon DynamoDB tables, Amazon Aurora databases, and other data sources with channel namespace handlers. - AWS
Data source integration sounds cool, albeit the naming schema which is misleading and comes from its AppSync origins. It's not really a data source but just a data integration, if any, a data destination. If anyone from the AppSync team is reading this, please stop bringing the AppSync naming schemas here.
According to this detailed blog post on AWS, with AWS AppSync Events, you can build rich, real-time applications with features like data validation, event transformation, and persistent storage of events.
So, this is defined at the namespace level (and you can have multiple namespaces on the API), which provides plenty of flexibility.
We have two types of event handler integrations:
Publish - save message payload in a DB or transform it (remove curse words, or PII).
Subscribe - extra authorization options or save user info/actions/history into a DB.
Event Handler Definition
Each integration (unless it's a Lambda function ) can be done a "code" - which you write in JS (not a fan) that does something (write to a DynamoDB table, etc.) when someone publishes a message or subscribes to a channel within your namespace. It's like API Direct integrations (check out my blog post, why I don't use it) where VTL is replaced with JS. It's better but still limited.
For Lambda data source, it's even more complicated. You can have a 'code' integration and write code that invokes your Lambda function (which seems quite useless!) or direct—now we are talking!
There are two modes for direct Lambda function integrations: RequestResponse and Event. 'Event' is an asynchronous invocation where your Lambda can fail, and it won't impact the publish/subscription event. I'd rather have the option to push the event to an SQS queue or EventBridge (which exists but requires you to write the JS code that publishes to it).
'RequestResponse', on the other hand, is a synchronous invocation where a failure in the Lambda function causes the publish/subscribe event to fail.
Lastly, you can even create an event handler with code but no data source; just write a JS handler yourself. I don't think you can add external dependencies, so it's useful for simple transformations.
My Impressions
Overall, it's super complicated and confusing, BUT it's also very powerful when you use Lambda event handler. Yes, you get a lot of flexibility here, but that's just too much. A simple SQS/EB for async flow and a simple synchronous invoke of a Lambda function would have been more than enough. I totally agree with Jeremy Daly on this.
Setting a Lambda function as a data source and event handler will give us the most flexibility. We can use our own function, set best practices, use a logger, metrics, and proper observability. We will also need to parse incoming events (which have their schemas) and respond in the correct response schemas.
Luckily, to do that, we will use Powertools for AWS Lambda for all the above.
Powertools for AWS Lambda Recap
Powertools for AWS Lambda is an amazing open-source library that provides production-ready utilities such as logging, input validation, and observability with X-ray traces. There are several runtimes supported, but I will focus on Python.
I've written an entire series on these utilities. If you are unfamiliar with them, start with the logging blog post.
Starting version 2.19 for TS and 3.11 for Python, Powertools added the AppSync events event handler. It provides a familiar FastAPI/Flask experience way to handle both publish and subscribe events with simple decorators. I will add other utilities to show you a production-ready handler. In addition, Powertools handle error handling and send responses back to AppSync according to the function's responses - exception or success.
Sounds promising, right? It is, but there are some caveats. More on that later below.
Let's Build the Infrastructure
OK, so let's try and build with CDK the following service:
AppSync Events API
Namespace called 'default'
Lambda function data source that handles both publish and subscribe events. We will use Powertool's new AppSync event handler.
Set the Lambda function from number 3 as an event handler with direct integration and no code (invoke our function). I chose 'RequestResponse' mode as I want to control and fail the event that the input is invalid or the user is not authorized to subscribe to the channel.
Enable logging on AppSync Events.
Associate a WAF ACL with the AppSync Events API. If you don't know what WAF is, check out my blog post here.
Let's review the AWS CDK construct below that builds all these resources:
Deploying this construct will create the AppSync Events API:

With a 'default' namespace:

and the event handlers:

and the logging and WAF definitions:

Next, explore the Lambda handler code to see how the new Powertools' AppSync makes your life much easier!
AppSyncEventsResolver Mechanics
Let's take a look at simplified version of the publish feature:

The DevEx is simple, and it works great. Powertools parses the incoming event (see JSON below—a list of dictionaries; each item has an ID and requires a response by itself), extracts the relevant payload, and calls the relevant handler according to the channel with each of the items in the dictionary one by one.
In this case, we wrote a specific channel handler for 'default/channel.' If somebody sends a message to 'default/test,' our channel handler will not get called.
Let's take a look at the event our function can receive:
Notice lines 32-45. This is the payload we want to handle.
Each item in the list can have a different schema and requires a response. You need to parse each one. You process them one by one and send the return value of each item.
If you raise an exception, Powertools will catch it and send an error response for you for that specific item out of the entire list. Amazing. This is quite similar to the SQS batching experience.
If you wish to receive all dictionary events simultaneously, you can use the "aggregate" feature. In this mode, you must manage the response and iterate the items yourself. You get a worse DevEx, and there's more room for error.
I don't know why you'd want to do that, but you can.
In addition, there is one glaring missing issue in the validation of the message payload itself. Your publish handler gets a dictionary and unvalidated item. You need to process it, and parse it yourself and raise an exception if it fails schema validation. In the other event handlers by Powertools, validation was baked in (SQS, DynamoDB streams etc.). I'm sure it's just a matter of time until the team (or me!) opens a PR and adds it.
Let's move to subscription handling:

As you can see, there's support for wildcard patterns (it also works for publish handlers). From the docs:
Only the following patterns are supported:
/namespace/*Â - Matches all channels in the specified namespace
/*Â - Matches all channels in all namespaces
Patterns like /namespace/channel* or /namespace/*/subpath are not supported.
More specific routes will always take precedence over less specific ones. For example, /default/channel1Â will take precedence over /default/*, which will take precedence over /*.
In my opinion, I'd rather NOT use it and be clearer in my code. If I need a special channel handle, I'll add it myself. However, if you want to write just one default function to handle ALL channels, that's okay—useful for the subscribe handler. Just be careful with it.
The more channels, the more complicated the code is when you have wildcards, as there's hidden precedence implemented.
Overall, it was a similar experience to the publish, and we also got access to the channel path, which is very helpful.
Now, let's make a Lambda handler production-ready code.
The Lambda Handler
In this Python Lambda function handler, I'm going to use the following utilities:
Powertool's logger for writing JSON formatted CloudWatch logs.
Powertool's tracer for integrating with AWS X-Ray tracing.
Powertool's metrics for writing custom CloudWatch logs.
Powertool's NEW AppSync events event handler, the AppSyncEventsResolver.
aws-lambda-env-modeler for parsing and serializing the environment variables with Pydantic.
In lines 91-94, we initialize our utilities and serialize our environment variables.
In line 96, we send the event to the AppSyncEventsResolver class, and it parses the input and calls one of the inner handlers according to the path and action, such as subscribe or publish.
Summary
AWS AppSync Events API lets you build serverless WebSocket applications with native integrations to Lambda, DynamoDB, Aurora, and more—no custom connection management is required. With the latest Powertools for AWS Lambda support, you can easily handle publish and subscription events using production-ready utilities like event handlers with logging, tracing, and custom CW metrics. Remember that every AppSync Events action—including event handler triggers—costs $1 per million operations.
While event handlers with data sources are confusing, poorly named, and not cheap, they are very flexible, and once you understand how to use them, you can build powerful integrations with them.