Here are four ways you can implement WebSockets using serverless

Yan Cui

I help clients go faster for less using serverless technologies.

This article is brought to you by

Don’t reinvent the patterns. Catalyst gives you consistent APIs for messaging, data, and workflow with key microservice patterns like circuit-breakers and retries for free.

Try the Catalyst beta

The myth that “you can’t do WebSockets with serverless” still persists today, even though we have some very good ways to implement WebSockets without needing to manage any servers.

Part of the problem is that many still falsely equate “serverless” with Lambda. But serverless is much more than that. To me, it describes any technology that:

  1. No need to manage servers.
  2. Scale to zero.
  3. Usage-based pricing with no minimum charge.

With this in mind, API Gateway, AppSync, and IoT Core are all serverless technologies. All three let you implement WebSockets.

Momento Topics [1] is another good option if you are open to exploring non-AWS options.

API Gateway

API Gateway WebSockets are very low-level and require a lot of work.

As the application developer, you must maintain the mapping from WebSocket connection IDs to users. You do this by implementing Lambda functions that handle API Gateway’s onConnect and onDisconnect events.

When sending messages to a user, you must find the user’s connection ID (in your own database) and call the API Gateway Management API to send the message.

Because you can only send messages one at a time, it can be very inefficient and costly to implement group chats or broadcasts.

Imagine building a sports streaming app and wanting to notify everyone watching Barcelona vs Real Madrid that a goal has gone in. If you have a million live viewers, then that translates:

  • a million reads from your DynamoDB table
  • a million API requests to the API Gateway Management API

Of all the options here, this is my least favourite.

AppSync

By comparison, AppSync subscriptions are a breeze to work with.

In the GraphQL schema, you connect a subscription operation (i.e. what a client can subscribe to) to a mutation operation.

That’s it! AppSync takes care of the rest.

Multiple subscribers can subscribe to the same update. When someone publishes a post, the update is automatically broadcast to all the subscribers.

Subscribers can filter what updates they want. To support this, you only have to add arguments to the subscription operation. Here, we allow the caller to filter on author, title, publishYear and publishMonth.

If you only want to receive new posts from “Cixin Liu” then you simply call

addedPost(author: "Cixin Liu") {
  id
  author
  title
  content
  url
}

If you want to learn more about AppSync subscriptions, then check out this explainer video [2].

IoT Core

Despite its name, the AWS IoT Core service is not just for IoT. It’s actually just a serverless messaging service that speaks MQTT [3].

You can subscribe and publish messages to topics. Pretty simple.

But it has some nice quality-of-life features because it’s designed with IoT in mind. For example, it can store messages if a device is offline and deliver them when it next comes online.

The quickest way to understand how it works is to try it out yourself. Head over to the IoT Core console and click “MQTT test client”. Here, you can subscribe to a topic (e.g. “test”) and then publish a message to the topic.

Momento Topics

Momento Topics [1] are conceptually very simple. You can subscribe to a topic and receive and publish messages through a WebSocket connection.

Allen Helton wrote a nice article [4] on the architecture behind Momento Topics. I highly recommend giving it a read.

The innovation that Momento Topics brings to the table is its pricing structure.

AppSync, API Gateway and IoT Core all charge based on a combination of:

  • Number of messages sent and received through WebSockets.
  • Connection time.
  • Standard EC2 data transfer cost. Which is code for “it’s complicated” [5].

Momento, on the other hand, offers a pay-per-use pricing model. Even if you have millions of connected users, you don’t pay for the connections if they are idle.

This offers a different cost dimension that we can use to align with our business model.

The Frugal Architect [6] Law 2 says that our architecture’s cost should grow along the same dimension as how the business would make money.

In other words, if your business revenue grows with user engagement, then so should your architecture’s cost. If millions of connected but inactive users don’t generate revenue for your business, then connection time shouldn’t be the driver of your cost neither.

Comparison

Of the four options we discussed here, API Gateway WebSockets requires the most effort to implement. Again, it’s a low-level construct, and you must work with connections and map them to users.

API Gateway also doesn’t support broadcasts. You can only send one message at a time.

However, it’s a two-way (i.e. duplex) connection. The client can send and receive messages through the WebSocket connection. It’s also a vanilla WebSockets connection, and you can develop your own application protocol on top of it.

With AppSync and IoT Core, your application must speak GraphQL and MQTT, respectively. This can be a barrier to entry because it requires additional application dependencies, and you need to understand how these protocols work.

Security

Regarding security, all four options provide some form of authentication and authorization mechanism.

With API Gateway, you can use AWS IAM or a Lambda authorizer to protect the WebSocket API. Once connected, you can decide what data is sent to the user and validate any messages received from the user.

You have a lot of control here but also a lot of responsibilities!

With AppSync, you can use all the available authentication and authorization mechanisms [7] to protect the Subscription endpoint. This controls who can subscribe for updates.

Additionally, you can use a resolver template to control who can subscribe to which updates [8]. For example, to stop a user from subscribing to notifications intended for another user.

With MQTT and Momento Topics, you can control who can access which topic. To ensure a user can’t subscribe to other users’ updates, you should scope a user’s permission to just his/her topic. Luckily, in both cases, topics are a virtual construct and do not need to be explicitly created before use. So there’s no limit on how many topics you can have.

With all this in mind, here’s a summary of how these services stack up.

The key takeaway is that you have several options for implementing WebSockets serverlessly. Each offers a slightly different set of trade-offs. And that’s a great place to be.

If you want to learn more about building serverless applications, then check out my upcoming workshop [9] and get 15% OFF when you subscribe to our newsletter.

Links

[1] Momento Topics

[2] What are AppSync Subscriptions?

[3] The MQTT specification

[4] How we built Momento Topics, a serverless WebSocket service

[5] Understanding data transfer cost in AWS

[6] The Frugal Architect

[7] AppSync authentication and authorization

[8] Authorized AppSync Subscriptions

[9] Production-Ready Serverless workshop

Related Posts

Whenever you’re ready, here are 3 ways I can help you:

  1. Production-Ready Serverless: Join 20+ AWS Heroes & Community Builders and 1000+ other students in levelling up your serverless game. This is your one-stop shop for quickly levelling up your serverless skills.
  2. I help clients launch product ideas, improve their development processes and upskill their teams. If you’d like to work together, then let’s get in touch.
  3. Join my community on Discord, ask questions, and join the discussion on all things AWS and Serverless.