When I first wrote about building a serverless chat on AWS using AppSync Events, it was lacking two key capabilities:
- two-way communication over WebSockets, and
 - message persistence on publish
 
Since then, AppSync Events API received some improvements, including these two capabilities that it initially lacked. Having tested this first hand on a simple example, my aim with this post is to share impressions of what now seems to be a way more complete service offering. Spoiler alert - the impressions are rather good, and make sure to stick around to the end as code example is included!
Before
There were some workarounds proposed at the time by AWS themselves, but mine was maybe a bit more robust, coming with the complexity trade-off. You can read the previous article here, but this is how the architecture looks like:
I made a clear separation - use AppSync Events only for subscribing, while I had a separate API for publishing. Messages were stored and streamed to AppSync, making their way to all subscribers. This isn't ideal, but ensures consistency.
After
Given the new features AWS introduced to the service, now it's possible to achieve even better performance with even simpler architecture. The new solution looks like in the below diagram:
With these improvements we remove several elements of the previous solution:
- No DynamoDB Streams
 - No EventBridge Pipes
 - No separate API endpoint for publishing messages
 
The new improvements allow sending and receiving messages completely over WebSockets natively using AppSync Events realtime endpoint, so there is no need for workarounds anymore. We still keep the GET API endpoint for fetching existing messages, so no change regarding that. Note that this can be simplified even further by using only Lambda Function URL, but we'll be keeping the approach with API Gateway. Now, let's address what's needed to make the new solution work.
Making it work
As with both previous articles we will be using AWS CDK for the project. Perhaps the most interesting part is creating the AppSync Events API, configuring the channel namespace, and the publishing handler function. All of that is set up in this piece of CDK code:
// AppSync Event API, channel namespace, and handler
const eventApi = new EventApi(this, 'ChatAppSyncEventsApi', {
    apiName: 'ChatAppSyncEventApiWs',
});
// Add AppSync DynamoDB data source & add needed permission
const appSyncDynamoDbDataSource = eventApi.addDynamoDbDataSource('ddbsource', table);
table.grantReadWriteData(appSyncDynamoDbDataSource);
// create namespace with datasource and handler for onPublish
eventApi.addChannelNamespace('serverlesschat', {
    code: AppSyncCode.fromAsset(path.join(__dirname, '../src/appsyncjs/serverlesschat-handler.js')),
    publishHandlerConfig: {
        dataSource: appSyncDynamoDbDataSource,
    },
});
Simple enough, right? Let's break it down a bit.
Channel namespaces
Channel namespaces are something that's different than when we built this solution with IoT Core Topics. Topics are pretty loose and you can define your own topics without prior declaration or creation. While AppSync requires explicit declaration of the namespace first, the channels you can create within it are still very flexible.
Event handlers & data sources
This is the real breakthrough feature addition - more complex handlers and the ability to interact with data sources. This is what enables us to ditch the whole workaround from the previous solution that was using a separate REST API for storing messages to DynamoDB, and then streaming them to EventBridge Pipes to get them to subscribers. In the new solution, messages are published directly via WebSockets, and stored into DynamoDB using the onPublish event handler - no need for any additional streaming as subscribers get messages directly.
Two types of handlers exist - onSubscribe and onPublish, and can be used for various things, such as transformations, persistence to a data source, or even authorization. They are written in JS, using the APPSYNC_JS runtime, which might be familiar to users of AppSync already. The scope of the article is not to go deep dive into this topic, even though it very well could - for those interested in more details feel free to check out official AWS CDK docs on this.
Seeing it in action
To test the solution yourself, checkout the code and follow the README at:
https://github.com/imflamboyant/serverless-aws-chat/tree/main/chat-appsync-events-websocket
Thanks for reading! Have some ideas how you would extend the solution? Do let me know in the comments!
              

    
Top comments (0)