Anthony Chu Contact Me

Broadcast Real-time Updates from Cosmos DB with SignalR Service and Azure Functions

Monday, September 24, 2018

Azure Cosmos DB has a nifty change feed feature that tracks create and update events on documents in a collection. Applications, including serverless apps built with Azure Functions, can read events from the change feed and listen to new events on the change feed in real-time.

Azure SignalR Service is a managed service that allows applications to broadcast real-time messages to many connected clients over WebSockets and other supported transports.

September 24, 2018 - Azure SignalR Service is now generally available!

March 6, 2019 - SignalR Service bindings for Azure Functions are now generally available!

In this article, we'll look at how we can use Azure Functions and SignalR Service to broadcast real-time document changes in Cosmos DB to clients over WebSockets.

Overview

Here's how all the pieces fit together.

  1. A document is created or updated in Cosmos DB.
  2. The change or update event is automatically logged in the Cosmos DB change feed.
  3. Using the Cosmos DB trigger, an Azure Function is invoked when the event appears in the change feed.
  4. Using the SignalR Service output binding, the function outputs a message to SignalR Service.
  5. SignalR Service sends the message to all connected clients over WebSockets.

The SignalR client SDK is available in JavaScript and .NET Standard. There's also a Java client coming soon. We'll use a simple Vue.js app in a browser for this project. Using the .NET Standard SDK, we can also build desktop apps with .NET Framework and .NET Core or mobile apps with Xamarin to receive messages from SignalR Service.

Connecting Cosmos DB change feed to SignalR Service with Azure Functions

We'll first look at how to implement our Azure Functions using JavaScript, then we'll do the same with C#. Java and Python support in Azure Functions are coming in the future.

The example we'll use is an airline flight pricing database. Each flight is a document in a Cosmos DB collection, and any changes in the data will be reflected in the application in real-time.

JavaScript

The complete solution can be found here. The main function we'll look at is OnDocumentsChanged.

In function.json, we define two bindings:

  • Cosmos DB trigger - when a change is detected in the flights collection, the function is executed, passing in the updated document(s) in an argument named updatedFlights.
  • SignalR Service output binding - used by the function to output messages to a SignalR Service hub named flights.
{
  "disabled": false,
  "bindings": [
    {
      "type": "cosmosDBTrigger",
      "name": "updatedFlights",
      "direction": "in",
      "databaseName": "demo",
      "collectionName": "flights",
      "feedPollDelay": 1000
    },
    {
      "type": "signalR",
      "name": "signalRMessages",
      "hubName": "flights",
      "direction": "out"
    }
  ]
}

To use the Cosmos DB and SignalR Service bindings, we need to install them using the Functions Core Tools CLI:

  • func extensions install -p Microsoft.Azure.WebJobs.Extensions.CosmosDB -v 3.0.1
  • func extensions install -p Microsoft.Azure.WebJobs.Extensions.SignalRService -v 1.0.0

Check the extensions (Cosmos DB, SignalR Service) for the latest versions of these packages.

An update is coming to the Azure Functions Core Tools that will remove the need to install the SignalR extension manually.

Also ensure that these app settings are configured with the relevant connection strings: AzureWebJobsCosmosDBConnectionString and AzureSignalRConnectionString.

The function itself is very simple. We are simply mapping the array of updated documents into an array of SignalR Service message objects. Our message object instructs SignalR Service to invoke the flightUpdated method on each client with the Cosmos DB document as the argument. A SignalR Service message contains two properties:

  • target - The name of the method to invoke on each client
  • arguments - The arguments to pass to the method
module.exports = async function (context, updatedFlights) {
  context.bindings.signalRMessages =
    updatedFlights.map(flight => ({
      target: 'flightUpdated',
      arguments: [flight]
    }));;
};

Skip to the next section to see how to implement the client-side JavaScript.

C#

Take a look at this solution to implement the same thing in C#.

To use the Cosmos DB and SignalR Service bindings, we need to install them via NuGet:

  • dotnet add package Microsoft.Azure.WebJobs.Extensions.CosmosDB --version 3.0.1
  • dotnet add package Microsoft.Azure.WebJobs.Extensions.SignalRService --version 1.0.0

Check the extensions NuGet packages (Cosmos DB, SignalR Service) for the latest versions.

Also ensure that these app settings are configured with each service's connection string: AzureWebJobsCosmosDBConnectionString and AzureSignalRConnectionString.

The function that we want to look at is OnDocumentsChanged:

[FunctionName("OnDocumentsChanged")]
public static async Task Run(
  [CosmosDBTrigger("demo", "flights", ConnectionStringSetting = "AzureWebJobsCosmosDBConnectionString")]
    IEnumerable<object> updatedFlights,
  [SignalR(HubName = "flights")] IAsyncCollector<SignalRMessage> signalRMessages,
  ILogger log)
{
  foreach(var flight in updatedFlights)
  {
    await signalRMessages.AddAsync(new SignalRMessage
    {
      Target = "flightUpdated",
      Arguments = new[] { flight }
    });
  }
}

The CosmosDBTrigger attribute is listening to changes in a collection named flights in a database named demo. When a change occurs, the function is triggered and the list of updated document(s) are available in the updatedFlights parameter.

The function uses the SignalR output binding to output messages to a SignalR Service hub named flights. We'll invoke a method named flightUpdated on each connected client, passing the updated document as the argument. A SignalR Service message contains two properties:

  • Target - The name of the method to invoke on each client
  • Arguments - The arguments to pass to the method

Receiving real-time updates in a browser

Both the JavaScript and C# implementations invoke a flightUpdated method on each client. In the client-side JavaScript, add an event listener for flightUpdated:

connection.on('flightUpdated', flightUpdated)

function flightUpdated(updatedFlight) {
  // update UI
}

To see the body of flightUpdated and the rest of the single page app, take a look at index.html.

And that's all we need to do to broadcast changes in Cosmos DB to clients connected to SignalR Service!

Resources

The source code is on GitHub.

Azure SignalR Service is now generally available. Learn more by reading the docs and try it today!

Check out this episode of On .NET, where we demo this application [19:35].