Serverless Real-Time Application with Azure SignalR
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.
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.
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:
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.
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.
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.
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.
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.
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:
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.