AWS AppSync Events

Srikar Gandhi
6 min readFeb 10, 2025

--

Recently, I got a requirement to notify the client applications running on-prem whenever a specific event occurs in the AWS cloud environment, which the client might be interested in as shown below. I considered several options but ultimately chose AWS AppSync Events.

Options Considered

Polling

Clients poll over HTTP for updates on topics they are interested in. This approach adds unnecessary traffic and requires scaling the application handling the polling requests. Additionally, maintaining metrics and logging is an overhead. I needed a serverless solution that provided these features out of the box.

AWS IoT Core

AWS IoT Core over MQTT protocol was an option, but for .NET clients, AWS STS access key and secret key authentication did not work. Certificate-based authentication was worked, but managing certificates for on-prem client applications became complex, especially since the on-prem environment was managed by an external client.

AWS API Gateway Web Sockets

With API Gateway, you need to manage your own connections. This means listening to $connect and $disconnect events and storing connection IDs in a database (DynamoDB or Redis ). Since $disconnect events are not guaranteed for every connection, you are responsible for garbage collection.

Sending a message requires invoking the API and is limited to one connection at a time. If you need to send messages to all connected users, you must loop over all stored connections and send messages individually. You could offload this task to an SQS queue and a Lambda function, but it’s not ideal.

Why AWS AppSync Events?

Given these constraints, I finalized AWS AppSync Events, which met all my requirements and provided built-in metrics, logging, and a fully managed WebSocket connection service.

In this blog, I will walk you through the code to implement a client that integrates with AWS AppSync Events.

AWS AppSync Events Overview

AWS AppSync Events lets you create secure and performant serverless WebSocket APIs that can broadcast real-time event data to millions of subscribers without needing to manage connections or resource scaling. No API code is required to get started, allowing you to create production-ready real-time web and mobile experiences in minutes.

Use Cases

  • Live chat and messaging
  • Sports and score updates
  • Real-time in-app alerts and notifications
  • Live commenting and activity feeds

AWS AppSync Events Features

WebSocket and HTTP Support

  • Clients can publish events over HTTP and subscribe to channels using WebSockets.
  • Event APIs provide WebSocket endpoints that enable real-time pub/sub capabilities.

Channel Namespaces and Channels

  • Events are published to channels, which are grouped under namespaces.
  • Namespaces allow you to define authentication rules, authorization policies, and serverless functions that apply to all channels within that namespace.

Namespace Handlers

You can configure functions to run when events are published or when clients subscribe:

  • OnPublish — Runs when an event is published to a channel, allowing you to transform, filter, or reject events.
  • OnSubscribe — Runs when a client subscribes to a channel, allowing you to customize the behavior or reject subscription requests.

Flexible Authentication and Authorization

AWS AppSync Events supports multiple authentication mechanisms:

  • API Key
  • IAM
  • Amazon Cognito
  • OpenID Connect (OIDC)
  • Lambda Authorizers

Authentication can be configured at both the API level and channel namespace level.

Channel Subscriptions

  • Clients receive events for the channels they subscribe to.
  • Wildcard Channel Subscriptions allow clients to subscribe to a group of related channels using wildcard syntax (e.g., namespace/channel/*).

Scalable Event Broadcasting

  • AWS AppSync Events automatically scales to handle a large number of concurrent connections and efficiently broadcasts events to all subscribed clients.

Integration with AWS Ecosystem

  • AWS AppSync Events integrates with Amazon CloudWatch Logs, CloudWatch Metrics, and AWS WAF.
  • Events can be published directly from Amazon EventBridge, AWS Lambda, and other AWS services.
  • Amazon Cognito is directly supported as an authentication provider.

Cost Estimation

  • 1000 clients connected for 24 hours & 30 days
  • 1000 connections x 1,440 minutes per day x $0.08 per million minutes = 0.1152 per day => 30 * 0.1152 = 3.45 per month

Proof of Concept (PoC)

We will build a .NET Console and React Client Application to subscribe to a channel. These applications are hosted on-prem and are interested in specific event notifications published over AWS AppSync Events.

Flow

  1. A Lambda function publishes a message to a channel.
  2. AWS AppSync broadcasts the message to all subscribed clients.
  3. The on-prem applications receive real-time updates via WebSockets.

Implementation

Publisher Code (C#)

The following C# code demonstrates how to publish an event to AWS AppSync using HttpClient.

  public class HttpClientHelper
{
private static readonly HttpClient _httpClient = new HttpClient();
public static async Task PublishEventAsync(string channel, string message)
{
var eventsList = new List<string>
{
JsonSerializer.Serialize(new Message { EventDetails = new List<EventDetail>()
{ new EventDetail() { Data = message } } })
};
var jsonContent = new StringContent(
JsonSerializer.Serialize(new EventData { channel = channel, events = eventsList }),
Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, $"https://{Config.HttpDomain}/event")
{
Content = jsonContent
};
request.Headers.Host = Config.HttpDomain;
request.Headers.Add("x-api-key", Config.ApiKey);
var response = await _httpClient.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"✅ Message published: {result}");
}
}
public class EventData
{
public string channel { get; set; }
public List<string> events { get; set; }
}
public class Message
{
public List<EventDetail> EventDetails { get; set; }
}
public class EventDetail
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Name { get; set; } = Guid.NewGuid().ToString();
public string Data { get; set; }
}

Subscriber Code (C#)

The following C# code demonstrates how to subscribe to an AWS AppSync event channel using WebSockets.

public class WebSocketClient
{
private readonly Uri _url;
private readonly ClientWebSocket _webSocket;
private readonly string _authHeader;
public event Action<string>? OnMessageReceived; // Event for received messages
public class SumbscriptionMessage
{
public string type { get; set; }
public string id { get; set; }
public string channel { get; set; }
public Authorization authorization { get;set; }
}
public class Authorization
{
[JsonPropertyName("x-api-key")]
public string x_api_key { get; set; }
public string host { get; set; }
}
public WebSocketClient()
{
_url = new Uri($"wss://{Config.RealTimeDomain}/event/realtime");
_authHeader = GetAuthProtocol();
_webSocket = new ClientWebSocket();
_webSocket.Options.AddSubProtocol("aws-appsync-event-ws");
_webSocket.Options.AddSubProtocol(_authHeader);
}
private string GetAuthProtocol()
{
var authJson = $"{{\"x-api-key\":\"{Config.ApiKey}\", \"host\":\"{Config.HttpDomain}\"}}";
var authBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authJson))
.Replace("+", "-")
.Replace("/", "_")
.TrimEnd('=');
return $"header-{authBase64}";
}
public async Task ConnectAsync()
{
try
{
await _webSocket.ConnectAsync(_url, CancellationToken.None);
Console.WriteLine("✅ WebSocket connected");
// Send connection initialization message
var initMessage = Encoding.UTF8.GetBytes("{\"type\": \"connection_init\"}");
await _webSocket.SendAsync(new ArraySegment<byte>(initMessage), WebSocketMessageType.Text, true, CancellationToken.None);
// Start listening for messages
_ = Task.Run(ReceiveMessagesAsync);
}
catch (Exception ex)
{
Console.WriteLine($"❌ WebSocket connection failed: {ex.Message}");
}
}
public async Task SubscribeAsync(string channel2)
{
if (_webSocket.State != WebSocketState.Open)
{
Console.WriteLine("❌ WebSocket is not open. Cannot subscribe.");
return;
}
// Construct the correct subscription payload
var message = new SumbscriptionMessage
{
type = "subscribe",
id = Guid.NewGuid().ToString(),
channel= channel2,
authorization = new Authorization()
{
x_api_key = Config.ApiKey,
host = Config.HttpDomain
}
};
string jsonMessage = JsonSerializer.Serialize(message);
byte[] messageBytes = Encoding.UTF8.GetBytes(jsonMessage);
Console.WriteLine($"📡 Sending subscription request: {jsonMessage}");
await _webSocket.SendAsync(new ArraySegment<byte>(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None);
}
private async Task ReceiveMessagesAsync()
{
var buffer = new byte[4096];
while (_webSocket.State == WebSocketState.Open)
{
var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
Console.WriteLine("🔴 WebSocket closed.");
break;
}
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"📩 Received: {message}");
// Notify subscribers
OnMessageReceived?.Invoke(message);
}
}
}

Configuration

   public static class Config
{
public static string RealTimeDomain = "abc.appsync-realtime-api.us-east-1.amazonaws.com"; // Example: abcdefg.appsync-api.us-east-1.amazonaws.com
public static string HttpDomain = "abc.appsync-api.us-east-1.amazonaws.com"; // Example: abcdefg.appsync-api.us-east-1.amazonaws.com
public static string ApiKey = "test-kay";
}

Conclusion

AWS AppSync Events provides a serverless, scalable, and fully managed WebSocket API service, making it an ideal choice for real-time event broadcasting.

--

--

No responses yet