Serverless Real-Time Application with Azure SignalR

Leon Fausten
Holisticon Consultants
5 min readMay 21, 2021

This article is the last part of a series of articles about a journey to a completely serverless application on azure. In the previous parts we talked about the azure functions backend and the static hosted frontend.

This part is about adding real-time functionality to our app with the help of Azure SignalR. The task-list entries in different browser and sessions should be synchronized automatically via websockets. The source code can be found on github.

Azure SignalR

SignalR is a services which enables us to send messages to registered clients. We want to send an update message, which triggers the task-list reload. The clients don’t need to poll the server for changes periodically.

Azure SignalR Service simplifies the process of adding real-time web functionality to applications over HTTP.

Azure Portal: Create SignalR Service

We can create the service in different kind of modes.
In the default mode, a server that hosts a hub is created. A hub server exposes methods, that can be called by the clients. But the clients don’t connect to the hub server directly. The SignalR service is between them and used like a proxy. After a connection is established a client uses the same hub server during its lifetime.
The serverless mode cannot have a hub server. But it still has hubs to separate traffic. The service itself is responsible for maintaining the client connections.

Photo by Microsoft Documentation: SignalR Serverless Mode

In this mode the server application doesn’t have a fixed connection to SignalR and the clients. Instead, a client can reach a different backend (or function instance) on every request. Communication is done through a rest api or with websockets. To receive messages by the clients, webhooks can be used.
We create our instance of SignalR in the serverless service mode and use the input binding of our azure function, as we will see later. We don’t use the client to server messages in our application.

Client Connection Information

New clients register itself securely with a client-unique access token at SignalR. They call a http function to get the connection information, consisting of the service url and the access token. The http function can be secured as well. This function is part of our function app and named negotiate function. Let’s create this function with a http trigger in our function app:

function.json of the negotiate function

To get the SignalR connection info we add the signalRConnectionInfo input binding to our new function. The connection info contains the service url and the unique access token. In the typescript code we return them to our clients.

index.ts of the negotiate function

To use SignalR in our functions we have to add the connection string with the service access token to our function configuration (name: AzureSignalRConnectionString). To use a different one we can add the binding parameter connectionStringSetting. We find the string in the keys section of SignalR in the portal.

Azure Portal: Connection String Example
SignalR Connection Information

Remember to add it also to the local.settings.json if you want to start your app locally.

Client: Subscribe to Updates

The client calls the negotiate function and establishes a websocket connection to SignalR. To establish the connection we use the official npm package from microsoft.

client connect to signalR and listen for “taskUpdate” messages

We configure the connection with the HubConnectionBuilder (line 11). When the start method is executed (line 16), the negotiate function is called and the response is used to establish the websocket connection.
After that we start to listen to all updates on the taskUpdate topic (line 22). On every message we call the given callback method (line 23).

The callback method triggers the task update.

update tasks via broadcast message

Backend: Publish Updates

The clients are listening for update notifications now, but our functions don’t publish any. We want to send a notification, if a new task is added or marked as completed. To publish updates we use the output binding of SignalR in the add and complete function.

function.json with signalRMessages binding of the add task function

In line 27 to 32 we added the output binding. It is important that we use the same hub as in the negotiate function. This binding uses the already existing AzureSignalRConnectionString configuration variable, we added for the negotiate function.
In the Azure Function Backend part, we learned that we can access our binding with context.bindings.signalRMessages . To send a broadcast message to all connected clients, we add the following code to the add function:

send signalR broadcast message

The rest of the function stays untouched.

It is possible to send multiple messages by adding more message objects to the array. A message object must have the target parameter. The target defines the topic, the message is sent to. The arguments contain the message content. In our case, we add the action that triggered the message.
It is possible to add a userId or groupName parameter to send it to a specific user or a group of users. You can find more informations about the message configuration here. We don’t need this for our application.

The todo list is now updated automatically on every client, if a change is submitted by one client.

Conclusion

In this article we learned how to add realtime functionality with minimal code changes to our application. SignalR offers us an easy way to use websockets in a serverless environment. The todo list is now updated automatically, if a change occurs.

This was the last part of our journey to a completely serverless application. We implemented the backend with azure functions, hosted the frontend as static website inside a blob container and added real-time functionality to our app.

We didn’t talked about the local app development itself, the app code, testing or other important topics. In a real world project these topics should be covered. But we demonstrated how a modern cloud based architecture can look and developed a lightweight alternative, to a container based architecture.

Sources and Further Information

--

--